babycha
流密码,初始化的状态向量(state)有部分已知。
利用其中初始化为0的state[12]~state[13],用选项1调整偏移,把想泄露的flag部分搞进这个窗口即可。
from pwn import *
flag = b""
for i in range(5):
io = remote('babycha.chal.irisc.tf', 10100)
io.recvuntil(b'>')
io.sendline(b'1')
io.sendline((48-8*i) * b'A')
io.recvuntil(b'>')
io.sendline(b'2')
res = bytes.fromhex(io.recvline(keepends=False).strip().decode())
flag += res[8*i:8*i+8]
print(flag)
Accessible Sesamum Indicum
这题的背景很有意思,实现了一个相当于"没有enter键"的PIN密码锁——锁最后读入的4个数字与PIN码吻合即成功解锁。每位在16个hex字符中选取。可以上传一个最长65536的猜测序列。要求连续成功解锁16次。
写个随机化的生成算法,虽然生成不出最优解,不过多运行几次就会发现得到的猜测序列长度都在60000上下。把阈值设在63000多跑几次就能出一组解,这时猜测序列包含正确PIN码的概率已经相当高了。
import random
def generate_hex_string():
hex_chars = "0123456789abcdef"
hex_string = ""
used_subsequences = set()
while len(hex_string) < (63000):
hex_char = random.choice(hex_chars)
hex_string += hex_char
subsequence = hex_string[-4:]
if subsequence in used_subsequences:
hex_string = hex_string[:-1]
else:
used_subsequences.add(subsequence)
print(len(hex_string))
return hex_string
result = generate_hex_string()
with open('result.txt', 'w') as f:
f.write(result)
之后才知道这个问题叫做de Bruijn序列,算法不算太难理解,主要是建图的思想,再求一个欧拉回路。
不过您猜怎么着,SageMath竟然有内置这玩意的API)……
from sage.combinat.debruijn_sequence import debruijn_sequence
from pwn import *
io = remote("accessible-sesasum-indicum.chal.irisc.tf", "10104")
seq = "".join([f"{i:x}" for i in debruijn_sequence(16, 4)])
for _ in range(16):
io.sendline(seq.encode())
print(io.read())
io.interactive()
pwntools也有:
pwn.cyclic_gen("0123456789abcdef", 4).get()
dhash
题目实现了一个哈希函数,要求找到哈希为0的原像。对每块的哈希算法是$c_i=m_i^e\mod{N}$,其中$N$为素数。然后对每块$c_i$叠加地做异或,最终哈希为$H(m_1\mid\mid \cdots \mid\mid m_n)=m_1^e\oplus \cdots \oplus m_n^e \mod{N}$。
用类似RSA的方式可以求出$c_i$对应的$m_i$(这里图省事直接用nth_root()
开根了)。
因此实际上只需要求若干块$c_i$,异或起来为0。
稍做分析就会发现随便取至少3块异或起来为0的$c_i$即可,因为只用1块只有零解,只用2块只有2个相同解,都被题目限制条件过滤掉了。
N, e = (..., 65537)
m1 = Mod(0b100, N).nth_root(e)
m2 = Mod(0b010, N).nth_root(e)
m3 = Mod(0b110, N).nth_root(e)
payload = hex(m1)[2:] + hex(m2)[2:] + hex(m3)[2:]
print(payload)
Integral Communication
CBC 比特翻转。先通过控制第0块密文来控制第1块明文,再通过控制IV来控制第0块明文。
from pwn import *
origin = b'{"from": "guest", "act": "echo", "msg": "1997"}'
origin = [origin[:16], origin[16:32], origin[32:48]]
target = b'{"from": "admin", "act": "flag", "msg": "1997"}'
target = [target[:16], target[16:32], target[32:48]]
io = remote('integral-communication.chal.irisc.tf', 10103)
# 第一步交互,获取伪造前的密文与IV
io.recvuntil(b'>')
io.sendline(b'1')
io.sendline(b'1997')
io.recvuntil(b'IV:')
origin_IV = bytes.fromhex(io.recvline(keepends=False).strip().decode())
io.recvuntil(b'Command:')
ct = bytes.fromhex(io.recvline(keepends=False).strip().decode())
ct = [ct[:16], ct[16:32], ct[32:48]]
# 第二步,通过控制第0块密文来控制第1块明文,同时获得此时第0块明文(乱码,decode失败)
ct[0] = xor(xor(origin[1], target[1]), ct[0])
io.recvuntil(b'>')
io.sendline(b'2')
io.recvuntil(b'IV:')
io.sendline(origin_IV.hex().encode())
io.recvuntil(b'Command:')
io.sendline(''.join(c.hex() for c in ct).encode())
io.recvuntil(b'Failed to decode UTF-8:')
failed_pt = bytes.fromhex(io.recvline(keepends=False).strip().decode())
failed_pt = [failed_pt[:16], failed_pt[16:32], failed_pt[32:48]]
# 第三步,通过控制IV来控制第0块明文
IV = xor(xor(origin_IV, target[0]), failed_pt[0])
io.recvuntil(b'>')
io.sendline(b'2')
io.recvuntil(b'IV:')
io.sendline(IV.hex().encode())
io.recvuntil(b'Command:')
io.sendline(''.join(c.hex() for c in ct).encode())
print(io.recvline())
以上是本次比赛中4道较简单的题,没有太多可说的。在接下来的文章中,我们继续分析剩下较难的3道题。