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道题。

Last modification:February 6, 2024
如果觉得我的文章对你有用,请随意赞赏