คลัง
crypto

Padding Oracle Attack

Padding Oracle เป็นการโจมตี CBC ที่ทรงพลังที่สุดอันหนึ่ง: ถ้าเซิร์ฟเวอร์บอกได้ว่า padding ของข้อความที่ถอดรหัส 'ถูกต้องหรือไม่' (แม้แค่ผ่าน error/timing ต่างกัน) ผู้โจมตีสามารถถอดรหัส ciphertext ได้ทั้งหมดโดยไม่ต้องรู้ key บทนี้อธิบายกลไกทีละ byte พร้อมโค้ด

Advanced#padding-oracle#cbc#pkcs7#crypto#aes#ctf#padbuster

1. Padding Oracle คืออะไร

ใน CBC ข้อมูลต้องเติม padding ให้ครบ block (มาตรฐาน PKCS#7: เติม byte ที่มีค่าเท่ากับจำนวน byte ที่เติม เช่นขาด 4 byte เติม 04 04 04 04) เมื่อเซิร์ฟเวอร์ถอดรหัส มันเช็คว่า padding ถูกต้องไหม — 'oracle' คือสิ่งใดก็ตามที่บอกผู้โจมตีได้ว่า padding ถูกหรือผิด เช่น HTTP 500 vs 200, ข้อความ error ต่างกัน, หรือแม้แต่เวลาตอบสนองที่ต่างกัน

จุดที่ทำให้อันตราย: oracle รั่วข้อมูล 1 บิต (ถูก/ผิด) ต่อการลอง 1 ครั้ง แต่ข้อมูลนั้นพอให้กู้ plaintext ได้ทีละ byte โดยเฉลี่ยลองแค่ 128 ครั้งต่อ byte (สูงสุด 256) ไม่ต้องรู้ key เลย

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. 1โฟกัสที่ byte สุดท้ายของ block ก่อน: ปรับ byte สุดท้ายของ C_(i−1) (เรียก C') ไปเรื่อยๆ 0–255
  2. 2เมื่อ padding ถูก (oracle บอก valid) แปลว่า plaintext byte สุดท้ายหลังถอด = 0x01 (padding ที่ถูกต้องของ 1 byte)
  3. 3นั่นหมายถึง intermediate[15] ⊕ C'[15] = 0x01 → intermediate[15] = C'[15] ⊕ 0x01
  4. 4รู้ intermediate[15] แล้ว → plaintext จริง[15] = intermediate[15] ⊕ C_จริง[15]
  5. 5ไป byte ถัดไป: ตั้ง byte สุดท้ายให้ได้ padding 0x02 แล้ว brute byte ที่ 14 ให้ได้ valid (0x02 0x02)
  6. 6ทำซ้ำถอยหลังจนครบ 16 byte ของ block แล้วไป block ถัดไป
Padding Oracle — brute byte สุดท้ายให้ padding ถูก
C' (block ที่ผู้โจมตีควบคุม) . . . (brute byte สุดท้าย 0–255) . . . ?? Dec 01 ← padding valid! ⟹ intermediate = C' ⊕ 01

3. โค้ดโจมตี (สำหรับ lab/CTF ที่ได้รับอนุญาต)

Padding oracle decryption — โครงหลัก (Python)
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)
ระวัง false positive ตอน pad=1 (อาจเจอ 0x02 0x02 บังเอิญ) — verify ด้วยการแก้ byte ถัดไปแล้วเช็คซ้ำ
เครื่องมือสำเร็จ 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. 1ระบุ pattern ของ oracle ให้ชัดก่อน: ส่ง ciphertext เดิมกับที่แก้ 1 byte สุดท้ายไปเทียบกัน จดว่า response ต่างกันตรงไหน (HTTP status, ข้อความ error, ความยาว response, หรือเวลาตอบสนอง)
  2. 2เป็นเว็บที่ใช้ cookie เข้ารหัสตรงๆ ไหม? ถ้าใช่ ลอง `padbuster http://target/ EncryptedSample 16 -cookies auth=EncryptedSample` ก่อนเลย (ติดตั้งด้วย `apt install padbuster` ถ้ายังไม่มี)
  3. 3padbuster ใช้ไม่ได้ (เป็น custom protocol, binary, หรือ format ที่ padbuster ไม่รองรับ) → เขียน python เอง: สร้างฟังก์ชัน `oracle(c_prev, c_block)` ที่ส่งค่าไปให้ target แล้วคืน True/False ตาม pattern ที่จดไว้ในขั้นแรก
  4. 4ทดสอบ oracle function ด้วยมือก่อนสัก 2-3 ค่า (ลองใส่ byte ที่รู้ว่าถูก/ผิดแน่ๆ) ให้มั่นใจว่ามันแยกผลถูก/ผิดได้จริง ก่อนรันสคริปต์เต็มที่ใช้เวลานาน
  5. 5รัน `decrypt_block()` ตามโค้ดในหัวข้อด้านบน ไล่ครบทุก block (จับคู่ IV+C1, C1+C2, C2+C3, ...)
  6. 6ระวัง false positive ตอน pad=1 (มีโอกาสเจอ 0x02 0x02 บังเอิญ) → verify ด้วยการแก้ byte ก่อนหน้าแล้วเช็คซ้ำอีกครั้งตามที่ note ในโค้ดบอกไว้
  7. 7ถอดได้ทั้งข้อความแล้ว → แปลผล (อาจเป็น session data/JSON ใน cookie หรือ flag ตรงๆ)
  8. 8อยากปลอม ciphertext ใหม่ (ไม่ใช่แค่ถอด) → ใช้เทคนิค CBC-R: ตั้ง `C_(i-1) = intermediate ⊕ plaintext_ที่ต้องการ` (ดูหัวข้อ 4 ด้านบน)
  9. 9oracle ช้ามาก (ต้องยิงหลายพันครั้ง กว่าจะครบ) → parallelize ด้วย python `threading`/`multiprocessing` หรือใช้ flag `-threads` ของ padbuster
  10. 10ถอด/ปลอมสำเร็จแล้ว → ตรวจ flag หรือข้อมูลที่ได้ตรงกับที่โจทย์ต้องการ
สงสัย padding oracle มีแค่ Kali — ไล่ตามนี้
ระบุ pattern ของ oracle (ลองแก้ byte สุดท้ายเทียบ response)
✅ เจอ pattern ชัดเจน (error/status/length/timing ต่างกัน)→ เลือกเครื่องมือ
❌ response เหมือนกันหมดทุกครั้ง→ อาจไม่ใช่ padding oracle จริง
เป็น HTTP cookie ธรรมดาไหม?
✅ ใช่→ ลอง padbuster ก่อน
❌ เป็น custom protocol/binary→ เขียน python เอง
รัน padbuster URL sample_ciphertext blocksize -cookies ...
✅ ถอดได้→ จบ
❌ padbuster ใช้ไม่ได้/error→ เขียน python เอง
เขียน oracle() function + ทดสอบด้วยมือ 2-3 ค่าก่อน
✅ oracle แยกถูก/ผิดได้แม่น→ รัน decrypt_block เต็มสคริปต์
❌ ผลไม่นิ่ง/เจอ false positive บ่อย→ ปรับ verify byte ถัดไปซ้ำ
รัน decrypt_block ไล่ครบทุก block
✅ ถอดได้ทั้งข้อความ→ จบ อ่านผล
❌ ช้ามาก/ค้าง→ parallelize ด้วย threading
อยากปลอม ciphertext ใหม่ (ไม่ใช่แค่ถอด) ไหม?
✅ ใช่→ CBC-R: ตั้ง C_(i-1)=intermediate⊕target
❌ แค่ถอดพอ→ จบ
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
ทดสอบ pattern ของ oracle (error/status/timing)curl, python3 (requests)-Burp Suite Repeater/Intruder
ถอดอัตโนมัติ (HTTP cookie)padbusterapt install padbuster-
เขียน oracle เอง (custom protocol)python3, pwntoolspip install pwntools-
เร่งความเร็ว (parallel requests)python3 (threading/multiprocessing)--
ปลอม ciphertext ใหม่ (CBC-R)python3--
ดู/แก้ hex ของ ciphertext ด้วยมือxxd-CyberChef (To Hex / From Hex)
🚑 ถ้าตันสนิท ลองท่าถัดไป: CBC — ถ้าจริงๆ ไม่มี oracle ให้ลอง bit-flipping แทน · ECB — ถ้าเจอ ciphertext block ซ้ำ (ไม่ใช่ CBC จริง) · AES — ถ้ายังไม่แน่ใจ mode ให้กลับไปตรวจจากต้นทาง · Hash Cracking — ถ้า server ป้องกันด้วย MAC/signature ที่อาจ crack ได้แทนการหา padding oracle

หัวข้อที่เชื่อมโยง

โน้ตของฉัน

ยังไม่มีโน้ตสำหรับหัวข้อนี้