Giải bài Merklision - Vòng khởi động Sinh viên với An toàn thông tin 2020
Written by Minh Tran
9 minute read
Nhắc đến CTF thì chắc hẳn bạn đọc của chiếc blog này không còn lạ gì nữa. Mình từng đi thi SvATTT 3 năm, từ 2015-2017, hay chơi mảng Crypto. Mình định tính là năm 2018 nghỉ thi CTF để ra trường đấy, cơ mà giờ đến vòng loại năm 2020 rồi mà mình vẫn chưa cầm được bằng đại học :))
Dù sao thì, sau cuộc thi, những cậu em ở UET-CTF chưa làm được bài crypto đã hỏi mình làm sao để làm được bài này.
Cuộc thi năm nay mình không ra đề cũng như không liên quan đến ban tổ chức. Với kinh nghiệm tham gia cuộc thi SvATTT, mình thường tìm hiểu về người ra đề để có được bức tranh toàn cảnh về cách giải bài. Ví dụ:
PiggyBird
ra đề, liên quan đến AES padding oracle. Hồi đó mình không giải được.cothan
ra đề, tên là Onion và NotSideChannel. Về cơ bản, chỉ cần nháp 50%, code 50% thì sẽ có flag.Năm nay, người ra đề là ndh
, một người anh cực bá đến từ KMA-HCM, đồng đội của mình trong team Acebear
. Trước mình có làm một số bài của anh Hiếu. Nói chung, bài của anh Hiếu cần nháp 80% và code 20% thì mới có được flag. Như bài này là một ví dụ.
Source code của bài có thế tìm thấy ở đây
n
từ người dùng, từ đó sinh ra một chuỗi ngẫu nhiên có n
phần tử, mỗi phần tử là 1 chữ cái thuộc a-zA-Z
2020
chuỗi khác, không được trùng lặp.Merkle-Hash
từ input của người dùng đưa ra đúng với Merkle-Hash
từ đầu bài sinh ra thì được coi là hợp lệ.Đầu tiên cần tìm hiểu merkle tree - merkle hash là gì. Đầu tiên, merkle tree là một cấu trúc dữ liệu đơn giản, cho phép một khối dữ liệu được băm trên toàn bộ khối dữ liệu một cách độc lập và phân tán.
Chiếc ảnh ở Wikipedia giải thích khá rõ về cấu trúc này.
Hash tree được sử dụng trong Hash-based cryptography, áp dụng khá tốt trong việc bảo toàn dữ liệu, nghĩa là dữ liệu gửi qua mạng nếu thỏa mãn đúng merkle hash thì khả năng cao dữ liệu đó toàn vẹn. Ví dụ có thể nói đến Bitcoin & Ethereum.
Vậy thì làm sao có thể tạo ra được 1 collision, mà đề bài lại bắt tạo ra 2020 collision?
Second Preimage Attack nghĩa là mình được đưa cho một tập dữ liệu, và mình phải tìm một tập dữ liệu khác thỏa mãn hash như tập dữ liệu ban đầu.
Với Merkle Tree, nếu không kiểm tra kĩ đầu vào, rất dễ để tạp được đáp án như mong muốn. Như hình trên, ta có thể phân tích được một tập dữ liệu khác như sau:
L1, L2, L3, L4
.H(L1)+H(L2)
và H(L3)+H(L4)
làm tầng tiếp theo.Vì vậy, ta có thể đưa tập dữ liệu khác là H(L1)+H(L2), H(L3)+H(L4)
thì sẽ sinh ra mã băm cuối tương tự như dữ liệu đầu vào mà vẫn thỏa mãn là không trùng lặp. Ví dụ:
1
M(1, 2, 3, 4) = M(H(1,2), H(3,4))
Mặc dù vậy, ta mới chỉ có log2(n)
collision, với 0 < n < 2020
thì mới có khoảng 10 collisions.
Trong source code của chương trình, nếu độ dài của dữ liệu đầu vào là số lẻ, phần tử cuối cùng của chuỗi dữ liệu sẽ tự động được lặp lại
Do đó, cứ khi nào gặp bộ dữ liệu có độ dài lẻ, ta lại có 1 collision nữa bằng cách lặp phần tử cuối. Ví dụ:
1
M(1, 2, 3, 4, 5) = M(1, 2, 3, 4, 5, 5)
Mặc dù vậy, với mỗi tầng, ta có thể lặp phần tử cuối 1 lần. Mà tổng cộng cũng chỉ có 10 tầng.
Nếu chỉ nghĩ một cách rời rạc hai cách đụng độ trên, ta có thể sinh ra được 20 nghiệm, ít hơn 100 lần so với yêu cầu đề bài. Vậy làm sao để có được hơn 100 lần nghiệm nữa? Hmm … ngoài nhân đệ quy?
Chiến thuật như này:
1, 2, 3, 4, 5
, thì ta sẽ có đụng độ với 1, 2, 3, 4, 5, 5
(áp dụng 2)1, 2, 3, 4, 5, 5
, thì ta sẽ có đụng độ với H(1,2), H(3,4), H(5,5)
(áp dụng 1)H(1,2), H(3,4), H(5,5)
, ta lại có đụng độ với H(1,2), H(3,4), H(5,5), H(5, 5)
(áp dụng 2)1, 2, 3, 4, 5, 5, 5, 5
(mind blown)Vậy từ 1
dữ liệu có độ dài bằng 5
, ta có thể sinh ra 4
collision như trường hợp vừa rồi (chưa tính đến các tầng tiếp theo).
Có thể suy ra được, độ dài lớn nhất mà có thể đụng độ với nhau được là lũy thừa của 2 đầu tiên lớn hơn n
(với n = 5 thì max_length = 2**3 = 8).
Ta cần ưu tiên chọn n
sao cho có thể sinh ra được càng nhiều tầng càng tốt.
Để làm được như vậy, n = (((2*2-1)*2-1)*2-1)....; n < 2020
, và ta có n = 1025
, max_length = 2048
.
Nếu làm như vậy thì có bao nhiêu nghiệm? Mình cũng không biết nữa …
Có hai cách để sinh ra các trường hợp collision; cách khôn và cách ngu. Ở đây mình chọn cách ngu.
Mình chân thành xin lỗi công sức của các anh, các thầy đã dạy mình code những năm qua, nhưng mình lười nên brute được bao nhiêu thì brute thôi, giải được bài này đã ăn điểm rồi á ._.
Thuật toán của mình như sau:
2048
.set
như của đề bài, loại ra những nghiệm đã bị trùng lặp (lặp nhiều lắm :D).Code mình có lưu lại tại đây
1
2
3
4
$ python .\merklision.py
Maximum length can collide is 2048
There are 2047 collisions
Takes 11 sec to produce
Trong thời gian thi (4 tiếng), hình như có 5 đội giải được bài này. Đối với một cuộc thi CTF online, như vậy cũng khá là khoai. Một team gồm 4 người với 4 bài nghĩa là mỗi người phải hoàn thành hoàn hảo 1 bài của mình. Như đứa em nói
If you find any bugs or problems on this blog, please open an issue over on Github.
GithubFeel free to contact me via email minhtt159[at]teamkhunglong.com or facebook messages
Facebook