前言:
上了一天的课已经很疲惫了,想着今晚就把昨天所解的一道格式化字符串的漏洞给写一下,然后好好睡一觉接着上课,昨晚画工图画到怀疑人生,突然惊觉明天运动会放假,不用上课啦哈哈哈哈哈哈,开心~
正题:
一道泄漏libc来利用的格式化字符串题。
直接上手反汇编:
加上运行过后整体了解到有一块检测登陆用户和三个模块函数,一个是编写文件’put’,一个是显示文件’dir’,还有一个是读取文件’get’。
这里推荐一个比较好用的格式化漏洞查看的插件,叫lazyIDA,在GitHub上有开源项目。
漏洞点出现在这里读取文件函数里面:
最后面的printf处。它的地址:
1 | .text:0804889E call _printf |
用gdb在这里下断点后开始调试。前面验证用户名密码的步骤很容易就可以patch掉,密码是’rxraclhm’。暂停在断点处:
往下看堆栈中的数据:
可以看见我们的输入出现在距离格式化字符串的偏移量为7的位置。
偏移地址找到了,接下来就是找需要泄漏的函数,我们这里用常规函数’__libc_start_main’来泄漏。但是好像在堆栈中没有找到这个函数?不一定,我们往下继续找:
终于在距离偏移91处找到了改函数+247后的地址,所以泄漏改地址之后再减去247后就是真正的’__libc_start_main’函数的地址。那么我们就在所’put’上去的文件内容中写上:
1 | %91$p |
就可以得到该地址。
得到system函数的地址之后接下来我们要做的是什么?
- 如何执行system函数?
- /bin/sh字符串如何获取?
1.如何执行system函数:
我们可以利用格式化字符串漏洞覆盖大数字的作用去修改got表的地址,这里我们想到的就是将puts函数的got表地址修改成system函数地址,所以当执行puts函数的时候其实执行的是system函数。接下来就是如何去修改的问题了。
这里有两种方法:
0xa. 利用pwntools中已经成型的函数fmtstr-payload
该函数的利用方式:
1 | fmtstr_payload(7, {puts_got: system_addr}) |
意思就是,格式化字符串的偏移是7,我希望在puts_got地址处写入system_addr地址。默认情况下是按照字节来写的。
puts_got的地址可以用ELF.got[‘puts’]来获取,system地址上面已经获取到了。
0xb. 自己构造playload
格式化字符串写一般分两次写入,每次写半个dword长度的内容,这样可以大大减少程序输出大量空格的时间。两个payload如下:
1 | payload1 = p32(puts_got) + '%%%dc' % ((system_addr & 0xffff)-4) + '%7$hn' |
当前环境中,实际内容是:
1 | payload1 = "x28xa0x04x08%396c%7$hn" |
其中p32(puts_got)将数字形式的0x0804a028转为可被读入内存的字符串形式”x28xa0x04x08”,%396c与%46942c代表输出396或46942个空格,system_addr & 0xffff 取半个dword后还需减去4,是因为前面p32(puts_got)已经占了四个字节,这四个字节与后面的空格数相加的总字节数相加刚好为system_addr & 0xffff,而该值将会写入当前printf的第7个不定参数中,而这第七个不定参数正好是puts_got与puts_got+2 ,以shellcode2的执行情况为例,请看下图:
prinf函数的参数从栈顶开始,栈顶指向我们所构造的format payload字符串的地址,然后往下分别是第一个不定参数,第二个不定参数……第七个不定参数即为我们所输入的格式化串中的前四个字节内容0x0804a02a。因而执行完该语句后,会向0x0804a02a写入两个字节内容:0xb762。
payload1的执行过程同理,当执行完以上两条payload之后,我们便成功向地址0x0804a028中写入了四字节内容0xb7620190,即将plt表中puts的地址替换成了system函数的地址,所以当再次向系统发送dir指令,并执行puts函数时,实际执行的则是system函数。
2. 如何获取/bin/sh字符串?
这里就得看自己的眼尖不尖了,或者也可以说是自己想不想的到了,puts函数在dir函数中用到,因为puts出来的是文件名,所以当puts执行的是system函数时所用的参数就是文件名,所以我们可以把文件名写成/bin/sh来完成调用。
因为整个exp中需要put两次文件,所以第一次put文件名可以用’/sh’,第二次在用’/bin’。或者是最后一次直接使用’/bin/sh;’文件名,用分号直接区分开来。我们可以用gdb来看看,在puts函数处下断点:
这是加分号的情况。
这是分两次输入/bin/sh的情况。以上两种都成功执行了system函数。
EXP:
1 | from pwn import * |
学习愉快~