คลัง
crypto

CBC Mode

CBC (Cipher Block Chaining) XOR plaintext block เข้ากับ ciphertext block ก่อนหน้า ก่อนเข้ารหัส จึงแก้จุดอ่อน ECB (block เดียวกันไม่ให้ ciphertext เหมือนกัน) แต่เปิดช่องโจมตี 2 แบบที่พบบ่อยใน CTF: bit-flipping (แก้ byte ใน ciphertext เพื่อควบคุม plaintext) และ padding oracle (ถอดได้ทั้งข้อความถ้ามี oracle บอก padding ถูก/ผิด)

IntermediateAdvanced#cbc#block-cipher#aes#iv#bit-flipping#padding-oracle#crypto#ctf

1. CBC ทำงานอย่างไร

ตอนเข้ารหัส แต่ละ plaintext block P_i จะถูก XOR กับ ciphertext block ก่อนหน้า C_(i−1) ก่อนเข้ารหัส block แรกใช้ IV แทน C_0: C_i = Enc(P_i ⊕ C_(i−1)) ตอนถอดจึงเป็น P_i = Dec(C_i) ⊕ C_(i−1) — จุดสำคัญคือ plaintext ที่ถอดได้ขึ้นกับ ciphertext block ก่อนหน้า โดยตรง นี่คือรากของทั้ง bit-flipping และ padding oracle

CBC decryption — P_i ขึ้นกับ C_(i−1)
C_1 C_2 C_3 Dec(C_2) Dec(C_3) P_2 P_3 C_1 XOR เข้า P_2
สังเกตเส้นประสีส้ม: C_1 ถูก XOR เข้ากับผลถอดของ block 2 → กลายเป็น P_2 ดังนั้นถ้าเราแก้ byte ใน C_1 เราจะแก้ byte เดียวกันใน P_2 ได้แบบควบคุมได้ (แต่ block 1 เองจะพังกลายเป็นขยะ)

2. Bit-Flipping Attack

เพราะ P_i = Dec(C_i) ⊕ C_(i−1) ถ้าเรารู้ (หรือเดา) plaintext เดิมที่ตำแหน่งหนึ่ง และอยากให้มันกลายเป็นค่าใหม่ ให้แก้ byte ใน ciphertext block ก่อนหน้าด้วยสูตร: C'_(i−1)[j] = C_(i−1)[j] ⊕ P_เดิม[j] ⊕ P_ต้องการ[j] ใช้บ่อยกับ cookie/token ที่เข้ารหัส CBC เช่นพลิก admin=0admin=1

Bit-flip byte ใน block ก่อนหน้า (Python)
# มี ciphertext (IV + blocks) อยากพลิก plaintext ตำแหน่ง idx
# จาก known 'user' -> target 'root' ใน block ถัดไป
from pwn import xor  # หรือทำ XOR เอง

BS = 16
ct = bytearray(iv + ciphertext)   # ทั้งก้อน
# ตำแหน่ง byte ใน plaintext block N -> แก้ที่ ct block N-1
known   = b'user'
target  = b'root'
off     = block_index_prev * BS + col   # ตำแหน่งใน ct ของ block ก่อนหน้า
for k in range(len(known)):
    ct[off + k] ^= known[k] ^ target[k]
# ส่ง ct ที่แก้แล้วกลับไปให้ server ถอด
block ก่อนหน้าที่ถูกแก้จะเสียหาย (กลายเป็นขยะ) — ต้องยอมรับได้ หรือแก้ block ที่ไม่มีข้อมูลสำคัญ
พลิก byte แรกของ plaintext ทั้งข้อความ = แก้ byte ใน IV โดยตรง (IV คือ 'block ก่อนหน้า' ของ block แรก) ถ้าโจทย์ให้แก้ IV ได้ = ควบคุม P_1 ได้เต็มๆ โดยไม่ทำ block ไหนพัง

3. Padding Oracle (ถอดได้ทั้งข้อความ)

ถ้า server เผยว่า padding ถูกหรือผิด (ต่างกันที่ error / status / เวลา) ผู้โจมตีถอด ciphertext ได้ทุก byte โดยไม่ต้องรู้ key โดยไล่เดา byte สุดท้ายให้ padding valid (\x01) แล้วขยับไปเรื่อยๆ ทีละ byte — รายละเอียดคณิตศาสตร์เต็ม + สคริปต์ automatic อยู่ในหัวข้อ Padding Oracle Attack

เจอโจทย์ CBC — ไล่ตามนี้
แก้ ciphertext แล้วส่งกลับได้ไหม?
ได้ + รู้/เดา plaintext→ bit-flipping ปรับค่า
ได้ + server บอก padding ผิด→ padding oracle ถอดทั้งหมด
IV ถูกส่งมาด้วยไหม?
แก้ IV = ควบคุม P_1 เต็ม
block เดียวกันซ้ำ → ciphertext ซ้ำไหม?
ถ้าซ้ำอาจเป็น ECB ไม่ใช่ CBC
ใช้ผลถอด/พลิก ไปทำ auth bypass / forge token
สัญญาณเดาว่าเป็น
block เดียวกัน → ciphertext ซ้ำECB (ไปหัวข้อ ecb)
error ต่างกันเมื่อแก้ byte ท้ายpadding oracle เป็นไปได้
cookie ยาวเป็นทวีคูณของ 16AES-CBC block 16 byte
แก้ 1 byte ใน ct → plaintext block ถัดไปเพี้ยน 1 byteCBC bit-flipping ได้

4. ข้อควรระวัง & Quick Reference

  • block ก่อนหน้าที่ถูกแก้จะกลายเป็นขยะเสมอ — วางแผนว่า block ไหนยอมพังได้
  • byte แรกของข้อความคุมผ่าน IV; byte อื่นคุมผ่าน ciphertext block ก่อนหน้า
  • padding oracle ไม่ต้องรู้ key — แค่ oracle บอกถูก/ผิดก็พอ (แม้ผ่าน timing)
  • ถ้า ciphertext block ซ้ำเมื่อ plaintext ซ้ำ = ไม่ใช่ CBC ที่ IV สุ่ม (สงสัย ECB)
เนื้อหาต่อเนื่อง: chaining diagram + โหมดอื่นอยู่ในหัวข้อ AES & Block Cipher Modes (aes); การถอดทั้งข้อความแบบ step-by-step อยู่ใน Padding Oracle Attack (padding-oracle); ส่วน block-swap/ECB อยู่ในหัวข้อ ECB Mode (ecb)

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

สมมติยืนยันแล้วว่าโจทย์ใช้ CBC (IV 16 byte แรกแยกออกมา หรือ ciphertext ไม่มี block ซ้ำเลย) มีแค่เครื่อง Kali เปล่าๆ ทำตามนี้ทีละขั้น

  1. 1แยก IV ออกจาก ciphertext ก่อน: ถ้า cookie/token เป็น base64/hex ยาว ลองตัด 16 byte แรกออกมาด้วย python (`iv, ct = data[:16], data[16:]`) ดูว่าเป็น IV แยกต่างหากไหม
  2. 2ทดสอบว่าแก้ ciphertext แล้วส่งกลับให้ server ถอดได้ไหม: แก้ 1 byte แบบสุ่มแล้วส่งไปดู ถ้าไม่ crash/ไม่ถูกปฏิเสธทันที = มีโอกาสโจมตีได้
  3. 3ทดลอง padding oracle: แก้ byte สุดท้ายของ ciphertext ไล่ค่า 0x00-0xFF ทีละค่าแล้วส่งไป สังเกต response (error message ต่างกัน, HTTP status ต่างกัน, ความยาว response, หรือเวลาตอบสนอง)
  4. 4เจอ response ที่ต่างกันชัดเจนเมื่อ padding ผิด/ถูก → ยืนยันว่ามี padding oracle จริง เขียน python script อัตโนมัติ หรือติดตั้ง padbuster: `apt install padbuster` (ถ้าไม่มีให้ `git clone` repo มา) แล้วรัน `padbuster URL sample_ciphertext 16 -cookies name=value`
  5. 5ไม่มี oracle ชัดเจน (response เหมือนกันหมดไม่ว่า padding ถูกหรือผิด) → เปลี่ยนไปลอง bit-flipping แทน
  6. 6bit-flipping ต้องรู้/เดา plaintext เดิมที่ตำแหน่งหนึ่งก่อน (เช่นรู้ว่ามี `role=user` อยู่ในข้อมูล) คำนวณ byte ที่ต้องแก้ใน ciphertext block ก่อนหน้าด้วยสูตร `C'[j] = C[j] ⊕ known[j] ⊕ target[j]` (โค้ดเต็มอยู่ในหัวข้อด้านบน)
  7. 7ส่ง ciphertext ที่แก้แล้วกลับไปให้ server ถอด ตรวจว่า plaintext ที่ได้ตรงกับที่ต้องการไหม (block ก่อนหน้าจะพังเป็นขยะ แต่ยอมรับได้ถ้าไม่ใช่ข้อมูลสำคัญ)
  8. 8อยากแก้ byte แรกสุดของข้อความทั้งหมด → แก้ที่ IV โดยตรงแทน (ถ้าโจทย์ให้เราควบคุม IV ได้ block แรกจะไม่พัง)
  9. 9ลอง bit-flipping/padding oracle แล้วยังไม่ได้ผล → เช็คว่า ciphertext มี block ซ้ำกันไหม (สงสัยว่าจริงๆ เป็น ECB ไม่ใช่ CBC) หรือ server เช็ค integrity/MAC ที่ป้องกันไว้อยู่
  10. 10ถอด/ปลอมข้อความสำเร็จ → เช็ค flag/privilege ที่ได้ตรงกับที่โจทย์ต้องการ
ยืนยัน CBC แล้ว มีแค่ Kali — ไล่ตามนี้
แยก 16 byte แรกออกมาดูว่าเป็น IV แยกต่างหากไหม
แก้ ciphertext แล้วส่งกลับได้ไหม (ไม่ crash/reject ทันที)?
✅ ส่งกลับได้→ ทดสอบ padding oracle
❌ ถูกปฏิเสธทันที (มี MAC/signature)→ CBC attack ใช้ไม่ได้ตรงๆ
แก้ byte สุดท้ายไล่ 0-255 แล้วส่งไป ดู response ต่างกันไหม (error/status/เวลา)
✅ response ต่างกันชัดเจน→ Padding Oracle เต็มรูปแบบ
❌ response เหมือนกันหมด→ ลอง bit-flipping แทน
รู้/เดา plaintext เดิมที่ตำแหน่งหนึ่งไหม (known-plaintext)?
✅ รู้→ bit-flip: C'[j]=C[j]⊕known[j]⊕target[j]
❌ ไม่รู้เลย→ เดายาก เช็คว่าเป็น ECB แทนไหม
ส่ง ciphertext ที่แก้แล้ว ตรวจผลลัพธ์ (auth bypass สำเร็จไหม)
✅ สำเร็จ→ จบ
❌ ไม่ตรงเป้า→ ทบทวนตำแหน่ง byte/offset ที่แก้
เช็คว่า ciphertext มี block ซ้ำกันไหม (อาจจริงๆ เป็น ECB)
✅ ซ้ำ→ ไปหัวข้อ ECB แทน
❌ ไม่ซ้ำ→ จนมุม ทบทวนโจทย์ใหม่
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
แยก IV ออกจาก ciphertextpython3, xxd--
ทดสอบส่ง ciphertext ที่แก้แล้วกลับ servercurl, python3 (requests)pip install requestsBurp Suite Repeater (ถ้ามี)
ถอด padding oracle อัตโนมัติpython3 (script เอง), padbusterapt install padbuster-
bit-flip คำนวณ byte ที่ต้องแก้python3-CyberChef (XOR)
ดู hex ของ ciphertext/plaintextxxd-CyberChef (To Hex / From Hex)
🚑 ถ้าตันสนิท ลองท่าถัดไป: Padding Oracle — ถ้ามี error/timing รั่วออกมาแม้เล็กน้อย ให้ทำต่อจนถอดได้ทั้งข้อความ · ECB — ถ้าเจอ block ซ้ำ (ไม่ใช่ CBC จริง) · AES — ถ้ายังไม่แน่ใจ mode ให้กลับไปเช็คจากต้นทาง · XOR — ถ้าโจทย์จริงๆ ให้คุม IV/keystream ได้ตรงๆ แบบเดียวกับ stream cipher

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

โน้ตของฉัน

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