for k inrange(16): shifted = "".join( [chr(((ord(c) - ord("a") + k) % 16) + ord("a")) for c in flag_enc] ) print(k, b16_decode(shifted))
Mini RSA
,這題就直接暴力找出
的值就好了,只要是完全立方數就是答案。
1 2 3 4 5 6 7 8 9 10 11 12 13
import gmpy2
n = 1615765684321463054078226051959887884233678317734892901740763321135213636796075462401950274602405095138589898087428337758445013281488966866073355710771864671726991918706558071231266976427184673800225254531695928541272546385146495736420261815693810544589811104967829354461491178200126099661909654163542661541699404839644035177445092988952614918424317082380174383819025585076206641993479326576180793544321194357018916215113009742654408597083724508169216182008449693917227497813165444372201517541788989925461711067825681947947471001390843774746442699739386923285801022685451221261010798837646928092277556198145662924691803032880040492762442561497760689933601781401617086600593482127465655390841361154025890679757514060456103104199255917164678161972735858939464790960448345988941481499050248673128656508055285037090026439683847266536283160142071643015434813473463469733112182328678706702116054036618277506997666534567846763938692335069955755244438415377933440029498378955355877502743215305768814857864433151287 c = 1220012318588871886132524757898884422174534558055593713309088304910273991073554732659977133980685370899257850121970812405700793710546674062154237544840177616746805668666317481140872605653768484867292138139949076102907399831998827567645230986345455915692863094364797526497302082734955903755050638155202890599808147130204332030239454609548193370732857240300019596815816006860639254992255194738107991811397196500685989396810773222940007523267032630601449381770324467476670441511297695830038371195786166055669921467988355155696963689199852044947912413082022187178952733134865103084455914904057821890898745653261258346107276390058792338949223415878232277034434046142510780902482500716765933896331360282637705554071922268580430157241598567522324772752885039646885713317810775113741411461898837845999905524246804112266440620557624165618470709586812253893125417659761396612984740891016230905299327084673080946823376058367658665796414168107502482827882764000030048859751949099453053128663379477059252309685864790106
for k inrange(10000000000): [r, exact] = gmpy2.iroot(c + n * k, 3) if exact: print(k) print(r) print(bytes.fromhex(hex(r)[2:])) print(r ** 3 - n) break
from pwn import remote, process from hashlib import md5 from itertools import product import string from sage.allimport factor, gcd, power_mod, Zmod, PolynomialRing from random import randint
conn = remote("mercury.picoctf.net", 41175)
defsolve_pow(): line = conn.recvline().decode().strip() prefix = line[33:38] suffix = line[-6:] print(f"md5({prefix} + ???) = ??? + {suffix}") for x in product(string.printable, repeat=10): s = prefix + "".join(x) if md5(s.encode()).hexdigest()[-6:] == suffix: conn.sendline(s) break
solve_pow() conn.recvuntil(b"Public") n = int(conn.recvline().decode().strip().split(" ")[-1]) e = int(conn.recvline().decode().strip().split(" ")[-1]) print(n) print(e)
BITS = 20
# https://crypto.stackexchange.com/questions/46486/rsa-given-n-e-dp-is-it-possible-to-find-d from multiprocessing.dummy import Pool import gmpy2 rs = [gmpy2.mpz(randint(1, n)) for _ inrange(5)] res = [gmpy2.powmod(r, e, n) for r in rs] # precompute r^e deffactor_with_dp(d_p): for r, re inzip(rs, res): p = gmpy2.gcd((gmpy2.powmod(re, d_p, n) - r) % n, n) if p > 1and p < n: assert n % p == 0 q = n // p return p, q ans = None for result in Pool(16).imap_unordered(factor_with_dp, range(1 << BITS, 0, -1)): ifnot result: continue p, q = result assert p * q == n print("factored") print(p) print(q) ans = str(p + q) break
defgenerate_square(alphabet): assertlen(alphabet) == pow(SQUARE_SIZE, 2) matrix = [] for i, letter inenumerate(alphabet): if i % SQUARE_SIZE == 0: row = [] row.append(letter) if i % SQUARE_SIZE == (SQUARE_SIZE - 1): matrix.append(row) return matrix
defget_index(letter, matrix): for row inrange(SQUARE_SIZE): for col inrange(SQUARE_SIZE): if matrix[row][col] == letter: return (row, col) print("letter not found in matrix.") exit()
defencrypt_string(s, matrix): result = "" iflen(s) % 2 == 0: plain = s else: plain = s + "irlgektq8ayfp5zu037nov1m9xbc64shwjd2"[0] for i inrange(0, len(plain), 2): result += encrypt_pair(plain[i : i + 2], matrix) return result
defdecrypt_string(s, matrix): assertlen(s) % 2 == 0 result = "" for i inrange(0, len(s), 2): result += decrypt_pair(s[i : i + 2], matrix) return result
alphabet = "irlgektq8ayfp5zu037nov1m9xbc64shwjd2" matrix = generate_square(alphabet) enc = encrypt_string("hello0pekomiko", matrix) print(enc) m = decrypt_string(enc, matrix) assert m == "hello0pekomiko" print(m)
from pwn import *
r = remote("mercury.picoctf.net", 40742) r.recvline() r.recvuntil(b"Here is the encrypted message: ") ct = r.recvline().decode().strip() pt = decrypt_string(ct, matrix) print(pt) r.sendlineafter(b"What is the plaintext message?", pt) print(r.recvline().decode())
defget_ciphertext_length(b: bytes): conn.recvuntil(b"Enter your text to be encrypted: ") conn.send(b + b"\n") conn.recvline() conn.recvline() returnint(conn.recvline().decode().strip())
flag = b"picoCTF{" chrs = b"{_}" + (string.ascii_lowercase + string.ascii_uppercase).encode()
from itertools import product
whileTrue: l = get_ciphertext_length(flag + b"-") # There is probably no "-" in flag for i in chrs: c = bytes([i]) b = flag + c if get_ciphertext_length(b) < l: break flag = flag + c print(flag) if c == b"}": break
Scrambled: RSA
這題我搞不太清楚為什麼和 RSA
有關係,所以就直接想辦法通靈觀察規律找出了解法。
首先是可以讓它加密長度為 1
的字串,可以發現說它的密文都是固定的。之後加密兩個字元的字串如
ab
時會發現它只有兩種密文,仔細觀察之後可以發現說那兩種密文中都有只加密
a 一個字元的的密文,所以去掉那一段之後剩下的應該就是
b 的密文,但是那卻不是只加密 b
一個字元的密文。
我是推測說它大概是和前綴有些關係的,所以就繼續做了點測試發現說符合我的猜想,不過順序都是完全隨機的。這樣我的作法是用個字典把字元與密文的對應關係存下來,然後一個一個字元去暴力破解就能找到
flag 了。
r = remote("mercury.picoctf.net", 61477) r.recvuntil(b"flag: ") flag_ct = r.recvline().decode() r.recvline() # n r.recvline() # e
defencrypt(s: str): r.sendlineafter(b"I will encrypt whatever you give me: ", s) r.recvuntil(b"Here you go: ") return r.recvline().decode().strip()
defget_represent(prefix: str, c: str, dt: dict): s = encrypt(prefix + c) for k in dt.keys(): s = s.replace(k, "") return s
dt = {} known = "picoCTF{" flag = "" for c in known: rp = get_represent(flag, c, dt) assert rp in flag_ct dt[rp] = c flag += c flag_ct = flag_ct.replace(rp, "")
whilenot flag.endswith("}"): for c in chrs: rp = get_represent(flag, c, dt) ifnot rp in flag_ct: continue dt[rp] = c flag += c flag_ct = flag_ct.replace(rp, "") print(flag) break
b16 = b16_encode("abcdef0123456789") chunks = [b16[i : i + 2] for i inrange(0, len(b16), 2)] print(chunks) hi_chars = set(c[0] for c in chunks) lo_chars = set(c[1] for c in chunks) print(hi_chars) print(lo_chars)
defencrypt(b16, key): enc = "" for i, c inenumerate(b16): enc += shift(c, key[i % len(key)]) return enc
defdecrypt(b16, key): enc = "" for i, c inenumerate(b16): enc += unshift(c, key[i % len(key)]) return enc
from z3 import *
defz3_InList(x, l): return Or([x == e for e in l])
defget_keys(ciphertext, keylen): key = [BitVec(f"key_{i}", 4) for i inrange(keylen)] ct = [ord(c) - ord("a") for c in ciphertext] hi = [ord(c) - ord("a") for c in hi_chars] lo = [ord(c) - ord("a") for c in lo_chars] s = Solver() for i, c inenumerate(ct): xc = (c - key[i % keylen]) % 16 s.add(z3_InList(xc, hi if i % 2 == 0else lo)) while s.check() == sat: m = s.model() kk = [m[k].as_long() for k in key] keystr = "".join([chr(k + ord("a")) for k in kk]) yield keystr s.add(Or([a != b for a, b inzip(key, kk)]))
print(b16) test_ct = encrypt(b16, "acbgdag") for k in get_keys(test_ct, 7): print(k, decrypt(test_ct, k))
flag_ct = "bgjpchahecjlodcdbobhjlfadpbhgmbeccbdefmacidbbpgioecobpbkjncfafbe" for keylen inrange(4, 16): for k in get_keys(flag_ct, keylen): b = decrypt(flag_ct, k) print(keylen, k, b16_decode(b))
It's Not My Fault 2
這題和它的第一題 It's Not My Fault 唯一的差別是
的範圍變成了 ,代表沒辦法用前面的暴力作法去找了。
方法的來源是 Mathematics of Public Key Cryptography. Version
2.0 的 24.5.2 這個方法是設 ,所以 ,所以原先的 。之後可以先算出一個多項式
,然後之後再對於
計算 的值然後和 做 就有很高的機率能找到答案。
不過可以看出多項式 ,再來計算 個 ,這樣的複雜度還是一樣
並沒有比較好。這個時候需要一個特殊的演算法可以讓你計算多項式在多個點的算法,這個可以參考
Mathematics of Public Key Cryptography. Version 2.0 的 2.16.1
或是 Modern Computer Algebra Third Edition 的 10.1。
from pwn import remote, process from hashlib import md5 from sage.allimport * from itertools import product import string from random import randint
conn = remote("mercury.picoctf.net", 53988)
defsolve_pow(): line = conn.recvline().decode().strip() prefix = line[33:38] suffix = line[-6:] print(f"md5({prefix} + ???) = ??? + {suffix}") for x in product(string.printable, repeat=10): s = prefix + "".join(x) if md5(s.encode()).hexdigest()[-6:] == suffix: conn.sendline(s) break
solve_pow() conn.recvuntil(b"Public") n = int(conn.recvline().decode().strip().split(" ")[-1]) e = int(conn.recvline().decode().strip().split(" ")[-1]) print(n) print(e)
BITS = 36
from sage.libs.ntl.ntl_ZZ_pX import ntl_ZZ_pContext, ntl_ZZ_pX from poly_fast import poly_fast_ntl from tqdm import tqdm
defsquare_root_attack(): K = 1 << BITS D = 1 << (BITS // 2) Z = Zmod(n) x = Z(randint(2, n - 1)) ctx = ntl_ZZ_pContext(n) xe = x ** e f_fac = [] for i in tqdm(range(0, D)): f_fac.append(ntl_ZZ_pX([-x, xe ** i], ctx)) # NTL's polynomial multiplication is much faster f = sage.all.product(f_fac) # Because itertools.product != sage.all.product xed = xe ** D ys = [xed ** b for b inrange(0, D)] for t in poly_fast_ntl(ctx, f, ys): p = gcd(t, n) if p > 1and p < n: assert n % p == 0 q = n // p return p, q
# if e*d_p != 1 (mod p), it will be infinite loop while (r := square_root_attack()) isNone: pass
p, q = r ans = str(p + q) conn.sendline(ans) print(conn.recvall().decode())
from sage.allimport * from sage.libs.ntl.ntl_ZZ_pX import ntl_ZZ_pContext, ntl_ZZ_pX
defpoly_fast_ntl(ctx, f, xs): n = len(xs) k = ceil(log(n, 2)) if (2 ** k) > n: xs = xs + [0] * ((2 ** k) - n)
defbuild_tree(xs, k): M = {} for j inrange(0, 2 ** k): M[(0, j)] = ntl_ZZ_pX([-xs[j], 1], ctx) for i inrange(1, k + 1): for j inrange(0, 2 ** (k - i)): M[(i, j)] = M[(i - 1, 2 * j)] * M[(i - 1, 2 * j + 1)] return M
defcompute(f, xs, k, M, s=0): if k == 0: yield f return r0 = f % M[(k - 1, 2 * s)] r1 = f % M[(k - 1, 2 * s + 1)] mid = len(xs) // 2 yieldfrom compute(r0, xs[:mid], k - 1, M, 2 * s) yieldfrom compute(r1, xs[mid:], k - 1, M, 2 * s + 1)
M = build_tree(xs, k) ans = list(compute(f, xs, k, M)) # Using int instead of Integer will overflow... return [Integer(pl.list()[0]) for pl in ans[:n]]
whileTrue: for c, d in product(chrs, repeat=2): r = test(flag + c + d) if r > len(flag) and r % 2 == 0: flag += c + d break else: print('no result found, probably a single "}"') break print(flag) if flag.endswith("}"): break