pwn题学习--fastbindup实战

最近帮队友研究了一个pwn题,感觉好久不写exp差点把自己坑出shit…


xman - fastisfast

首先看到,这个程序前后问感觉是个经典的pwn,尤其是看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
Book *create()
{
Book *result; // rax

result = (Book *)malloc(36uLL);
ptr = result;
return result;
}
void del()
{
if ( ptr )
free(ptr);
}

这个地方可以发现,ptr本身free之后,并没有被释放,换句话说,这个是一个典型的UAF。

这里想到,fast bin中对于堆的管理如下:

1
2
3
4
5
6
7
+---------------+
| prev_size |
+---------------+
| size |
+---------------+
| fd | |
+---------------+

如果说,这个chunk的fd存在指定值的时候,我们去修改fd,那么下一次malloc的返回值就会是fd的值。但是周末的时候,听到了大佬们讨论关于malloc的保护机制,去翻一下源代码看一下,大概看懂了防护机制。简单来说,检查了fd中的大小和malloc中的大小是否相等,这就不好搞了啊,如果我们随便的修改fd的话,很可能会被查出来。那么我们这里先做一次实验,看看会不会发生这样的事情。实验结果的确是,我们只有修改的fd指向了【堆块开头大小符合要求的时候才能够完成分配】。。。。。

那么我们首先要泄露数据,但是,但是,我发现free之后,也没有什么值得泄露的东西啊!

昨天晚上fuzz(?)了一下,发现有一个malloc的洞我居然没有发现:

1
2
3
puts("Please input your information");
puts("Name:");
readn(v1, 8LL);

这个位置上,在v1指向的ptrfree之后,如果重新读入一个指针的话,那么malloc就【可能为其分配空间】。

利用算是找到了,但是泄露怎么办呢。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.bss:0000000000602090 stdin@@GLIBC_2_2_5 dq ?                 ; DATA XREF: LOAD:0000000000400410↑o
.bss:0000000000602090 ; init_proc+4↑r
.bss:0000000000602090 ; Alternative name is 'stdin'
.bss:0000000000602090 ; Copy of shared data
.bss:0000000000602098 completed_6962 db ? ; DATA XREF: __do_global_dtors_aux↑r
.bss:0000000000602098 ; __do_global_dtors_aux+13↑w
.bss:0000000000602099 align 20h
.bss:00000000006020A0 public info
.bss:00000000006020A0 info dd ? ; DATA XREF: main:loc_400B59↑r
.bss:00000000006020A0 ; main+C0↑w ...
.bss:00000000006020A4 align 8
.bss:00000000006020A8 ; Book *ptr
.bss:00000000006020A8 ptr dq ? ; DATA XREF: create+E↑w
.bss:00000000006020A8 ; edit+17↑r ...
.bss:00000000006020A8 _bss ends
.bss:00000000006020A8

今天去找了大佬讨论,发现套路真的多。这个ptr是我们用来存放malloc对象的指针,然后,上面这个info,是一个计数器。我本来是想不到info用来做什么的,因为其功能就是【每次进入主循环,就自然增加1】,今天突然发现,原来可以用来【伪造malloc size】!具体是个什么思路呢,大致如下:

1
2
3
4
5
6
7
+----------------------+ <---fd
| (prevsize) &info - 8 |
+----------------------+
| (size) info |
+----------------------+
| ptr |
+----------------------+

如上,我们让free之后的堆块中的fd指向info的位置,然后我们malloc两次之后,就会将这个fd分配出来。如果我们在malloc之前,info已经达到了0x30次,那么当我们把fd分配出来的时候,正好会变成0x31,此时就能够绕过malloc的保护机制,并且将ptr的位置分配出来(为什么不是fd的位置?这个是malloc决定的,要跨国prev_size和size,把data部分交出来啦)

那么接下来,当我们在修改name的时候,【实际上就是在修改ptr指针】。此时如果我们让指针指向got表的开头,从0x602010开始的位置,那么就能够在输入comment和age之后,将这些地址泄露。其中,为了防止在输入comment的时候,发生【不小心将重要数据覆盖的事情】,我们这里对于整数,可以使用scanf的漏洞【输入’-'的时候,符号不会被考虑】,然后完成曾输入,经过调整之后,我么能够泄露地址:

1
2
3
_dl_runtime_resolve_avx : 0x7fca53b567c0
printf: 0x537ee950(只能够拿到低八位)
free: 0x7fca53b58c90

通过拿到了这个地址之后,我们就能够上网去查询当前我们使用的libc.so.6的版本号了
然而,今天试了很多次,发现其实libc-database好像没有那么好用啊。。。找不到指定的libc。。纠结了一会儿觉得就先这样吧。

之后的思路很简单,拿到libc之后,算出system的地址,然后修改got表,把free的地址改成system,然后重新create一个chunk,并且name里面的内容改成/bin/sh,然后free掉这个内容即可。