[MatesCTF Round 5 Qualification]-Crypto Writeup

Peterjson
7 min readJun 19, 2018

--

Hi all, lần đầu tiên MatesCTF public lên ctftime.org và Round 5 cũng khác biệt hơn khi đủ các thể loại pwn,web,crypto,re,misc chứ không thuần 1 chủ đề như các round trước. Và lần này team PiggyBird ra đề, cùng với đó là tính chất public ctftime nên khó tránh khỏi việc đề mates Round 5 khoai vcl :(. Tối hôm t7 mình ngủ thật sớm, sáng dậy sớm ăn sáng, cafe và 7h bắt đầu chiến đấu, ngồi mòn đít đến tận 10h đêm mà chỉ giải được có 2 bài 😃 là ViettelStore và web-token( 2 bài mà đội nào cũng làm được :( ), thế là mình quyết định làm 1 giấc mai hóng wu các đội. May thay BTC thông báo còn mở thêm 1 tuần, thôi gáng làm cho xong vậy, vì đề quá hay, học được quá nhiều thứ. Thôi lãm nhãm, mình đi vào phần chính thôi. Mình xin trình bày theo thứ tự các bài mình giải được :>

1. ViettelStore - Crypto100

code server.py:

Nhìn sơ qua file server thì ban đầu server khởi tạo cho tạo money range(1000000,5000000), signkey lấy random từ (letters+digits) và range được random từ 8,32. Đọc tiếp thì bài này cho ta có 5 củ mà muốn mua FLAG thì cần 1 tỷ :sad:, thôi thì đọc tiếp, đến hàm pay() thì khá hay ho

pay() function

Khi mình thấy sign dùng hash ở đây thì nghĩ ngay đến Hash Length Extension, nhưng khi đọc ngay đến đây thì khẳng định ngay là Hash Length Extension. Để ý cơ chế parse_qsl, chỗ này server loop từ đầu đến hết, nếu gặp k=product thì gán product = v, tương tự cho price. Còn nếu tồn tài 2 thằng product thì sao =)). Lúc này k, v bị ghi đè thành. Lúc này dùng Hash Length Extension sẽ hiệu quả cho phép ta add thêm '&product=FLAG&price=1000' (1000 thôi để đủ trả ahihi).

Here’s my script: solve.py

2. Web-Token - Crypto100

code server.py:

đọc qua file server đề cho thì tóm tắt như sau:

  1. Nhận input từ user, replace(':','')
  2. sau đó tạo cookie qua gen_token() và trả về cho user

[+] Khi nhận cookie thì sẽ check qua verify_token và verify_hmac. Nhận thấy chỉ cần verify_hmac(secret, mac, user + “:admin”) trả về True thì server sẽ trả flag về cho ta.

Ban đầu mình khá bối rối khi gặp HMAC (lần đầu tiên :( ). Google đọc từ implement đến lỗi cũng như writeup. Sau khi BTC release bài ViettelStore thì mình đã chuyển hẳn sang làm, khi xong ViettelStore thì quay lại đọc kĩ source lần nữa thì quên bén rằng AES default mode là ECB. Lúc này mới biết solve như thế nào :(

Ý tưởng:

hand-craft user và brute từ kí tự của HMAC_SECRET

user = pad(c)+ 'a'*12 (với c là char brute)

lúc này: pad(user + ':user' + HMAC_SECRET)

= pad(c) | 'a'*12 + ':use' | 'r' + HMAC_SECRET[:14] | pad(HMAC_SECRET[15])

Ta sẽ brute được char cuối của HMAC_SECRET do block 1 và block 4 sau khi được mã hoá sẽ giống nhau(do ở đây mode là ECB). Tiếp tục như thế cho đến khi có toàn bộ HMAC_SECRET. Sau khi có HMAC_SECRET rồi thì coi như có thể login as admin =)).

Here’s my script: solve.py

Còn 1 cách rất hay đó là fake HMAC_SECRET, vì HMAC_SECRET server lấy từ cookie ra nên lợi dụng điều đó và AES_ECB ta có thể handcraft HMAC_SECRET. Bài này mình đọc được trên ctftime.org. Các bạn có thể tham khảo thêm: link

3. ddu du ddu du ddu du ddu du ddu du ddu du ddu du ddu du ddu du ddu du - Crypto100

Nghe nhạc phát nào :D

Challenge: nc ec2–13–251–81–16.ap-southeast-1.compute.amazonaws.com 3333

Đây là 1 bài blackbox với giao diện cơ bản như sau:

Hmmm ... fuzz thử 1 tý ở option 1 thì mình nhận ra quy luật sau:

Điều này có nghĩa là:

enc(ab'') sẽ có prefix là enc('a')

enc('abc') sẽ có prefix là enc('ab')

enc('abcd') sẽ có prefix là enc('abc')

Ở option 2 thì server đưa cho ta 1 chuỗi enc và send back lại cho server chuỗi plain.

Ý tưởng: do server không đặt timeout ở option 2 nên cứ lấy chuỗi enc server đưa dựa vào option 1 brute từng char plain sau đó send back và nhận flag.

Here’s my script: solve.py

4. Web-Token v2 - Crypto400

code server.py:

Bài này theo mình cảm nhận là bài hay nhất, thú vị nhất, bản thân mình học được thêm nhiều thứ, về bài này thì code khá giống như bài Web-Token. Chỉ thay đổi xíu chỗ hàm encrypt

data của chúng ta sẽ được compress trước khi thực hiện enc_ecb. Đến đây thì mình hoàn toàn bị 'đứng' và không biết xử lý như thế nào. Sau một hồi loay hoay search google thì nhận biết bài này về CRIME attack (Compression Ratio Info-Leak Mass Exploitation). Các bạn có thể tham khảo thêm: link1 link2

Ý tưởng cơ bản CRIME-attack, lúc này length 2 cipher trả về sẽ chênh lệch và ta nhận biết được 1 char trong message.

(ABCD)EFGHIJKL = ABCDEFGHIJKL = Z@%fkT2r$#!B
(input)
(AAAA)ABCDEFGH = 5ABCDEFGH = jhG*4m#$A
(input)

Qua một ngày chủ nhật, tìm đọc về CRIME và các writeup khác, và thử làm nhưng vẫn không được. Mình quyết định dựng lại hàm encrypt(), tự tạo key và HMAC_SECRET, mấu chốt mình nhận ra được là CRIME-attack sẽ compress data vì thế sẽ làm giảm len(cipher) qua đó nhận biết được từng char của HMAC_SECRET. Ở đây ta sẽ lợi dụng hàm pad để len(cipher) trả về sẽ chênh lệch như mong muốn từ đó có thể.

Ý tưởng là fuzz làm sao cho nếu chuỗi không bị lặp và có thể giữ nguyên length sau khi compress sẽ có length == 48(sau khi pad length sẽ là 64), để khi lúc ta fuzz đúng cái char mà chuỗi sẽ bị compress (tức là fuzz đúng HMAC_SECRET[0]) lúc này khi pad() thì length sẽ là 48, ta nhận biết được sự chênh lệch length và biết được đâu là HMAC_SECRET[0]

khi này 'o' là HMAC_SECRET[0]

Lặp lại ý tưởng như trên cho đến khi có được toàn bộ HMAC_SECRET và login as admin.

Tuy nhiên trong quá trình mình làm thì:

[+] brute 1 char thành công cho đến char thứ 6

[+] không thể brute ra char thứ 7 vì khi này chênh lệch length giữ các cipher là không còn nữa, đọc qua code của tác giả này : link thì khi không thể brute được 1 char thì ta đi brute 2 char. Ta phải thử 2**62 lần :sad: (thôi thì đi ngủ để cho tìm 2 char đó rồi tính tiếp)

[+] lúc này có 8 char rồi, ko lẽ brute 2 char nữa :3

[+] Theo như trang gihub (link) thì khi không brute được 1 char thì brute 2 char và sau đó quay lại 1 char, nếu không được 1 char thì brute 2 char rồi lại quay lại, do mạng nhà trọ mình yếu nên việc gửi request 2 char lên thì tốn mớ thời gian, và may mắn thay mình chỉ cần brute 2 char 1 lần, và các lần còn lại chỉ cần brute 1 char

Ý tưởng cơ bản để tìm HMAC_SECRET theo mình là như thế, nếu bạn nào đọc qua và có cách nào hay hơn thì đừng ngại trao đổi với mình nhá. Còn code thì theo flow như trên, nên bài này mình không up script.

Pheww, đây là 1 bài hay ho đáng học hỏi, qua đó cũng biết được lỗi này nghệ thuật cũng như ảnh hưởng nhiều như thế nào.

Trong suốt quá trình diễn ra cuộc thi cũng như sau cuộc thi mình vẫn không làm được bài Super Encryption System — Crypto300. Mời các bạn xem qua WU của 1 tác giả khác.

Link: https://github.com/hnn4abo/ctf/tree/master/viettel-mates-ctf-2018/super-encryption-system

(Bài Crypto300 này theo mình rất hay, mình tham khảo được trên ctftime.org)

Kết: Cảm ơn BTC cũng như các anh trong PiggyBird Team đã ra các challenge hay, đã tạo ra một cuộc thi mang lại nhiều kiến thức bổ ích, bản thân mình thì vẫn cần phải học hỏi thêm nữa và khá ngớ ngẩn khi vướng bài Web-Token vì chỉ đâm đầu vào cái HMAC_SECRET mà quên bén đi là AES default mode là ECB. Ban đầu mình còn overthinking là dùng Hash Length Extension nữa cơ :( :HMAC ra đời là để prevent HLE mà: :gà vcl:. Sẽ rút ra kinh nghiệm cho các lần sau.

--

--

Responses (1)