Cookie Arena CTF 2023
Cookie_Arena_CTF_2023
CRYPTOGRAPHY WRITEUP
Author:
Pham Quoc Trung
Used Language:
Python3
Problem Solving:
Basic Operator
Sử dụng kiến thức toán học và cấu trúc đại số để giải mã flag Format FLAG: CHH{XXX}
Attachment: chal.py
cipher.txt
Ở bài này, mình thấy flag được mã hóa thông qua nhiều funtion. Ý tưởng đơn giản là mình sẽ viết ngược lại từng hàm và chạy. Theo thứ tự trong hàm ecb_mode thì mình sẽ dịch ngược pow_func, exp_func, xor_shift_right_func, mul_func, plus_func, split_block và padding_pkcs7.
Đầu tiên là pow_func:
Ở đây thì new_data = data^e (mod p). Để có thể tính lại được data thì thoạt nhìn, nó có vẻ là một bài toán khó. Tuy nhiên, để ý trong đoạn code thì e = 2 nên new_data = data^2 (mod p), hay data^2 ≡ new_data (mod p). Vấn đề này có thể được giải quyết thông qua thuật toán Cipolla's hoặc Tonelli-shanks. Ở đây, mình sử dụng sagemath để thực hiện nó.
Mình dùng sqrt(all=True) để có thể liệt kê đủ nghiệm cần tìm. Nếu các bạn dùng nth_root(2), các bạn sẽ cần dùng p trừ đi kết quả thu được để lấy được nghiệm còn lại.
Tiếp theo là tới hàm exp_func:
Khác với hàm trước, ở đây new_data = base ^ data (mod p), hay base^data ≡ new_data (mod p). Đây là bài toán về logarit rời rạc. Mình sẽ dùng luôn hàm của sagemath.
Ở đây mình có thể thấy được 1 số số mà chắc chắn không phải đáp án cho flag. Các bạn chạy thử code sẽ thấy rõ. Mình viết một đoạn code nhỏ để tạo nên một mảng đúng duy nhất:
Giờ thì không phải thao tác trên 2 mảng nữa. Giờ đến lượt hàm xor_shift_right_func:
Ở đây thì bit_loc == 1. Đầu tiên, data được dịch sang phải 1 bit, sau đó XOR với data gốc. Kết quả được AND với 0xffffffff. 0xffffffff là một hằng số hexa, tương đương với một số nguyên 32 bit với tất cả các bit đều là 1. Phép AND này đảm bảo rằng kết quả cuối cùng sẽ nằm trong phạm vi 32 bit.
Khi data >> 1, bit đầu của data bây giờ sẽ là 0. Khi đó, ta chỉ cần lấy bit tại cùng vị trí của kết quả XOR với 0, sẽ ra được bit đầu của data gốc. Để dễ hiểu hơn thì:
Giả sử data = 1011010 và data >> 1 = 0101101 (số 0 được thêm vào cho phép dịch) XOR: 1011010 0101101 1110111
Vậy thì ở đây, ta chỉ cần cho bit đầu của kết quả XOR với 0 sẽ ra bit đầu của data. Lấy bit đầu đó XOR với bit thứ 2 của new_data, sẽ ra được bit 2 của data gốc. Cứ như thế sẽ ra được data gốc. Ý tưởng ở đây sẽ là tạo ra mảng chứa 1 bit 0 trước, lấy bit 0 đó XOR với bit đầu của new_data. Ra kết quả bao nhiêu sẽ add lại vào mảng trên để XOR tiếp với bit thứ 2 của new_data. Cứ như thế sau khi XOR hết, xóa số 0 ban đầu của mảng đi, ta sẽ được data gốc.
Giờ đến mul_func:
Với hàm này, hãy để ý 0xffffffff là số lẻ trong decimal. Từ đó, ta có thể áp dụng công thức:
new_data = (data * mul) % (0xffffffff + 1) hay (data * mul) ≡ new_data % (0xffffffff + 1)
Sử dụng nghịch đảo modun của mul để giải phương trình trên, giải thích cho điều này:
(data * mul) ≡ new_data % (0xffffffff + 1) <=> data * mul * mul^-1 ≡ (new_data * mul^-1) % (0xffffffff + 1) Mà mul * mul^-1 = 1(t/c nghịch đảo modun) <=> data ≡ (new_data * mul^-1) % (0xffffffff + 1) Với data < (0xffffffff + 1), data % (0xffffffff + 1) sẽ bằng data
Tiếp tục là với hàm plus_func:
Này thì đơn giản là phép cộng, chúng ta sẽ trừ đi
Đến hàm split_block:
Mình sẽ viết hàm để join chúng lại:
Đến đây thì dường như chúng ta đã ra được flag, mình nghĩ là không cần thiết phải viết hàm để bỏ pad đi lắm. Nhưng nếu các bạn muốn tham khảo thì đây là nó:
Flag: CHH{w3lc0m3_70_7h3_m47h_w0rld(1_h4t3_1t_th3r3)}
À quên, đây là mình trình bày code để các bạn có thể step-by-step các bước. Final code mình sẽ để ở đây nhé :v
Knapsack Ls
Hệ thống mã hóa giựa trên bài toán Knapsack (bài toán xếp ba lô) đã bị coi là lỗi thời, nhưng điều đó không có nghĩa là nó có thể bị phá giải quá dễ dàng. Dựa vào implementation của mã hóa Knapsack trong knapsack.py, giải mã cipher để thu hồi flag. Format FLAG: CHH{XXX}
Attachments: pub_key.txt
knapsack.py
cipher.txt
Về knapsack và cách giải mã nó, các bạn có thể tham khảo bài viết này (Cảm ơn tác giả vì đã giải thích khá dễ hiểu)
Mình chạy thử file knapsack.py sau khi uncomment dòng # print((tmp.bit_length()+7)//8) thì nhận thấy message bị chia thành từng block 4 bytes và từ 4 bytes đó encrypt được cipher dài 17 bytes. Từ đó, mình sẽ chia cipher thành từng block 17 bytes và để ý little endian nên phải đảo ngược.
Giờ thì mình cần một hàm để giải mã knapsack. Ở đây, mình sử dụng code giải bài Archaic của ASIS CTF quals 2014. Đây là lời giải của chính btc, nên có vẻ khá uy tín.
Mình sẽ chạy đoạn code trên bằng sage -python, cùng với đó là các pipeline | grep 0 | grep 1 | grep -v "-". Mình làm như vậy để có thể lọc ra được các short-vector ứng với mỗi block cần thiết cho bước giải mã tiếp theo.
Với mỗi short-vector thu được, mình sẽ chuyển nó từ hệ nhị phân little-endian về dạng bytes để đọc được flag
Và đây là kết quả mình thu được:
Viết lại bằng tay(mình lười code :v) hoặc dùng code, ta sẽ thu được flag.
Flag: CHH{kn4p54ck_15_br0k3n_th3r3f0r3_e4sy!!!}
Rubic Cipher
Flag đã được xáo trộn bằng một thuật toán dựa trên cơ chế xoay khối rubik, tìm hiểu và áp dụng cơ chế này để thu hồi flag Format FLAG: CHH{XXX}
Attachments: rubik.txt
scramble_sequence.txt
cipher.txt
Hãy phân tích một chút về những gì chúng ta được cung cấp. File rubik.txt đã cho mình một mô hình cục rubik ban đầu với những số được đánh trên từng ô của rubik. Ở file scramble_sequence.txt, dòng đầu cho chúng ta biết được trạng thái của rubik sau khi thực hiện xoay "F". Đối với các bạn chơi rubik thì điều này sẽ tương đối dễ hiểu. Đoạn sau có sự xuất hiện của IV và KEY, mình có thể nhận ra dạng mã hóa CBC. Ở đây chúng ta thấy IV dài 54-bytes, nên mình sẽ phải chia cipher text ra từng block 54-bytes. Key cho thuật toán mã hóa trong CBC lần này là một tổ hợp xoay rubik.
Vậy thì ở đây, điều khó nhất là mình phải dựng được hàm để xoay rubik bằng python. May mắn cho mình, challenge này tương tự như một challenge trong giải rgbCTF 2020 - RubikCBC. Ở trong writeup này, người viết đã dựng sẵn cho mình hàm scramble. Việc cần làm còn lại là chia khối và giải mã theo CBC.
Flag: CHH{wh0_kn3w_rub1k_puzzl3_c4n_b3_u53d_f0r_3ncryp710n_t00?}
RSA Percent Leak
Hệ thống mã hóa RSA đã vô tình để lộ quan hệ giữa p và q (thể hiện bởi l), sử dụng l để tái tạo p, q, và thu hồi flag. Format FLAG: CHH{XXX}
Attachment: server.py
Ở đây, challenge đã cho mình sẵn n, l, c. Việc cần làm của mình ở đây là tìm ra d để có thể giải mã RSA. Để tìm được d thì phải biết phi, nghĩa là phải tính được p,q. Tuy nhiên, vấn đề ở chỗ p và q là 2 số nguyên tố lớn vl (1024-bit). Để mà factor được n dường như là điều không thể. Vậy thì tìm p,q thế nào bây giờ?
Có một dữ kiện được leak ra là l = (p & q) * (p ^ q) | 0x1337. Với ...
Flag: CHH{pl3453_pr0v1d3_4_d3t41ll3d_wr1t3up_b3c4us3_7h1s_k1nd_0f_a77ack_1s_r4th3r_r4r3}
© 2023,Pham Quoc Trung. All rights reserved.
Last updated