source
và viết công thức ra là được. Có được:
HTB{4_b3tTeR_w3apOn_i5_n3edeD!?!}
chi + i
trong hàm encrypt()
thành chi - i
là có được ngay hàm decrypt()
, vậy ta có code:
HTB{DID_YOU_KNOW_ABOUT_THE_TRITHEMIUS_CIPHER?!_IT_IS_SIMILAR_TO_CAESAR_CIPHER}
n
là một số nguyên tố lỏ 1024 bits, tới đây thì mình vác đi giải luôn. Đơn giản vì khi n
là số nguyên tố, phi hàm Euler fn
của n
sẽ chính bằng n-1
, lí do tại sao thì hướng dẫn sử dụng phi hàm Euler đã có (dùng để đếm số các số co-prime với một số cho trước).d
bằng cách nào:
HTB{0h_d4mn_4ny7h1ng_r41s3d_t0_0_1s_1!!!}
params
cực bé nên mình có thể phang thẳng dlog
vào bài mà không cần lo nghĩ gì cả. Cụ thể code như sau:
HTB{y0u_n3ed_a_b1gGeR_w3ap0n!!}
confusion
and diffusion
trong mã hóa đối xứng. Chính vì vậy mình có thể tự gen một hàm decrypt()
sử dụng để giải mã.
HTB{th1s_1s_th3_t1ny_3ncryp710n_4lg0r1thm_____y0u_m1ght_h4v3_4lr34dy_s7umbl3d_up0n_1t_1f_y0u_d0_r3v3rs1ng}
iv
và enc
, thứ mình cần tìm lại là key
.key
cũng chính là shared_secret
trong các dạng bài giao thức khóa DH quen thuộc, và vấn đề nằm ở hai khóa riêng priv_a
và priv_b
(chỉ cần tìm một trong hai là có thể giải được bài rồi). Tuy nhiên, bài không define cho ta một ellip cụ thể, rõ ràng mình cần tìm lại ellip này.G
, A
và B
đều thuộc ellip xét trong trường Fp
, cụ thể tọa độ của 3 điểm này sẽ thỏa mãn phương trình của ellip, hay:b
và trường của ellip. Trừ phương trình (1) cho (2) và (3) và chuyển vế, ta thu được 2 phương trình dạng left = 0 (mod p)
, lấy GCD
của hai cái left
này giúp mình tìm lại p
, đồng thời giải luôn được b
:priv_a
hoặc priv_b
. Mình đã nghĩ tới ý tưởng áp dụng Smart's Attack nhưng khi check lại thì điều kiện E.order() == p
bị sai. Đánh liều dlog
ra thì lại làm được :penguin::priv_a = 4
, đem đi giải key
là xong (ai ngựa ngựa có thể tìm luôn priv_b
rồi assert
thử xem đúng hay sai).
HTB{0rD3r_mUsT_b3_prEs3RveD_!!@!}
G.order() = 11
, đồng nghĩa với việc ta có thể brute force các giá trị của a
trong đoạn [1; 11]
và tiếp tục giải.Tuệ
support nên đã làm được. Bài thuộc một dạng RSA với partial known bits
(nói là bits thôi nhưng thực ra các số p
và q
đã bị khuyết mất các chữ số).p
và q
kia chính là các số còn lại sau khi thay đổi p
, q
ban đầu. Cụ thể với p
từ vị trí [0]
đến cuối thì cách một chữ số lại xóa đi một lần, q
thì xuất phát từ vị trí [1]
.p * q == n
, theo như cách nhân từng học thì:p
và q
, code hơi khó một chút nhưng khá hay. Idea ở đây là thử từng trường hợp vào chỗ khuyết rồi nhân lại xem thỏa mãn hay không.
HTB{v3r1fy1ng_pr1m3s_m0dul0_p0w3rs_0f_10!}
Tuệ
, shout-out :fire:Permuted
. Dạng này khá mới với mình nhưng được mọi người support nên cũng đấm được.key
, cụ thể là tìm priv_a
hoặc priv_b
. Đọc source thì biết được phép toán trang bị trong nhóm Permuted
này, bên cạnh đó, qua việc test nhiều lần thì mình nhận ra các phần tử trong nhóm Permuted
này đều có các vòng giao hoán của nó, tạm gọi là order
.order
ở đây là số k
nhỏ nhất sao cho g**k = g
, cái này khá giống với order
trong định nghĩa nhóm. Vậy tại sao mình lại phân tích yếu tố order
này?dlog
để tìm lại key
. Nhưng dlog
ở đây chỉ có approach giống Pohlig-Hellman
, không phải hoàn toàn giống. Mình sẽ vẫn tìm lại order
rồi factor
, giải các phương trình nhỏ rồi dùng CRT
để output ra kết quả cuối cùng.a
thỏa A = g**a
, trước tiên mình sẽ thử tính g.order()
. Hàm order()
này có thể dùng trong sage hoặc tự viết cũng được, nhưng cuối cùng sẽ tìm được: g.order() = 3311019189498977856900
g.order()
mình nhận được ước số khá bé, rất dễ dàng cho thuật toán Pohlig-Hellman
trong Sn (Symmetric Group)
. Cụ thể code để tìm lại dlog
như sau:(Nhớ dùng trong sage)
a
và b
mình đã assert
oke đồng nghĩa với approach mình đúng và có thể giải flag dễ dàng rồi.
HTB{w3lL_n0T_aLl_gRoUpS_aRe_eQUaL_!!}
makelariss
cũng khá hay. Shout-out :fire:server_message
(gọi tắt là sm
). sm
sẽ được mã hóa bằng thuật toán TEA-CBC
với IV
cố định (IV
này lấy từ local), vậy còn key
thì sao? Chúng ta sẽ chính là người cung cấp key
cho bài. Thực chất server
sẽ chạy 10 vòng, ở mỗi vòng sẽ lấy vào ct
và 4 key
của mình, nếu có thể tạo ra collision giữa ct
và enc_sm
thì được accept và chạy vào vòng kế tiếp. Tất nhiên sau khi sử dụng key
nào thì key
đó sẽ trở nên useless cho vòng key
tiếp theo. ct
cũng không được reused btw :smiley_cat:key
khác nhau, tổng cộng là mình cần có 10 cặp (4 keys, ct)
để break bài này.IV
là cố định và mode là CBC
nên việc recover lại IV
không quá khó, yêu cầu hiểu về các mode là có thể làm được rồi:sm
, bản mã của sm
, key
do chúng ta cung cấp, vậy thì bằng một thủ thuật đơn giản mình có thể tìm lại được IV
:D(key, enc_sm) của CBC
khá giống với ECB
, vậy nên mình sẽ decrypt(enc_sm)
bằng mode ECB
và xor output với sm
là recover xong IV
:(Nhớ dùng lại file như bài Iced TEA
. pt
và ct
ở trên là mình lấy từ server, còn key
là của mình gửi lên)
IV = b'\r\xdd\xd2w<\xf4\xb9\x08'
(chỉ 8 bytes thôi nên đừng confuse :monkey:, just chill). Việc tìm lại IV
rất có ý nghĩa cho attack của mình vì mình phải gửi lên ct
đã encrypt
bằng key
và IV
.Equivalent keys
. Tài liệu tham khảo về Equivalent keys
có thể xem ở mục 3.5 trong đây (shout-out anh Thangcoithongminh
:fire:). Vì paper đã viết khá rõ rồi nên mình sẽ chỉ giải thích vulnerability thôi. Nếu nhìn vào hàm encrypt_block
trong class Cipher
:key
khác nhau có thể mã hóa giống nhau cho một bản rõ, ở đây maximum chỉ tìm được 3 key
khác nên số vòng test key
là 4 :monkey:key
rồi, giờ đem đi thử nghiệm thôi:
HTB{th1s_4tt4ck_m4k3s_T34_1n4ppr0pr14t3_f0r_h4sh1ng!}
HashRoll()
. Chúng ta sẽ cần break 3 lần liên tiếp để server nhả flag. Về công việc trong mỗi vòng, mình có thể tóm tắt lại như sau:
server_msg
với 32 bytes, sau đó trả ra cho chúng ta một hàm băm của server_msg
gọi là server_hash = H(server_msg)
user_state
hợp lệ, sau đó server sẽ thử băm user_state
ra, nếu H(user_state) = server_hash
thì chuyển tới vòng tiếp theouser_state
, tạm gọi là S'
sao cho H(S') = H(S)
, với S
là server_msg
. Trong đó, S'
(cũng như S
) gồm có các thành phần a
, b
, c
, d
là số bits để ROL - left rotate
(dịch vòng trái) và e
, f
là hai số 128 bits sắp ROLed
. Tuy nhiên, có hai điều cần phải lưu ý, S'
cần phải hợp lệ, tức là:
S'
a + b + c + d
không được nhỏ hơn 2 (tránh vài trường hợp đặc biệt)a
, b
, c
, d
phải nằm trong nửa đoạn [0, 128)
, e
và f
phải nằm trong khoảng (0, 2^128 - 1)
(a, b, c, d, e, f)
sao chogiờ mình sẽ đi tới cách attack, theo cả unintended và intended solution.
z3
để giải. Khi đề bài cho mình server_msg
(S
) và server_hash
(H(S)
) cũng đồng nghĩa với việc chúng ta có được các giá trị h1
, h2
(từ H(S)
) và m1
, m2
(từ S
). Về chi tiết mình sẽ không đi quá sâu mà chỉ gợi ý tìm bằng cách khai thác buffer
vì buffer = l2b(S)
. Từ đây, chắc chắn một điều ta có thể giải ra bộ số (a, b, c, d, e, f)
thỏa mãn h1' + h2' = h1 + h2
như trên bằng z3
. Đóng gói bộ số này gửi lên server là xong:(Shout-out anh lilthawg
vì bộ code lỏ này :fire:)
xor
chính là phép cộng trong trường Fp
với p = 2
, vậy còn phép dịch trái thì sao. Dịch trái 1 bit tương ứng với việc nhân số đó với 2^1, 2 bits là nhân với 2^2, tổng quát lại, dịch k bits là nhân với 2^k.A
của ta là đa thức, khi đó, ta gọi trường hữu hạn Fp
trên là một vành đa thức (polynomial rings) Fp[z]
thỏa mãn A
là một tổ hợp tuyến tính của a[i] * z^i
, a[i]
là phần tử của Fp
. Trong bài này, trường nghiên cứu là GF(2^128)
GF(2^k)
- là trường Galois đã gặp trong mã hóa AES với k = 3
, ta có GF(2^3)
và trường này có 8 phần tử phân biệt từ 0 đến 7, cụ thể như sau:Số | Biểu diễn nhị phân | Biểu diễn đa thức |
---|---|---|
0 | 000 | 0x^2 + 0x^1 + 0x^0 = 0 |
1 | 001 | 0x^2 + 0x^1 + 1x^0 = 1 |
2 | 010 | 0x^2 + 1x^1 + 0x^0 = x |
3 | 011 | 0x^2 + 1x^1 + 1x^0 = x + 1 |
4 | 100 | 1x^2 + 0x^1 + 0x^0 = x^2 |
5 | 101 | 1x^2 + 0x^1 + 1x^0 = x^2 + 1 |
6 | 110 | 1x^2 + 1x^1 + 0x^0 = x^2 + x |
7 | 111 | 1x^2 + 1x^1 + 1x^0 = x^2 + x + 1 |
GF(2^3)
được biểu diễn dưới dạng trên (cột 3). Nếu ta xor
hai phần tử với nhau, giả sử 7 xor 3
sẽ nhận được kết quả là x^2
, vì:GF(2^128)
và chứa 128 phần tử từ 0 đến 127. Khi đó, nếu tính h1'
và h2'
như bài, ta sẽ thực hiện:(e, f)
dựa vào (a, b, c, d)
và (h1, h2, m1, m2)
thỏa mãn yêu cầu đề bài, vậy attack thôi.(Code hơi dài và khó viết nên quả thật bài này Insane
cũng đáng)
ROL
, GF(2**128)
và việc tìm collision
cho hàm băm nên bài này thực sự rất hay, xứng đáng :100:
HTB{k33p_r0t4t1ng_4nd_r0t4t1ng_4nd_x0r1ng_4nd_r0t4t1ng!}
Mạnh AT18
, anh lilthawg29
với ông bạn lỏ Thụy AT20
. Về flow của các challs dưới đây thì có thể mình chưa thể nắm hết được nhưng mình sẽ cố gắng trình bày chính xác nhất có thể, thanks for reading :heart:Setup.sol
:RussianRoulette.sol
:Setup.sol
sẽ được thực thi chính, và flow của chương trình sẽ như sau (cũng có thể hỏi ChatGPT nếu stuck):
Setup
TARGET
(biến immutable
, tạm hiểu là không thể thay đổi, giống với chức năng của biến constant
). Bên cạnh đó TARGET
thuộc kiểu RussianRoulette
(đã import từ file RussianRoulette.sol
)TARGET
bằng địa chỉ của RussianRoulette
và 10 Ether
khởi tạoisSolved()
, dùng để kiểm tra số dư của TARGET
, nếu bằng 0 thì trả về True
RussianRoulette.sol
:
pullTrigger()
, hàm này chúng ta có thể call được. Hàm này sẽ check nếu hash của block trước mod 10 bằng 7 thì tự hủy luôn (selfdestruct
), thực ra là nó sẽ gửi tất cả tiền về cho mình, nếu không thì in ra dòng im SAFU ... for now
(thực ra chẳng SAFU
nổi đâu vì mình sắp cho nó đi bán muối rồi :smiling_imp:)TARGET
kia rồi mình sẽ nhận được flag. Có một điều mình chưa nói là khi Spawn Docker
của bài sẽ có 2 ports cho 1 id. Thực chất 1 port là để connect để lấy thông tin còn 1 port để nc với server (để lấy flag) thôi.pullTrigger()
vì mình tương tác với nó mà, bên cạnh đó hàm cũng là cách duy nhất để thó sạch tiền của thằng cu TARGET
kia. Hàm này kiểm tra hash của block trước, nhưng bố ai biết được block trước như nào :penguin:, vậy nên khá may rủi. Tuy nhiên, hàm không giới hạn lần gọi, nên mình sẽ gọi nhiều lần pullTrigger()
cho đến khi thó được tiền thì thôi. Idea đơn giản như thế nhưng code gần trăm dòng chứ ít :penguin:(để chạy được code này thì cần spawn docker, lấy ip server, lấy info, cài đặt thư viện web3, nói chung là nhiều lắm nên cách này cũng hơi lỏ :monkey:)
HTB{99%_0f_g4mbl3rs_quit_b4_bigwin}
Foundry
nhưng tạm thời mình chưa tìm hiểu kĩ nên thôi :monkey:Setup.sol
:LuckyFaucet
:Setup.sol
biết được:
TARGET
của mình có 500 Ether
khởi tạoTARGET
bị hụt đi 10 Ether
LuckyFaucet
:
Bound
, một là max một là minsetBounds()
cho phép mình thay đổi Bound
sendRandomETH()
. Hàm này như một cái vòi nước từ túi của TARGET
, dùng để gửi tiền cho mình nhưng là tiền trong giới hạn Bound
thôi.setBounds()
là hàm mình có thể call vào, thì chắc chắn lỗ hổng nằm ở đây. Mình cần lấy đi Ether
từ TARGET
, mà tiền của TARGET
phụ thuộc vào Bound
do mình set, vậy nên đây là hướng đi.Bound
như nào mới bypass, vậy mình cần phân tích lượng tiền mà nó gửi:amountToSend
càng lớn càng tốt. Bên cạnh đó, trong Solidity, nếu mình khai báo một số k < 0
ở dạng uint64
, số trả về sẽ cực lớn, cụ thể là uint64(k) = 2**64 + k
. Sure về hướng exploit rồi, giờ thì tới code thôi:(Nhớ thay thế các giá trị vào nha :heart:)
HTB{1_f0rg0r_s0m3_U}
10 Ether
tương đương với 10 * 10**18 wei
và tiền giao dịch theo đơn vị wei
nên có phép so sánh ở dòng 56 kia :moneybag:.Standard wallet
rồi I already have a seed
bcrt1qs7qqy5573cqqd6d3uj7htn2x02hqrk3yde3ygr
HTB{n0t_y0ur_k3ys_n0t_y0ur_c01n5}
Thụy AT20
support, sau khi làm thì thấy chall chả khác :dog: gì Forensics very easy cả :penguin:Errors.sol
:Events.sol
:FixedPointMath.sol
:Interfaces.sol
:LoanPool.sol
:Setup.sol
:Token.sol
: