# KCSC-CTF 2023 ## CFB64 ### Đề bài: nc 188.166.220.129 60124 chall.py: import time import sys import os from Crypto.Cipher import AES flag = os.environ.get("FLAG", b"KCSC{FAKE_FLAGGGGGGGGGGGGGGGGGGGGGG}") key = os.urandom(16) iv = os.urandom(16) def encrypt(key, iv, plaintext): cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=64) ciphertext = cipher.encrypt(plaintext) return ciphertext print(f'encrypted_flag = {encrypt(key, iv, flag).hex()}') for _ in range(23): plaintext = bytes.fromhex(input("plaintext: ")) print(f'ciphertext = {encrypt(key, iv, plaintext).hex()}') ### Giải: Bài này sử dụng mã hóa AES mode CFB nên trước tiên ta cần hiểu mã hóa này như thế nào đã. ![](https://hackmd.io/_uploads/r15V2aJH2.png) Đây là mô hình mã hóa AES mode CFB. Ta có mã hóa như sau: ![](https://hackmd.io/_uploads/rytw26JSh.png) ![](https://hackmd.io/_uploads/rkNOh61S3.png) Lần mã hóa đầu tiên: * Khối dữ liệu ngõ vào của quá trình mã hóa lấy từ IV, ứng với biểu thức I1, * Vector khởi tạo IV được mã hóa để tạo ra một khối giá trị chứa b bit, ứng với biểu thức Oj * s bit MSB của kết quả trên sẽ được dùng để XOR với s bit dữ liệu (plaintext) để tạo ra s bit ciphertext, ứng với biểu thức C#j IV là giá trị không cần bảo mật nhưng phải là giá trị không thể dự đoán trước được. IV có độ dài là b bit, bằng độ dài của một khối dữ liệu mà chuẩn mã hóa quy định. s là một số nguyên và 1 ≤ s ≤ b, đây chính là độ dài plaintext và ciphertext cho một lần mã hóa/giải mã. Giá trị của s có thể được tích hợp vào tên gọi của chế độ CFB để chỉ rõ chế độ số lượng bit được hỗ trợ. Ví dụ: * 1-bit CFB – Mỗi lần thực hiện mã hóa/giải mã 1 bit * 8-bit CFB – Mỗi lần thực hiện mã hóa/giải mã 8 bit * 64-bit CFB – Mỗi lần thực hiện mã hóa/giải mã 64 bit Lần mã hóa sau lần đầu tiên: Khối dữ liệu ngõ vào của quá trình mã hóa được ghép giã b-s bit LSB của khối ngõ vào của lần mã hóa trước đó và s bit của ciphertext của lần mã hóa trước đó, ứng với biểu thức Ij, Giá trị của bước trên được mã hóa để tạo ra một khối giá trị chứa b bit, ứng với biểu thức Oj s bit MSB của kết quả trên sẽ được dùng để XOR với s bit dữ liệu (plaintext) để tạo ra s bit ciphertext, ứng với biểu thức C#j **Hướng giải** * Đầu tiên xem nc có cái gì đã. ![](https://hackmd.io/_uploads/BJb8apyH3.png) - ta có len(encrypt_flag) = 96 nên ta có 6 khối vì ta thấy đây là CFB có segment_size = 64 nên 64bit = 8 byte = 16 hex - server trả về cho ta bản encrypt của flag và cho ta nhập plaintext kiểu hex vào để bản mã hóa.Nên em có ý tưởng đó là mình sẽ nhập 1 khối 16 hex có giá trị 0 thì ta sẽ thu được ct1 vì xor có tính chất x^0 = x nên ta sẽ dùng tính chất này để tìm cái E[iv] của flag và mình chỉ cần lấy 16 hex đầu của encrypt_flag ^ E[iv] = plaintext = 1. ![](https://hackmd.io/_uploads/HJHJlCkrh.png) Từ đây là ta có được plaintext1 rồi vào các khối còn lại ta làm tương tự đó là sẽ nhập độ dài plaintext tăng dần bằng thứ tự khối mình muốn lấy. Ví dụ: Khối 2 = 2 khối. Để tìm được khối 2 thì mình sẽ cho khối đầu tiên chính là cái plaintext1 mình vừa tìm được và 1 khối 0 nữa vì khi chạy thì ta sẽ tìm được E(c1) của khối 2 và ta lấy encrypt_flag(16:32) xor với E(c1) là sẽ tìm được plaintext2. Và ta cứ làm cho đến khi tìm được hết các khối **Code** from pwn import * import binascii block_size = 16 zero_block = b'0'*16 io = remote("188.166.220.129", 60124) io.recvuntil(b'encrypted_flag = ') encrypt_flag = io.recvline().strip().decode() flag_block = len(encrypt_flag)//block_size ecrypt_flag_block = [encrypt_flag[i:i+16] for i in range(0,len(encrypt_flag),16)] flag = "" for i in range(flag_block): plaintext = binascii.hexlify(flag.encode()) + zero_block io.sendlineafter(b'plaintext: ',plaintext) io.recvuntil(b'ciphertext = ') ct = io.recvline().strip().decode() flag += xor(bytes.fromhex(ecrypt_flag_block[i]),bytes.fromhex(ct[-16:])).decode() print(flag) Flag: KCSC{S0_L0n9_&_S0_eazy_chosenn_plaintext_attack} ## Only Lord Can Go ### Đề bài: nc 188.166.220.129 60123 main.go package main import ( "fmt" "io/ioutil" "math/rand" "strconv" ) func main() { fmt.Println(" _ __ ___ ___ ___ _ ___ _____ _____ ___ ___ __ __ ") fmt.Println("| |/ / / __|/ __| / __| | | / _ \\ |_ _||_ _|| __|| _ \\\\ \\ / / ") fmt.Println("| ' < | (__ \\__ \\| (__ | |__ | (_) | | | | | | _| | / \\ V / ") fmt.Println("|_|\\_\\ \\___||___/ \\___| |____| \\___/ |_| |_| |___||_|_\\ |_| ") fmt.Println() var a,b,c,d,e,y,m int m = 1<<31 - 1 a = rand.Intn(m) b = rand.Intn(m) c = rand.Intn(m) d = rand.Intn(m) e = rand.Intn(m) y = rand.Intn(m) fmt.Println("I will give u 5 lucky numbers :>") for i:=1; i<=5; i++ { y = (a*d + b*e + c) % m fmt.Printf("Lucky number %v: %v \n", i, y) e = d d = y } fmt.Println() fmt.Println("Now show off your guessing skills, ego ._.") var guess string for i:=1; i<=23; i++ { y = (a*d + b*e + c) % m fmt.Print("Guess: ") fmt.Scan(&guess) numGuess, _ := strconv.Atoi(guess) if numGuess == y { fmt.Printf("Nai xuw !!! Remain: %v/23\n", 23-i) } else { fmt.Println("Luck is only for those who try, if you don't understand that, then get out !!!") return } e = d d = y } fmt.Println("WOW, I rly want know how do u can guess all correctly, plz sharing w me :<") content, _ := ioutil.ReadFile("flag.txt") fmt.Println(string(content)) } ### Giải: Đề bài cho ta thuật toán tính số bằng cách tính (a*d + b*e + c)%m = y với e = d và d = y và đã tính trước cho ta 5 lần tính là y1->y5 và m = 1<<31-1 = 2^31-1và yêu cầu ta phải tính thêm 23 lần nữa để lấy flag. Trước tiên lại vào nc xem server cho ta cái gì. ![](https://hackmd.io/_uploads/Hys9D0yB2.png) Server cho ta 5 số và yêu cầu đoán. Từ cách mã hóa trên ta thay 5 số vào ta có hệ phương trình 5 ẩn sau: 1193586604 = (a*d + b*e + c) % 2^31-1 90851867 = (a*1193586604 + b*d + c) % 2^31-1 1072153267 = (a*90851867 + b*1193586604 + c) % 2^31-1 1582222533 = (a*1072153267 + b*90851867 + c) % 2^31-1 457737674 = (a*1582222533 + b*1072153267 + c) % 2^31-1 Để tính được các y tiếp theo thì bắt buộc ta phải đi tìm a,b,c,d,e. Bài nên ta chỉ cần giải như phương trình 5 nghiệm bình thường và chuyển nghiệm đấy về trường số nguyên tố m là xong. Bài này em dùng Sagemath để giải hệ rồi chuyển sang python để lấy flag **Code** ***Sage** from sage.all import * # Khai báo các biến a, b, c, d, e = var('a b c d e') # Khai báo các phương trình eq4 = a * d + b * e + c == 1193586604 eq5 = a * 1193586604 + b * d + c == 90851867 eq1 = a * 90851867 + b * 1193586604 + c == 1072153267 eq2 = a * 1072153267 + b * 90851867 + c ==1582222533 eq3 = a * 1582222533 + b * 1072153267 + c == 457737674 # Giải hệ phương trình sol = solve([eq1, eq2, eq3,eq4,eq5], a, b, c,d,e) a = sol[0][0].rhs() b = sol[0][1].rhs() c = sol[0][2].rhs() d = sol[0][3].rhs() e = sol[0][4].rhs() print("a = ",f'{a}') print("b = ",f'{b}') print("c = ",f'{c}') print("d = ",f'{d}') print("e = ",f'{e}') ![](https://hackmd.io/_uploads/r1ILQyxBh.png) ***Python*** from Crypto.Util.number import * from pwn import * m = 2 ** 31 - 1 # a = -739476830427074683 / 1525423535536253042 # b = -681814611266640678 / 762711767768126521 # c = 3330280250670086008902526399 / 1525423535536253042 # d = 2309063035734740633316050453 / 1363629222533281356 # e = 350974905045546990921755053007726957821152637 / 1859484656546721365463159068657198736 a = (-739476830427074683 % m * inverse(1525423535536253042,m)) % m b = (-681814611266640678 % m * inverse(762711767768126521,m)) % m c = (3330280250670086008902526399 % m * inverse(1525423535536253042,m)) % m d = (2309063035734740633316050453 % m * inverse(1363629222533281356,m)) % m e = (350974905045546990921755053007726957821152637 % m * inverse(1859484656546721365463159068657198736,m)) % m print(a,b,c,d,e) for i in range(5): y = (a * d + b * e + c) % m e = d d = y io = remote("188.166.220.129", 60123) for _ in range(23): y = (a * d + b * e + c) % m io.sendlineafter(b'Guess: ', str(y).encode()) e = d d = y io.interactive() ==> Flag: KCSC{G0<1.19_1s_sUcK_0r_U_r_h4rd-w0rk1n9_CRYpt0gr4ph3r???}