pwnable_tw_2

babystack

0x01

刚开始这题一点思路都没有,连溢出点都没有找到。后来参考了
http://pzhxbz.cn/?p=91
https://github.com/dr0gba/dr0gba.github.io/blob/master/pwn/babystack.md
这两个文章,后者是在github上搜索到的exp。
其实还是有些摸不到头脑的,这印证了一句话“学而不思则罔,思而不学则殆”,单纯的想是不会有结果的,要结合实践去了解,这里得到的几个教训是:

1.不去动态调试,永远不知道栈上的信息有多丰富
2.strcpy拷贝的字符串要验证长度
3.strncmp(*str,*str,num),num=0时,这个函数返回值是0,即两个字符串相等。
4.x64的参数调用的方式使得漏洞的利用方式需要使用one_gadgets,或者使用ROP。

程序逻辑:

输入 1 :login and logout
输入 2 :使用exit()退出程序
输入 3 :使用strcpy()向main函数的一个数组中copy

0x02 leak

虽然我们可以使用登录时按一个回车键(即发送’\x0a’)来绕过登陆,但是后面的使用发现登录密码还是要知道的。那么怎么获取登陆密码呢,我对这种爆破信息不怎么敏感,所以想不到密码是固定的,然后密码的比对是这样的

1
2
3
4
5
6
7
8
readn(&s, 0x7Fu);
v1 = strlen(&s);
if ( !strncmp(&s, buf_addr, v1) )
{
log_flag = 1;
result = puts("Login Success !");
}
else

也就是说,它密码的比对,长度是由输入来决定的,所以我们可以一个一个字节来爆破密码,这样我们可以获取密码。但是这个密码在栈中的作用就相当于一个canary,除了这个我们还需要获取获取地址信息才可以完成利用。

main函数栈中的变量分布

1
2
3
4
char v6; // [sp+0h] [bp-60h]@14
__int64 passcode; // [sp+40h] [bp-20h]@1
__int64 v8; // [sp+48h] [bp-18h]@1
char choise; // [sp+50h] [bp-10h]@2

这里获取libc地址的方法是:利用3拷贝来将copy()函数中的信息拷贝到main()函数栈中,发现copy()函数栈中有一个libc(setvbuf)的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0000| 0x7fffffffe080 --> 0x7ffff7ffe700 --> 0x7ffff7ffa000 (jg 0x7ffff7ffa047)
0008| 0x7fffffffe088 --> 0x7fffffffe120 --> 0x1
0016| 0x7fffffffe090 --> 0x7fffffffdf00 --> 0x0
0024| 0x7fffffffe098 --> 0x7ffff7ffe4c0 --> 0x7ffff7ffe420 --> 0x7ffff7ff5a50 --> 0x7ffff7ffe168 --> 0x555555554000 (--> ...)
0032| 0x7fffffffe0a0 --> 0x7ffff7b9ba47 ("__vdso_getcpu")
0040| 0x7fffffffe0a8 --> 0x7fffffffe170 --> 0x7fffffff0a33 --> 0x0
0048| 0x7fffffffe0b0 --> 0x7ffff7dd4620 --> 0xfbad2887
0056| 0x7fffffffe0b8 --> 0x7ffff7a8b947 (cmp eax,0xffffffff)
0064| 0x7fffffffe0c0 --> 0x7ffff7dd4620 --> 0xfbad2887
0072| 0x7fffffffe0c8 --> 0x7ffff7ff2700 (0x00007ffff7ff2700)
0080| 0x7fffffffe0d0 --> 0x555555554b70 (xor ebp,ebp)
0088| 0x7fffffffe0d8 --> 0x7ffff7a88439 (<_IO_file_setbuf+9>: test rax,rax)
0096| 0x7fffffffe0e0 --> 0x7ffff7dd4620 --> 0xfbad2887
0104| 0x7fffffffe0e8 --> 0x7ffff7a7ffb4 (<setvbuf+324>: xor edx,edx)

copy()函数是将 char src; // [sp+10h] [bp-80h]@1这个局部变量copy到给定的参数的位置,由于字符串没有做截止处理,所以我们可以copy很多信息到main()函数栈中的v6变量处,我们设置好copy的内容(passcod等),恰好将这个setvbuf的地址信息copy到main函数中的&choise+8这个位置,然后我们又可以通过爆破登录信息来获取这个地址信息。

其实main函数的ret_addr其实也有地址信息,但是我们爆破不了,因为ebp里面的地址是6位字节的,也就是说最后有两位字节是’\x00’,密码比对使用的是strncmp(),也就是说后面的信息被截止了,我们无法获取,就算使用copy()也会引入’\x00’,使得我们想要爆破的地方信息被截止或者覆盖,所以不可行。

0x03 exploit

leak完成后,利用就很简单了,使用one_gadgets覆盖返回地址即可,构造需要copy()的内容即可

payload = padding + passcode + padding + one_gadgets

one_gadgets查找工具:https://github.com/david942j/one_gadget

exp在这里

Spirited Away

0x01

评论超过10即溢出了一位,comment>100,sprintf连接后的字符串溢出两位,comment在栈上可以溢出写到name这个指针上,然后把name指针指向栈中reason中构造的fake_chunk,free后重新malloc,name指针就可以指向栈,并且可写。

0x02 leak

栈中有很多地址信息,而且输入字符串不闭合,这样可以把栈中的地址泄露出来,可以获取栈地址、堆地址和libc地址。

0x03 exploit

这里由于栈底的reason无法溢出写,所以要构造一个fake_chunk,然后free了之后,重新malloc,将name指针指向了reason区域,这样我们就可以溢出写到ret_addr了

这里构造fake_chunk的方法参考how2heap的house_of_sprit。

另外感谢uafio这位前辈,发邮件去问他这题,他告诉了我这个方法~