[攻防世界]进阶区pwn-100
可以说这个题做地很难受,一开始不会用gadget,被迫去看大佬的wp,结果大佬的一个“ 显然…… ”让我懵了快一天。真的是一边学一边做才写出来,也学了不少东西。
这个题只开了NX
IDA打开,看字符串窗口,只有puts和read,既没有system也没有bash
这个程序的主要部分都在sub_40063D函数中,查看伪代码
这个a1就是sub_40068E中的v1,在sub_40068E可以看到定义v1的栈空间只有0x40,但是sub_40063D中读入了0x200,是一个很明显的栈溢出漏洞
对于一般题来说,这个时候就可以利用puts泄露它本身的地址,再根据libc的偏移算出system和/bin/sh的真实地址,利用ROP就能拿到shell,但是这个题没给libc,所以需要用pwntools的一个模块DynELF来泄露system的地址
由于没有可以用来构造rop的函数,所以需要用到gadget来传参、跳转,用64位中的通用gadget
DynELF需要用puts泄露地址,而puts只需要一个参数,所以找pop rdi; ret传参即可
在IDA中查看发现不太一样,没有0x400763,这是因为这个pop r15是两步组成的,拆开看就行
先按D,把它转换成数据,再把0x400763这行转换成代码 (按C)
这样就可以看到pop rdi;ret了
现在可以用DynELF泄露system地址,可以用找到的gadget构造rop链,但是还缺一个/bin/sh
由于这个程序里有read函数,所以可以用rop链调用read,随便找一个可写入的字段,把 “/bin/sh” 写入进去就行
可以利用gdb中的vmmap查看程序可写入的地址
r代表只读,rw代表可写,所以只要在0x601000和0x602000之间随便选一小节,够写入 “/bin/sh” 就可以
思路:
- 用DynELF泄露system地址
- 利用找到的gadget构造rop链
- 第一次攻击,用read写入 “/bin/sh” ,并跳转到start来恢复栈,直接跳到main会让栈空间越来越少
- 第二次攻击,构造system(“/bin/sh”)
- 拿到shell,cat flag
EXP:
1 | from pwn import* |
关于DynELF:可以看看这个大佬的分享
关于64位linux的通用gadget:可以看看这篇文章
payload0解释:
- ‘a’*72:让v1溢出,0x40
- p64(gadget1): 第一段gadget的起始地址
- p64(0)+p64(1):让rbx=0,rbp=1,这样0x400614的跳转不会实现,才能继续进行到gadget2
- p64(read_got):read函数地址,放进r12
- p64(8):read的参数,读8位
- p64(binsh_addr):read参数,写入的地址
- p64(0): read的参数
- p64(gadget2):第二段gadget的起始地址
- ‘\x00’*56:gadget2结束又跳转到gadget1,7个寄存器,每个填8字节,故需要再填充7 * 8字节才能到返回地址
- p64(start_addr):第一次攻击后跳转到start,进行栈恢复,这样栈就可以重复利用多次
payload1解释:
- “A”*72:让v1溢出,0x40
- p64(pop_rdi):跳转到one gadget
- p64(binsh_addr):system参数
- p64(system_addr):system函数地址,跳转到system
其实这个题完全可以只用one gadget构造rop,而/bin/sh也可以用LibcSearcher来找到
第一次攻击时用 pop rdi;ret 来给puts传参泄露puts真实地址,并跳转回start
第二次攻击也用 pop rdi;ret 来给puts传参泄露read真实地址,并跳转回start
用LibcSearcher得到system,bin/sh偏移,计算真实地址
第三次攻击还用 pop rdi;ret 给system传参,构造system(“/bin/sh”)拿到shell