Padding Oracle Attack
Padding Oracle เป็นการโจมตี CBC ที่ทรงพลังที่สุดอันหนึ่ง: ถ้าเซิร์ฟเวอร์บอกได้ว่า padding ของข้อความที่ถอดรหัส 'ถูกต้องหรือไม่' (แม้แค่ผ่าน error/timing ต่างกัน) ผู้โจมตีสามารถถอดรหัส ciphertext ได้ทั้งหมดโดยไม่ต้องรู้ key บทนี้อธิบายกลไกทีละ byte พร้อมโค้ด
1. Padding Oracle คืออะไร
ใน CBC ข้อมูลต้องเติม padding ให้ครบ block (มาตรฐาน PKCS#7: เติม byte ที่มีค่าเท่ากับจำนวน byte ที่เติม เช่นขาด 4 byte เติม 04 04 04 04) เมื่อเซิร์ฟเวอร์ถอดรหัส มันเช็คว่า padding ถูกต้องไหม — 'oracle' คือสิ่งใดก็ตามที่บอกผู้โจมตีได้ว่า padding ถูกหรือผิด เช่น HTTP 500 vs 200, ข้อความ error ต่างกัน, หรือแม้แต่เวลาตอบสนองที่ต่างกัน
2. กลไก — ทำไมถึงถอดได้
ใน CBC การถอดรหัส: P_i = Dec(C_i) ⊕ C_(i−1) ผู้โจมตีควบคุม C_(i−1) ได้ (เป็นข้อมูลที่ส่งไป) เป้าหมายคือหา Dec(C_i) ซึ่งเรียกว่า intermediate value ถ้าหา intermediate ได้ ก็ XOR กับ C_(i−1) ของจริง → ได้ plaintext
- 1โฟกัสที่ byte สุดท้ายของ block ก่อน: ปรับ byte สุดท้ายของ C_(i−1) (เรียก C') ไปเรื่อยๆ 0–255
- 2เมื่อ padding ถูก (oracle บอก valid) แปลว่า plaintext byte สุดท้ายหลังถอด = 0x01 (padding ที่ถูกต้องของ 1 byte)
- 3นั่นหมายถึง intermediate[15] ⊕ C'[15] = 0x01 → intermediate[15] = C'[15] ⊕ 0x01
- 4รู้ intermediate[15] แล้ว → plaintext จริง[15] = intermediate[15] ⊕ C_จริง[15]
- 5ไป byte ถัดไป: ตั้ง byte สุดท้ายให้ได้ padding 0x02 แล้ว brute byte ที่ 14 ให้ได้ valid (0x02 0x02)
- 6ทำซ้ำถอยหลังจนครบ 16 byte ของ block แล้วไป block ถัดไป
3. โค้ดโจมตี (สำหรับ lab/CTF ที่ได้รับอนุญาต)
BS = 16
def oracle(c_prev: bytes, c_block: bytes) -> bool:
"""ส่งไปเซิร์ฟเวอร์ คืน True ถ้า padding valid (เขียนตามโจทย์)"""
...
def decrypt_block(c_prev: bytes, c_block: bytes) -> bytes:
inter = bytearray(BS) # intermediate = Dec(c_block)
recovered = bytearray(BS)
for pad in range(1, BS + 1):
forged = bytearray(BS)
# ตั้ง byte ท้ายๆ ให้ได้ padding ปัจจุบัน
for k in range(1, pad):
forged[BS - k] = inter[BS - k] ^ pad
idx = BS - pad
for guess in range(256):
forged[idx] = guess
if oracle(bytes(forged), c_block):
inter[idx] = guess ^ pad
recovered[idx] = inter[idx] ^ c_prev[idx]
break
return bytes(recovered)
# ไล่ทุก block: ใช้ (IV, C1, C2, ...) เป็นคู่ (c_prev, c_block)padbuster ทำให้อัตโนมัติ: padbuster URL EncryptedSample BlockSize -cookies ... เหมาะกับ web padding oracle ส่วนโจทย์ที่เป็น service/script เขียน Python เองคุมได้ดีกว่า4. ต่อยอด — สร้าง ciphertext ปลอม (CBC-R)
เมื่อหา intermediate value ของ block ได้ ไม่ใช่แค่ถอดได้ — ยัง สร้าง ciphertext ที่ถอดออกมาเป็นข้อความที่เราต้องการ ได้ด้วย (CBC-R) โดยตั้ง C_(i−1) = intermediate ⊕ plaintext_ที่ต้องการ ใช้ปลอม session/cookie ที่เข้ารหัส CBC โดยไม่รู้ key
5. Quick Reference
- เงื่อนไข: CBC + oracle บอก padding valid/invalid (error/timing/status ต่างกัน)
P_i = Dec(C_i) ⊕ C_(i−1)— ควบคุม C_(i−1) เพื่อ brute- ถอดทีละ byte จากท้าย block, เฉลี่ย ~128 ครั้ง/byte
- หา intermediate ได้ → ถอด plaintext และปลอม ciphertext (CBC-R)
- เครื่องมือ: padbuster (web), หรือเขียน Python เอง
- ป้องกัน: ใช้ authenticated encryption (AES-GCM) หรือ encrypt-then-MAC
🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ
สมมติสงสัยว่ามี padding oracle (server ตอบต่างกันเมื่อ padding ผิด/ถูก) มีแค่เครื่อง Kali เปล่าๆ ทำตามนี้ทีละขั้นเพื่อถอดทั้งข้อความให้ได้
- 1ระบุ pattern ของ oracle ให้ชัดก่อน: ส่ง ciphertext เดิมกับที่แก้ 1 byte สุดท้ายไปเทียบกัน จดว่า response ต่างกันตรงไหน (HTTP status, ข้อความ error, ความยาว response, หรือเวลาตอบสนอง)
- 2เป็นเว็บที่ใช้ cookie เข้ารหัสตรงๆ ไหม? ถ้าใช่ ลอง `padbuster http://target/ EncryptedSample 16 -cookies auth=EncryptedSample` ก่อนเลย (ติดตั้งด้วย `apt install padbuster` ถ้ายังไม่มี)
- 3padbuster ใช้ไม่ได้ (เป็น custom protocol, binary, หรือ format ที่ padbuster ไม่รองรับ) → เขียน python เอง: สร้างฟังก์ชัน `oracle(c_prev, c_block)` ที่ส่งค่าไปให้ target แล้วคืน True/False ตาม pattern ที่จดไว้ในขั้นแรก
- 4ทดสอบ oracle function ด้วยมือก่อนสัก 2-3 ค่า (ลองใส่ byte ที่รู้ว่าถูก/ผิดแน่ๆ) ให้มั่นใจว่ามันแยกผลถูก/ผิดได้จริง ก่อนรันสคริปต์เต็มที่ใช้เวลานาน
- 5รัน `decrypt_block()` ตามโค้ดในหัวข้อด้านบน ไล่ครบทุก block (จับคู่ IV+C1, C1+C2, C2+C3, ...)
- 6ระวัง false positive ตอน pad=1 (มีโอกาสเจอ 0x02 0x02 บังเอิญ) → verify ด้วยการแก้ byte ก่อนหน้าแล้วเช็คซ้ำอีกครั้งตามที่ note ในโค้ดบอกไว้
- 7ถอดได้ทั้งข้อความแล้ว → แปลผล (อาจเป็น session data/JSON ใน cookie หรือ flag ตรงๆ)
- 8อยากปลอม ciphertext ใหม่ (ไม่ใช่แค่ถอด) → ใช้เทคนิค CBC-R: ตั้ง `C_(i-1) = intermediate ⊕ plaintext_ที่ต้องการ` (ดูหัวข้อ 4 ด้านบน)
- 9oracle ช้ามาก (ต้องยิงหลายพันครั้ง กว่าจะครบ) → parallelize ด้วย python `threading`/`multiprocessing` หรือใช้ flag `-threads` ของ padbuster
- 10ถอด/ปลอมสำเร็จแล้ว → ตรวจ flag หรือข้อมูลที่ได้ตรงกับที่โจทย์ต้องการ
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| ทดสอบ pattern ของ oracle (error/status/timing) | curl, python3 (requests) | - | Burp Suite Repeater/Intruder |
| ถอดอัตโนมัติ (HTTP cookie) | padbuster | apt install padbuster | - |
| เขียน oracle เอง (custom protocol) | python3, pwntools | pip install pwntools | - |
| เร่งความเร็ว (parallel requests) | python3 (threading/multiprocessing) | - | - |
| ปลอม ciphertext ใหม่ (CBC-R) | python3 | - | - |
| ดู/แก้ hex ของ ciphertext ด้วยมือ | xxd | - | CyberChef (To Hex / From Hex) |
หัวข้อที่เชื่อมโยง
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้