คลัง
crypto

AES & Block Cipher Modes

AES เป็น block cipher มาตรฐานที่เข้ารหัสทีละ block (16 bytes) ตัว AES เองแข็งแรงมาก แต่ช่องโหว่ใน CTF มักอยู่ที่ 'mode of operation' ที่ใช้ผิด — โดยเฉพาะ ECB ที่รั่ว pattern และ CBC ที่จัดการ IV/padding ผิด บทนี้เน้นการตรวจจับและโจมตี mode ที่อ่อนแอ

IntermediateAdvanced#aes#block-cipher#ecb#cbc#iv#mode#crypto#ctf

1. AES และแนวคิด block cipher

AES เข้ารหัสข้อมูลทีละ block ขนาด 16 bytes (128 บิต) ด้วย key ขนาด 128/192/256 บิต ตัว algorithm เองยังไม่มีการโจมตีที่ practical — ดังนั้นใน CTF คุณแทบไม่ต้องโจมตี AES ตรงๆ แต่ให้มองหาวิธีใช้งานที่ผิด เช่น mode, การ reuse IV/nonce, padding oracle, หรือ key/IV ที่เดาได้

Mode of operation คือวิธีต่อ block หลายอันเข้าด้วยกัน เพราะข้อมูลจริงยาวกว่า 16 bytes mode ที่พบบ่อย: ECB, CBC, CTR, GCM — แต่ละแบบมีจุดอ่อนต่างกัน ECB อ่อนสุดและเจอบ่อยสุดใน CTF

2. ECB — จุดอ่อนคลาสสิก

ECB (Electronic Codebook) เข้ารหัสแต่ละ block อิสระจากกัน ด้วย key เดียวกัน ผลคือ plaintext block ที่เหมือนกัน → ciphertext block ที่เหมือนกันเป๊ะ ทำให้ pattern ของข้อมูลรั่วออกมา (รูปภาพที่เข้ารหัส ECB ยังเห็นเค้าโครงเดิมได้) นี่คือเหตุผลที่ ECB ห้ามใช้กับข้อมูลจริง

ECB — แต่ละ block อิสระ (pattern รั่ว)
P1 C1 AES P2 C2 AES P1 = P2 ⟹ C1 = C2 (pattern รั่ว)
ตรวจจับ ECB — หา block ซ้ำ (Python)
def detect_ecb(ct: bytes, bs=16):
    blocks = [ct[i:i+bs] for i in range(0, len(ct), bs)]
    return len(blocks) != len(set(blocks))  # มี block ซ้ำ = น่าจะ ECB

# ใน CTF: ส่ง plaintext ที่เป็นตัวซ้ำยาวๆ (เช่น 'A'*48)
# ถ้า ciphertext มี block 16-byte ซ้ำกัน = ECB
เทคนิคนี้คือหัวใจของ ECB byte-at-a-time decryption (chosen-plaintext) ใน CTF
ถ้าโจทย์ยอมให้คุณแทรก plaintext แล้ว encrypt (chosen-plaintext) + ใช้ ECB → ทำ byte-at-a-time decryption ได้: ค่อยๆ จัด alignment เดา secret ทีละ byte โดยเทียบ block ที่เข้ารหัสออกมา (โจทย์คลาสสิก Cryptopals Set 2)

3. CBC — IV และ bit-flipping

CBC (Cipher Block Chaining) แก้จุดอ่อน ECB โดย XOR plaintext block กับ ciphertext block ก่อนหน้าก่อนเข้ารหัส block แรกใช้ IV (Initialization Vector) แทน ทำให้ block ซ้ำไม่ออกมาเหมือนกัน แต่ CBC มีจุดอ่อนของตัวเอง: bit-flipping และ padding oracle

CBC — chaining ด้วย XOR block ก่อนหน้า
P1 P2 IV→ AES AES C1 C2 C_(i-1) ป้อนเข้า XOR ของ block ถัดไป

Bit-flipping: เพราะ plaintext block ถูก XOR กับ ciphertext block ก่อนหน้าตอนถอดรหัส การแก้ไข byte ใน C_(i−1) จะทำให้ byte ตำแหน่งเดียวกันใน P_i เปลี่ยนแบบควบคุมได้ (แลกกับ P_(i−1) ที่เละ) ใช้โจมตีเมื่อ block แรกเป็น IV ที่ควบคุมได้ เช่น เปลี่ยน role=user เป็น role=admin

4. ถอดรหัสเมื่อมี key (งานทั่วไป)

AES decrypt CBC / ECB (Python pycryptodome)
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# CBC
cipher = AES.new(key, AES.MODE_CBC, iv)
pt = unpad(cipher.decrypt(ct), 16)

# ECB
cipher = AES.new(key, AES.MODE_ECB)
pt = unpad(cipher.decrypt(ct), 16)
print(pt)

5. Decision Tree

สังเกตทำต่อ
block ซ้ำใน ciphertextECB → byte-at-a-time / pattern leak
แทรก plaintext ได้ + ECBECB chosen-plaintext decryption
CBC + ควบคุม IV/prev block ได้bit-flipping เปลี่ยน plaintext
CBC + เซิร์ฟเวอร์บอก padding ผิด/ถูกPadding Oracle (ดูหัวข้อแยก)
IV/nonce ซ้ำ (CTR/GCM)keystream reuse → XOR สองข้อความ
มี keyถอดตรงๆ ด้วย pycryptodome

6. Quick Reference

  • block AES = 16 bytes; โจมตีที่ mode ไม่ใช่ที่ AES
  • ECB: block ซ้ำ → ciphertext ซ้ำ (ตรวจด้วย set ของ block)
  • ECB + chosen-plaintext → byte-at-a-time decryption
  • CBC: P_i = Dec(C_i) ⊕ C_(i−1); แก้ C_(i−1) → flip P_i
  • IV/nonce reuse = หายนะ (keystream/pattern รั่ว)
  • เครื่องมือ: pycryptodome, CyberChef (AES Decrypt + ECB detect)

🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ

สมมติเจอโจทย์ที่บอกว่าเข้ารหัสด้วย AES แต่ไม่รู้ว่าใช้ mode อะไร มีแค่เครื่อง Kali เปล่าๆ ทำตามนี้ทีละขั้น

  1. 1ถ้ามี source code ให้ (python/java/etc.) ให้ `grep -i 'MODE_\|cipher' *.py` หา `AES.MODE_ECB/CBC/CTR/GCM` ตรงๆ ก่อน — เร็วสุดถ้ามีให้
  2. 2ไม่มี source → เช็คความยาว ciphertext: เป็นทวีคูณของ 16 เสมอไหม (มี padding = ECB/CBC) หรือยาวเท่า plaintext พอดี (สงสัย CTR/GCM/stream)
  3. 3คุมส่ง input เอง (chosen-plaintext) ได้ไหม? ถ้าได้ ส่งข้อมูลซ้ำๆ ยาวๆ เช่น `'A'*48` แล้วดู ciphertext ที่ได้กลับมา
  4. 4มี hex/base64 ที่เข้ารหัสแล้ว — วางใน python แล้วเช็ค: `blocks=[ct[i:i+16] for i in range(0,len(ct),16)]; len(blocks)!=len(set(blocks))` ถ้า True = มี block ซ้ำ = ECB แน่นอน
  5. 5เป็น ECB → ไปทำตามหัวข้อ ECB ทันที (block-swap หรือ byte-at-a-time decryption ถ้าคุม input ได้)
  6. 6ไม่ซ้ำเลย (ทุก block ดูสุ่ม) แต่ยังเป็นทวีคูณของ 16 → น่าจะ CBC เช็คว่า 16 byte แรกของ ciphertext คือ IV แยกออกมาต่างหากไหม
  7. 7เป็น CBC → ลองแก้ 1 byte ใน ciphertext แล้วส่งกลับไปให้ server ถอด ดูว่า error/status/เวลาตอบต่างกันไหมเมื่อ padding ผิดกับถูก (เช่นลองแก้ byte สุดท้ายหลายค่า)
  8. 8server เผย error ต่างกันชัดเจน → นี่คือ Padding Oracle ไปทำตามหัวข้อ Padding Oracle เพื่อถอดทั้งข้อความโดยไม่ต้องรู้ key
  9. 9server ไม่บอก padding แต่ยัง parse ผลลัพธ์ต่อได้ (ไม่เช็ค integrity) → ลอง bit-flipping เปลี่ยนค่าที่ต้องการ (เช่น role=user → admin) ตามหัวข้อ CBC
  10. 10โจทย์ให้ key/IV มาตรงๆ → เปิด CyberChef (gchq.github.io/CyberChef) ลาก 'AES Decrypt' ใส่ key/IV/mode ให้ตรง หรือใช้ pycryptodome เขียนเอง
  11. 11ไม่ตรง ECB/CBC ชัดเจน (ความยาวไม่ทวีคูณ 16, ไม่มี padding oracle) → สงสัยเป็น CTR/GCM ที่ reuse nonce ไปดูหัวข้อ XOR เพราะหลักการ keystream reuse เหมือนกับ many-time pad
โจทย์บอกแค่ 'เข้ารหัส AES' มีแค่ Kali — ไล่ตามนี้
มี source code ให้ไหม? เห็น MODE_ECB/CBC/CTR/GCM ชัดไหม?
✅ เห็นชัด→ ไปหัวข้อ mode นั้นตรงๆ
❌ ไม่มี source→ วิเคราะห์จาก ciphertext เอง
ความยาว ciphertext เป็นทวีคูณของ 16 เสมอไหม?
✅ ใช่ (มี padding)→ สงสัย ECB/CBC
❌ ยาวเท่า input พอดี→ สงสัย CTR/GCM/stream
คุมส่ง input เอง (chosen-plaintext) ได้ไหม?
✅ ได้→ ส่ง 'A'×48 เช็ค block ซ้ำ
❌ ไม่ได้→ เช็ค CBC ตรงๆ
มี block 16-byte ซ้ำติดกันในผลลัพธ์ไหม?
✅ ซ้ำ→ ยืนยัน ECB แน่นอน
❌ ไม่ซ้ำเลย→ น่าจะ CBC (มี IV)
สงสัย CBC — แก้ ciphertext แล้วส่งกลับได้ไหม + server บอก error ต่างกันไหม?
✅ แก้ได้ + error/สถานะต่างกันเมื่อ padding ผิด/ถูก→ Padding Oracle
✅ แก้ได้แต่ไม่มี error บอก padding→ bit-flipping ธรรมดา
❌ แก้ไม่ได้เลย→ เช็คว่ามี key ให้ไหม
มี key/IV ให้ตรงๆ ไหม?
✅ มี→ ถอดตรงๆ ด้วย pycryptodome/CyberChef
❌ ไม่มี→ จนมุม ทบทวนโจทย์ใหม่ทั้งหมด
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
ดู source code หา mode ที่ใช้cat, grep--
ตรวจจับ ECB (block ซ้ำ)python3-gchq.github.io/CyberChef
ถอด AES เมื่อมี key/IVpython3 (pycryptodome)pip install pycryptodomeCyberChef (AES Decrypt)
bit-flip ciphertext (CBC)python3-CyberChef (XOR)
ทดสอบ padding oracle กับ serverpython3 (requests/pwntools)pip install requests pwntools-
เดา mode ด้วยลองผสมค่า--CyberChef (AES Decrypt ปรับ mode ทีละแบบ)
🚑 ถ้าตันสนิท ลองท่าถัดไป: ECB — ถ้าเห็น block ซ้ำหรือคุม input ได้ · CBC — ถ้าแก้ ciphertext แล้ว server ยัง parse ต่อได้โดยไม่เช็ค integrity · Padding Oracle — ถ้า server เผย error/timing ต่างกันเมื่อ padding ผิด/ถูก (โจมตีได้แรงที่สุด ถอดได้ทั้งข้อความ) · XOR — ถ้าจริงๆ เป็น CTR/GCM ที่ reuse nonce (หลักการเดียวกับ repeating-key/many-time pad)

โน้ตของฉัน

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