constgenerate = (length) => { var result = ''; var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var charactersLength = characters.length; for ( var i = 0; i < length; i++ ) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; }
// Generate a random username and display it if (usernameLength === null) { var name = "loading..."; window.location.href = "/?length=10"; } elseif (usernameLength.length > 0) { var name = generate(+usernameLength); } document.getElementById('generatedUsername').innerHTML = `Your generated username is: ${name}`;
# Do or do not, there is no flag. proxy_set_header Accept-Encoding ""; subs_filter_types text/html text/css text/xml; subs_filter"SEE{.*}""SEE{NO_FLAG_FOR_YOU_MUAHAHAHA}" ir; # https://gist.github.com/thoop/8165802 set$prerender0; if ($http_user_agent~* "googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest\/0\.|pinterestbot|slackbot|vkShare|W3C_Validator|whatsapp") { set $prerender 1; } if ($args ~ "_escaped_fragment_") { set $prerender 1; } if ($http_user_agent ~ "Prerender") { set $prerender 0; } if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") { set $prerender 0; } resolver 8.8.8.8; if ($prerender = 1) { rewrite .* /$scheme://$host$request_uri? break; proxy_pass http://prerender:3000; } if ($prerender = 0) { proxy_pass http://app:80; } } } }
let matches = url.parse(req.prerender.url).href.match(/^(http:\/\/|https:\/\/)app/gi) if (!matches) { return res.send(403, 'NO_FLAG_FOR_YOU_MUAHAHAHA validate'); } next(); }
// Adapted from https://github.com/prerender/prerender/blob/master/lib/plugins/removeScriptTags.js // except return a 403 instead of replacing the script tag constnoScriptsPlease = (req, res, next) => { if (!req.prerender.content || req.prerender.renderType != 'html') { returnnext(); }
var matches = req.prerender.content.toString().match(/<script(?:.*?)>(?:[\S\s]*?)<\/script>/gi); if (matches) return res.send(403, 'NO_FLAG_FOR_YOU_MUAHAHAHA script');
matches = req.prerender.content.toString().match(/<link[^>]+?rel="import"[^>]*?>/i); if (matches) return res.send(403, 'NO_FLAG_FOR_YOU_MUAHAHAHA link');
We made a Chrome extension to improve web accessibility. But Google
keeps saying that we have "broad host permissions" so we can't list it
on the web store?
exportconstmerge = (target, source) => { for (let key in source) { if (isObject(target[key]) && isObject(source[key])) { merge(target[key], source[key]); } else { target[key] = source[key]; } } return target; }
exportconstsetStyle = (elem, propertyObject) => { for (var property in propertyObject) elem.style[property] = propertyObject[property]; }
可以看出個很明顯的 prototype pollution,但是要 pollute 哪個呢
property 呢? 先仔細往後看一下它檢查 font 的迴圈,那邊使用了
i <= utils.FONTS.length 作為 condition,是個 off by one
error,代表它會嘗試去存取 utils.FONTS[10]。
// Special fonts, such as OpenDyslexic, are not supported by default. if (!font.includes('://')) { foo = awaitimport(chrome.runtime.getURL(`assets/js/${font}.js`)); foo.bar(); } else { // Load external fonts. const customStyle = JSON.parse(document.getElementById('page-style').innerText); utils.setStyle(document.body, utils.merge({fontFamily: 'custom'}, customStyle)); document.getElementById('page-style').remove();
去讀一下作者
writeup 可知它預期是讓我們去 pollute
credentials: include 並讓它 request
http://app/,然後從 style tag 中把 flag
拿出來。我想不到這個的原因大概是因為我還是第一次知道原來 prototype
pollution 對瀏覽器內建的 api 也有用。
from Crypto.PublicKey import RSA from Crypto.Util.number import *
key = RSA.import_key(open("key").read()) c = 4881495507745813082308282986718149515999022572229780274224400469722585868147852608187509420010185039618775981404400401792885121498931245511345550975906095728230775307758109150488484338848321930294974674504775451613333664851564381516108124030753196722125755223318280818682830523620259537479611172718588812979116127220273108594966911232629219195957347063537672749158765130948724281974252007489981278474243333628204092770981850816536671234821284093955702677837464584916991535090769911997642606614464990834915992346639919961494157328623213393722370119570740146804362651976343633725091450303521253550650219753876236656017
deffermat(x, mx): a = floor(sqrt(x)) b2 = a * a - x cnt = 0 whileTrue: if is_square(b2): b = floor(sqrt(b2)) return a + b, a - b a += 1 cnt += 1 if cnt == mx: return b2 = a * a - x
from Crypto.Util.number import getPrime, long_to_bytes, bytes_to_long
withopen("flag.txt", "rb") as f: FLAG = f.read()
n = bytes_to_long(FLAG)
#make sure i have a big modulus while n.bit_length() < 2048: n *= n
defencrypt(m1, m2): e = getPrime(256) assert m1.bit_length() >= 1600and long_to_bytes(m1).startswith(b"SEE{"), 'first message must be at least 1600 bits and begin with "SEE{"' assert500 <= m2.bit_length() <= 600, 'second message must be within 500 to 600 bits'
x = gcd(gcd(gen(), gen()), gen()) for i inrange(2, 10): r, exact = gmpy2.iroot(x, i) if exact: print(i, long_to_bytes(r)) # SEE{common_moduli_with_common_exponents_daf4ede8dda5c}
import math, json from secrets import token_urlsafe from Crypto.Util.number import isPrime, bytes_to_long, long_to_bytes
defmain(): try: # create token token = token_urlsafe(8) js = json.dumps({'token': token}) # select primes print(f'Welcome to the RSA testing service. Your token is "{token}".') print('Please give me 128-bit primes p and q:') p = int(input('p=')) assert isPrime(p) and p.bit_length() == 128, 'p must be a 128-bit prime' assertstr(float(math.pi)) instr(float(p)), 'p does not look like a certain universal constant' q = int(input('q=')) assert isPrime(q) and q.bit_length() == 128, 'q must be a 128-bit prime' assertstr(float(math.e)) instr(float(q)), 'q does not look like a certain universal constant'
# encode print('We will use e=65537 because it is good practice.') e = 65537 m = bytes_to_long(js.encode()) c = pow(m, e, p * q)
# decode print('Now what should the corresponding value of d be?') d = int(input('d=')) m = pow(c, d, p * q) # interpret as json js = json.loads(long_to_bytes(m).decode()) assert js['token'] == token, 'Invalid token!' print('RSA test passed!')
if'flag'in js: from secret import flag print(flag) except Exception as e: print(e)
from hashlib import sha256 from params import g, gm, n
# for some unknown reason, not converting python int to sage ZZ will make it really slow (4s vs 31s) # probably it is because python's bigint isn't as good as sage? g = ZZ(g) gm = ZZ(gm)
primes, power = n n = product(p**w for p, w inzip(primes, power))
rem = [] mod = [] for p, e inzip(primes, power): R = Zp(p, prec=e) x = (R(gm).log() / R(g).log()).lift() assert power_mod(g, x, p**e) == gm % (p**e) rem.append(x) mod.append(p ** (e - 1))
m = int(crt(rem, mod)) assert power_mod(g, m, n) == gm print("SEE{%s}" % sha256(m.to_bytes((m.bit_length() + 7) // 8, "little")).hexdigest()) # SEE{ca66f51d61e23518c23994777ab1ad2f1c7c4be09ed3d56b91108cf0666d49d1}
或是也可以參考這個 Math
SE 的問題。總之它的作法簡單來說就是找到 的 homomorphism 而已,然後 DLP 就變成
modular inverse。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# This function is from Discord, by @wiwam845 deftheta(x, p, power): n = p**power pp = p ** (power - 1) ppp = p ** (2 * power - 1) returnint(pow(x, 2 * pp, ppp) - 1) // n
g = pow(2, (p - 1), p**e) x = randint(1, p**e) y = pow(g, x, p**e) tg = theta(g, p, e) ty = theta(y, p, e) assert ty * pow(tg, -1, p ** (e - 1)) % (p ** (e - 1)) == x % (p ** (e - 1))
# recv float io.recvuntil(b"[") f = float(io.recvuntil(b"]")[:-1]) a, b = toab(f) data.append(a >> 11) data.append(b >> 10)
io.recvline() if io.recvn(2) == b"Do": # Do you want to hit or stand? if f < 0.57: io.sendline(b"h") continue else: io.sendline(b"s") whileTrue: l = io.recvlineS().strip() if"draws"notin l: break f = float(l.split("[")[1].split("]")[0]) a, b = toab(f) data.append(a >> 11) data.append(b >> 10) else: # You have gone bust. Dealer wins! pass
defrandfloat(rng): a = rng() >> 5 b = rng() >> 6 return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0)
deftraverse(cur, future, depth=0): # C++ version of `otraverse`, not really faster... p = Popen(["./fast_traverse"], stdin=PIPE, stdout=PIPE, stderr=PIPE) stdin = str(len(future)) + "\n" stdin += "".join(map(str,future)) + "\n" stdin += str(cur) stdout, stderr = p.communicate(stdin.encode()) # print(stdout.decode()) # print(otraverse(cur,future,depth)) # print(cur) # exit() win, lose, path = stdout.decode().strip().split('\n') returnint(win), int(lose), list(path)
# context.log_level = "debug" whileTrue: l = io.recvlineS().strip() print(l) f = float(l.split("[")[1].split("]")[0]) r = randfloat(rng) while r != f: r = randfloat(rng) cur = float(l.split("p1 = ")[1].split(")")[0]) future = np.array(get_future_n(rng, 4096)) win, lose, path = otraverse(cur, future) print(win, lose, path) io.sendline("\n".join(path).encode()) for x in path: io.recvuntil(b"Do you want to hit or stand? ") # for x in path: # io.sendlineafter(b"Do you want to hit or stand? ", x.encode()) io.recvuntil(b"Score: ") w, l = map(int, io.recvlineS().strip().split('-')) if l>=1337-800: print('lose') exit(1) print(w, l) io.recvline() io.recvline()
from secrets import randbits from Crypto.Util.number import bytes_to_long
# get a one-time-pad in which exactly half the bits are set defget_xorpad(bitlength): xorpad = randbits(bitlength) return xorpad ifbin(xorpad).count('1') == bitlength // 2else get_xorpad(bitlength)
defleak_encrypted_flag(): from secret import flag return bytes_to_long(flag.encode()) ^ get_xorpad(len(flag) * 8)
# I managed to leak the encrypted flag a few times if __name__ == '__main__': for _ inrange(200): print(leak_encrypted_flag())
有個 320 bits 的 flag,使用了 200 次 one time pad
加密。特別的地方在於它的 one time pad 的 1 和
0 的數量是完全平衡的 (各 160 個)。
所以我的想法是這樣,因為 one time pad 本身的 hamming weight 只有
160,也就代表任何一個密文 和
flag 的 hamming distance 也是
160。所以這個問題就像是給予了 200 個
之中的點,然後要找距離每個點都等距的那個點 ,也就是 flag。
from sage.allimport * from Crypto.Util.number import bytes_to_long
withopen("output.txt") as f: ar = [] for l in f: ar.append(int(l))
bits = 320 vecs = [list(map(int, f"{x:0{bits}b}")) for x in ar]
P = PolynomialRing(ZZ, bits, "x") xs = P.gens() polys = [] for v in vecs: polys.append(sum([(a - b) ** 2for a, b inzip(v, xs)])) eqs = Sequence([f - g for f, g inzip(polys, polys[1:])]) M, v = eqs.coefficient_matrix() M = M.dense_matrix() print(vector(v)) A = M[:, :-1] b = vector(-M[:, -1]) print(A.dimensions()) # f = bytes_to_long(flag.encode()) # print(vector(map(int, f"{f:0{bits}b}"))) # print("sancheck", A * vector(map(int, f"{f:0{bits}b}")) == b)
deflllsolve(A, b): # find a *small* solution x such that A*x=b A = A.T nr, nc = A.dimensions() A = A.augment(matrix.identity(nr)) A = A.stack(vector(list(b) + [0] * nr)) A[:, :nc] *= 2**30 A2 = [] for row in A.LLL(): if row[:nc] == 0: A2.append(row[nc:]) A2 = matrix(A2)
# unfortunately, the known information isn't enough for LLL to find a smaller solution (the flag) # so letting known part be zero and hope it is good enough t = vector(list(map(int, f"{flag_tmpl:0{bits}b}"))) A2 = A2.stack(t) A2[:, : 4 * 8] *= 2**30 A2[:, -8:] *= 2**30 flagv = t - A2.LLL()[0] print(flagv) flag = "" for i inrange(0, len(flagv), 8): bs = "".join(map(str, flagv[i : i + 8])) flag += chr(int(bs, 2)) print(flag)
lllsolve(A, b) # SEE{50-50_can_be_leaky_4c17bf2a20c4a8df}