Basic
首先给出两个常用shellcode仓库,可以检索需要的shellcode
接下来给出几个尽可能短的shellcode
1 | ; excve('/bin/sh','sh',0) |
最短shellcode
特征与条件
长度为22字节
主要是通过cdq将rdx高位为0,减小了长度,另一种方法是通过mul r/m64指令,实现清空rax和rdx
- eax 高二位必须为0,一般是满足的
汇编
1 | xor rsi, rsi |
1 | 48 31 f6 xor rsi, rsi |
字节码
1 | // int |
orw
特征与条件
长度为0x28字节
主要是通过异或实现了取代了mov减少长度
- rsp指向的地址必须是可用的
- 存在NULL字符
汇编
1 | // rdx为写入数量 |
字节码
1 | 0x6800000200c2c748 |
可指定地址orw
1 | shellcode = """ |
侧信道爆破
1 | code = asm( |
字符限制
编码工具
ae64 | alpha3 | |
---|---|---|
Encode x32 alphanumeric shellcode | x | ✔ |
Encode x64 alphanumeric shellcode | ✔ | ✔ |
Original shellcode can contain zero bytes | ✔ | x |
Base address register can contain offset | ✔ | x |
Alpha3
限制只能使用字母或者数字
alpha3使用:
alpha3需要python2环境,所以先安装python2
1 | from pwn import * |
1 | python2 ALPHA3.py x64 ascii mixedcase rdx --input="sc.bin" > out.bin |
可以选择架构、编码、限制的字符
AE64
AE64可以直接在python中导入,使用相对较为方便且限制较少
1 | from ae64 import AE64 |
手动绕过
主要是通过sub、add、xor等指令对于非字母数字指令进行加密。
可以先根据限制筛选出受限制后的指令列表,然后根据指令列表进行组合,从而实现绕过。
另一种方法是通过shellcode先实现write读取到shellcode的位置,然后输入新的无限制的
shellcode来完成绕过。
https://nets.ec/Alphanumeric_shellcode
特定位置字符限制
在最近的*CTF中存在一个用浮点数输入字符,并对浮点数做限制写shellcode的题目,实际上是限制了每八位需要有两位是特定字符,这里给出两种绕过思路:
1 | mov rcx, im64 |
这里im是可以由我们自由控制的立即数,因此我们可以通过插入这些无关指令填充来绕过限制,上面这些指令涵盖了3、4、5字节,可以灵活插入来达到需要的效果
1 | jmp short |
通过jmp短跳转直接跳过中间指令,从而绕过限制
jmp指令本身只有两个字节,更为灵活。
对于orw的限制
如果程序还对orw等系统调用作出了限制呢?
w的限制还好说,可以通过侧信道leak出flag,而如果禁用了open,orw就 很难进行下去了。
但是还有一种方法。
利用32位调用绕过orw
x86与x64的syscall number是不一样的,如果能够跳转到32位执行相应的shellcode,就可一绕过限制。
x86 sys_number
| sys_number | | | | |
|—|—|—|—|—|—|
|3|read|0x03|unsigned int fd|char *buf|size_t count|
|4|write|0x04|unsigned int fd|const char *buf|size_t count|
|5|open|0x05|const char *filename|int flags|umode_t mode|
而程序是由32位还是64位执行是由cs寄存器决定的,而retfq指令可以对其作出更改,从而切换寄存器状态,所以可以由此实现orw。
值得注意的是, 对于32位程序, 由于kernel 也要对其作出相应支持, 所以内核代码中有一个操作系统层面的arch判断, personality, 这会影响mmap之类的操作
x32 ABI
x32 ABI 是一个应用程序二进制接口 (ABI),也是 Linux 内核的接口之一。 x32 ABI 在 Intel 和 AMD 64 位硬件上提供 32 位整数、长整数和指针。
可以通过 查看内核源代码 unistd_x32.h 查看
1 | cat /usr/src/kernels/6.4.7-200.fc38.x86_64/arch/x86/include/generated/uapi/asm/unistd_x32.h |
1 |
即可以通过0x40000000+syscall_number 来调用一些系统调用。所以可以绕过对syscall的限制。
不过这个特性似乎在大多数发行版中不受支持。
io_uring
io_uring 本身可以实现所有orw乃至socket连接操作, 在linux5.xx最少需要mmap
和 io_uring_setup
两个syscall, 之后增加了 IORING_SETUP_NOMMAP
则可以只用一个syscall来实现orw
对于syscall指令的过滤
- vdso
- sysenter
- int 80
tricks
- 对于一些题目,对shellcode的检查用到了strlen,那么可以通过先使用一些存在NULL截断的指令,从而使得后面的字符串绕过限制。
- 在无法获取shellcode运行地址时,可以运行syscall,运行后,rcx会被改写为下一条指令的地址。在32位程序中,还可以通过call指令获取将运行地址压入栈中,在64位地址中,可以直接通过
lea rax, [rip]
来获取rip地址 - 对于需要libc地址的程序,可以考虑通过xmm寄存器获得libc相关地址