Phân tích và Giải pháp Kỹ thuật cho Các Bài toán Crypto, Misc và Reverse Engineering

1. Phân tích bài toán lũy thừa modulo (Crypto Warmup)

Bài toán yêu cầu khôi phục giá trị ban đầu dựa trên tính chất phân phối của phép lũy thừa modulo. Cụ thể, ta có công thức $(5m)^e \pmod n \equiv (5^e \pmod n) \cdot (m^e \pmod n) \pmod n$. Biết rằng $c \equiv m^e \pmod n$, ta chỉ cần tính toán tích của $5^e \pmod n$ và $c$, sau đó lấy phần dư theo $n$ để nhận được kết quả cuối cùng.

exp_val = 10007
ciphertext = 1285901843278876234855607310979623200548989981628646673003523113580651626686523566799395153922258813222744927018205882436414589516795415393990321785993777190284046937462277341231780571523062023964463963139910673601962881978696384360480028132774373962893042697866284303407898274683337284548529324550392212212259945699167254341062208031468355814520907121576009140399280898693924706067921614961798886587174234822238887374399666546113213239071736098162263227821798099750616137755055435397986788792824117529508255014392344357337010003489080209442530630893119917536518243474797351694663533728052713570044084663268350004738561234330890283895430742958255842196396542672482459665354739161276178850775803757753274712331067038077233072381051447436014423088822190073982228377699578821863871042501139434131053044240618505423456248872825597521393564957261041606004454706987978944644129728005540587982321571481413548381251589071459468890948819121023006292105804319208332473499823959882524985324120811768843639294924500467781666073366713198751960913508720530656411097981933156605831180926219778514434
modulus = 2422711508900009723470102727278184898228579351729629175495904760516536114771819178772843940622693480942295987032442940867670858858530606887743557817380121361626756206355705110299827107648704348792184242506797212331641569408152865458082131811787893384573565771304686373397987779236692592582009393836324438173880350455958049987506807351970912049246353746635267159741115761548052126938491673479606393396100458729618059852813438444299361468512008386975558106274324688665963516424534366163011821633197140729560513838981241752422348968312410911097523311183305812013220724215584901550592570168096761576532621840320623463208702401829189862290303098674021012353400081288819532365151476738751064469957971192132666136590103567843662591585345483671185892760751481722342403025068374371716176981888876927119331602694699049322860285991375002326127401769287658952682585275891296760732815680898653162425658904911584903825163141576325803464119867837508173795728753701563149748508464162635777787788266240105654089919642728171076155284842273517797069725130328742992830894075552022372717019366081516680737

factor_pow = pow(5, exp_val, modulus)
result = (factor_pow * ciphertext) % modulus
print(f"Kết quả khôi phục: {result}")

2. Xử lý nhiễu và phân tích thừa số nguyên tố (Crypto Wilson)

Trong tình huống này, dữ liệu đầu vào bị trộn lẫn với thành phần nhiễu. Thay vì cố gắng loại bỏ nhiễu trước, một cách tiếp cận hiệu quả hơn là phân tích trực tiếp kết quả sau khi thực hiện các phép toán modulo. Sau khi tách được giá trị trung gian, ta tiến hành phân tích thừa số. Kết quả thu được là tập hợp các thừa số nguyên tố có kích thước khác nhau. Bằng cách lọc ra hai thừa số có số bit lớn nhất và thực hiện phép nhân, sau đó chuyển đổi sang định dạng byte, ta có thể trích xuất thông tin đích. Phương pháp này dựa trên giả định rằng kích thước của chuỗi kết hợp giữa nhiễu và dữ liệu gốc sẽ không vượt quá giới hạn của modulo.

3. Phá vỡ bộ sinh số giả ngẫu nhiên tuyến tính (Crypto Predict 1)

Bài toán sử dụng mô hình Linear Congruential Generator (LCG) với công thức $x_{n+1} = (a \cdot x_n + b) \pmod m$. Để tìm ra tham số $m$, ta có thể xây dựng dãy hiệu số $d_i = x_{i+1} - x_i$ và áp dụng thuật toán ước chung lớn nhất (GCD) trên các biểu thức tổ hợp của dãy này. Khi có $m$, việc giải hệ phương trình tuyến tính để tìm $a$ và $b$ sẽ trở nên trực quan hơn thông qua nghịch đảo modulo.

Dưới đây là kịch bản tự động hóa quy trình tương tác và dự đoán giá trị tiếp theo:

import math
from Crypto.Util.number import inverse
from pwn import remote

def extract_lcg_params(samples):
    diffs = [samples[i+1] - samples[i] for i in range(len(samples)-1)]
    mod_1 = math.gcd(diffs[3]*diffs[1] - diffs[2]**2, diffs[2]*diffs[0] - diffs[1]**2)
    mod_2 = math.gcd(diffs[7]*diffs[5] - diffs[6]**2, diffs[6]*diffs[4] - diffs[5]**2)
    modulus = math.gcd(mod_1, mod_2)

    multiplier = ((samples[3] - samples[2]) * inverse(samples[2] - samples[1], modulus)) % modulus
    increment = (samples[2] - multiplier * samples[1]) % modulus
    return modulus, multiplier, increment

conn = remote("47.101.38.213", 60709)
sequence = []

for _ in range(10):
    conn.recvuntil(b"2.Quit\n", drop=False)
    conn.sendline(b"1")
    conn.recvuntil(b"format>", drop=False)
    conn.sendline(b"1")
    conn.recvuntil(b"is ", drop=False)
    sequence.append(int(conn.recvline(keepends=False)))

mod_val, mult_val, inc_val = extract_lcg_params(sequence)
current_state = sequence[-1]

for _ in range(190):
    current_state = (mult_val * current_state + inc_val) % mod_val
    conn.sendline(b"1")
    conn.recvuntil(b"format>", drop=False)
    conn.sendline(str(current_state).encode())
    conn.recvuntil(b"2.Quit\n", drop=False)

final_state = (mult_val * current_state + inc_val) % mod_val
conn.sendline(b"1")
conn.recvuntil(b"format>", drop=False)
conn.sendline(str(final_state).encode())
print(conn.recvline().decode())
conn.interactive()

4. Kết hợp khai triển nhị thức và định lý Fermat (Crypto Fermat with Binomial)

Bài toán cung cấp các gợi ý liên quan đến khai triển nhị thức bậc cao, trong đó chỉ các số hạng đầu và cuối được giữ lại khi thực hiện phép modulo. Bằng cách kết hợp với định lý nhỏ Fermat, ta có thể biến đổi các giá trị gợi ý thành các đa thức modulo $n$. Sự khác biệt giữa các biểu thức sau khi nâng lên lũy thừa cụ thể sẽ tạo ra bội số chung của các thừa số nguyên tố của $n$. Áp dụng thuật toán GCD giữa biểu thức tính toán và $n$ giúp tách thành công cặp $(p, q)$, từ đó khôi phục khóa giải mã RSA.

from Crypto.Util.number import long_to_bytes, inverse
import math

target_n = 16785815493192323072561202520621502124354484873141439416485516896177209180170954358917536655296832389378159385508553582292316986439935870611836481627326624997826278718936899131627405269577001541561786652522795230359038066756500166621870294967504124007392361309677236631475857268249665705586160191841238378747704555589725214417560311950480552359244858338836565084199965357719917862865124721829629583801017461751844801305560796234037169537157236798038347132037065583033136175781724971303334634074643113231202854595470566133689932557205673653855603056520355846208058018605770349841277407297822002222655287986001654291931
hint_a = 9100042084582559120159031222877385918131627674965428834732092483610222485185500852690169889158524199071605287690845087305188994225784309780346317628828223709405785692567210943043233210503182916135218501703770962120690794212530724251491005258749274621926983941779357144111637154016600411666779133073494389367203037839113492716852468953430962340504727673205375932008380231967356987830939184941935559824751997435555229308732296050345353884892269792098915710385538823080718443304466681676033122340721360871484264707931485035700119764663528137240048303559376884120155943565064746404763004240115053288163324152157473617990
hint_b = 9715835317933770028352656960194612511232332508578016476234653979758738724483036248158492859699534774069055258691831221183554038938838861427968839315936796645021031146434403767322049813439272141318924746372792359202961866805563916806745433288822292142587118556212747743843666517677675229877703300490191424342819740469916571702460052662852376459432341889334300260369825585572770112675953798390556158097350576177067289449449873243010686716960572551733859485003137822891360533488207469003452641541262228655905655933669756058464699629520449342902123166997135656358968094821077563145658394017534114487847397469149938035501
cipher_txt = 12824901853900928176431805967670922082099408423359348740734692908225214283313020989235896494342846615758839954388241321859784138067628962023114456647732896918446926075150525242914486413449921088854955092881525457824965211111708060188052025883249556408765110000416480911959539207515903372662706942051175602406993472605337450011214198253823995374918086046571064526610180657696977528863091044723572782646772325280660263920280922710139393502451340521170658459979145980603061780857907568888355186816350804498385690058379721372491179071169195852152316654113738209736216721551163445193680416946172818040555458013285013076026
pub_exp = 65537

term1 = pow(hint_b - 1011, 20212021, target_n) * pow(2021, 20212021, target_n)
term2 = hint_a * pow(1010, 20212021, target_n)
combined = (term1 - term2) % target_n

q_factor = math.gcd(combined, target_n)
p_factor = target_n // q_factor

phi_val = (p_factor - 1) * (q_factor - 1)
priv_exp = inverse(pub_exp, phi_val)
decrypted = pow(cipher_txt, priv_exp, target_n)
print(long_to_bytes(decrypted).decode())

5. Chuyển đổi cơ số và giải mã ký tự (Misc ezpython)

Dữ liệu đầu vào dưới dạng danh sách số nguyên thập phân, nhưng thực chất đại diện cho các giá trị bát phân. Mỗi số cần được tách thành các chữ số, sau đó quy đổi theo hệ cơ số 8 để lấy ra mã ASCII tương ứng. Quy trình chuyển đổi được thực hiện tuần tự qua các vị trí hàng trăm, hàng chục và hàng đơn vị.

input_data = [60, 170, 107, 141, 155, 145, 173, 167, 63, 63, 153, 62, 137, 163, 164, 64, 162, 67, 175]
decoded_string = ""

for val in input_data:
    hundreds = val // 100
    tens = (val // 10) % 10
    units = val % 10
    ascii_code = 64 * hundreds + 8 * tens + units
    decoded_string += chr(ascii_code)

print(f"Chuỗi giải mã: {decoded_string}")

6. Tái lập logic mã hóa và brute-force (RE installer)

Chương trình cài đặt áp dụng một thuật toán biến đổi mảng byte phức tạp, bao gồm việc cộng giá trị khóa, đảo ngược mảng và lặp lại quá trình này nhiều lần. Thay vì phân tích bytecode sâu, ta có thể xây dựng mô hình mô phỏng ngược bằng cách giữ cố định các vị trí đã biết và thay đổi từng byte của chuỗi đích. Bằng cách duyệt qua toàn bộ bảng mã ASCII khả thi tại mỗi vị trí, thực hiện chuỗi phép toán mã hóa tương ứng, và so sánh kết quả với mảng dữ liệu đích, ta có thể xác định chính xác từng ký tự. Phương pháp này đảm bảo độ chính xác tuyệt đối mà không đòi hỏi kỹ năng reverse engineering chuyên sâu.

target_cipher = [15728448, 16362025, 13718731, 13740602, 11425044, 13216326, 10048823, 13740603, 12757531, 12255100, 15138636, 12408061, 11228430, 10289095, 10114289, 14723575, 11272070, 9524519, 10267251, 12517282, 11796345, 13653174, 12495389, 13172636, 11468724, 9458930, 8956506, 12320680, 15291551, 11119205, 9568155, 10201663, 10398270, 14745427, 10944395, 13260012, 13194479, 11053619, 12145871, 11184688, 11359448, 11774503, 16602251, 15662990]
base_key = [238, 257, 150, 137, 167, 169, 184, 193, 210, 147, 219, 128, 140, 135, 185, 242, 204, 128, 132, 159, 222, 173, 226, 159, 207, 169, 154, 156, 216, 139, 168, 187, 220, 237, 207, 187, 218, 138, 218, 178, 246, 239, 246, 241]
placeholder = "0xGame{" + "a" * 42 + "}"
reconstructed_flag = ""

for pos in range(len(target_cipher)):
    for candidate in range(128):
        work_array = [ord(c) for c in placeholder]
        work_array[pos] = candidate
        active_key = base_key.copy()

        for _ in range(16):
            for idx in range(len(work_array)):
                work_array[idx] += active_key[idx]
                active_key[idx] += active_key[idx]
            active_key.reverse()

        if work_array[pos] == target_cipher[pos]:
            reconstructed_flag += chr(candidate)
            break

print(f"Flag khôi phục: {reconstructed_flag}")

Thẻ: CTF Crypto ReverseEngineering LCG RSA

Đăng vào ngày 22 tháng 5 lúc 01:21