WRECKCTF 2022

国外的小比赛,一个人随便打打(继续逆向入门),rank:

image

Re

flag-checker

签到,无壳64位程序,ida直接打开反编译,按照字符顺序组合一下即得到flag。

advanced-flag-checker

ida反编译以后进行异或即可,但要注意密文c有负数,不好处理所以在ida里用r键先转为字节再用脚本处理。每一段异或恢复的字符需要转为小端。
image

from Crypto.Util.number import *

c1 = [0x558C4DC,0x71100C9B,0xCE3D1DDE,0x322958FC,0x8CBE8F4E,0xB14A374B,0xEE9707A,0xF98DDD38,0x5D715F4D,0x410B9F90]
c2 = [1647945914,25126112,bytes_to_long(b'\xA1DB\xBB'),1096550281,bytes_to_long(b'\xF6\xE1\xEB+'),bytes_to_long(b'\xDE,hx'),1721577224,bytes_to_long(b'\xC8\xD2\xAEQ'),1813145471,1010629539]
flag = b''
for i in range(len(c1)):
    # 注意调整字节序
    flag = flag + long_to_bytes(c1[i]^c2[i])[::-1]
print(flag)
#  flag{hope_you_used_z3_for_this_128c13d7}

reverser

根据题目脚本逆向恢复key,发送给server即可得到flag。

# test
m = ''.join(f'{c:x}' for c in [13,15,1])
print(m)

s = [9]
target = '51c49a1a00647b037f5f3d5c878eb656'
for c in target:
    tmp = int(c, 16)
    s.append(tmp)
key_list = []
for i in range(len(s)-1):
    key_list.append((s[i+1]-s[i])%16)
key = ''.join(f'{c:x}' for c in key_list)
print(key)

image

Crypto

mtp

Multi-Time Pad,对输入的明文长度无限制,由于flag的长度为40,所以至少要恢复key的前40个元素。考虑每轮给server提交40个相同的字母(a-z),持续26轮,接收26组密文。对encrypt进行逆操作可恢复需要的key,最后通过爆破解密即可。exp:

from pwn import *
import string

table = string.ascii_lowercase
io = remote('challs.wreckctf.com',31239)
key = []

for j in range(26):
    io.recvuntil(b'>')
    io.sendline(b'1')
    io.recvuntil(b'?')
    io.sendline(table[j]*40)
    print(table[j])
    tmp = io.recvline()
    result = str(tmp)[11:-3]
    print(result)
    tmp_key = [ord(i) - ord('a') for i in result]
    key.append(tmp_key)

print(key)
new_key = []
for i in range(40):
    t = []
    for j in key:
        t.append(j[i])
    new_key.append(t)

io.recvuntil(b'>')
io.sendline(b'2')
print(io.recvline())

LETTERS = 'abcdefghijklmnopqrstuvwxyz'

key = [[4, 24, 14, 10, 3, 8, 13, 18, 19, 12, 2, 22, 9, 16, 11, 21, 20, 7, 17, 1, 6, 23, 25, 0, 5, 15], [15, 10, 24, 9, 22, 13, 3, 1, 12, 20, 14, 2, 19, 0, 5, 6, 23, 8, 18, 4, 17, 16, 11, 25, 7, 21], [2, 7, 3, 12, 9, 19, 11, 16, 10, 4, 25, 1, 0, 6, 21, 22, 17, 13, 23, 15, 24, 18, 20, 14, 5, 8], [3, 7, 21, 22, 9, 15, 24, 8, 16, 14, 17, 13, 2, 12, 1, 11, 0, 6, 18, 25, 4, 23, 10, 5, 20, 19], [11, 4, 19, 15, 0, 16, 22, 9, 8, 17, 6, 1, 14, 5, 20, 25, 18, 10, 13, 24, 3, 7, 2, 12, 23, 21], [20, 7, 25, 5, 11, 23, 12, 2, 8, 19, 0, 9, 16, 14, 10, 1, 6, 24, 18, 22, 17, 13, 4, 21, 15, 3], [22, 9, 3, 8, 4, 14, 16, 23, 2, 25, 18, 10, 7, 6, 19, 21, 13, 12, 5, 0, 11, 17, 15, 20, 1, 24], [22, 17, 11, 10, 9, 13, 0, 7, 21, 2, 24, 4, 14, 19, 6, 20, 23, 15, 1, 12, 18, 3, 16, 25, 5, 8], [2, 3, 8, 1, 14, 20, 24, 25, 10, 17, 5, 6, 15, 4, 0, 18, 12, 7, 21, 19, 9, 22, 16, 13, 11, 23], [14, 22, 23, 21, 6, 12, 9, 3, 5, 10, 0, 13, 1, 24, 8, 2, 16, 15, 19, 18, 17, 7, 25, 20, 11, 4], [2, 3, 21, 15, 23, 24, 6, 0, 17, 25, 14, 19, 10, 5, 9, 16, 7, 20, 12, 4, 1, 13, 8, 18, 22, 11], [11, 17, 12, 10, 14, 21, 15, 23, 19, 24, 2, 25, 3, 1, 18, 20, 22, 4, 5, 7, 13, 0, 9, 16, 8, 6], [1, 10, 16, 17, 13, 3, 6, 15, 7, 24, 21, 2, 14, 8, 12, 18, 4, 23, 11, 19, 20, 9, 25, 5, 0, 22], [16, 15, 5, 9, 6, 7, 0, 17, 22, 19, 11, 1, 24, 12, 3, 21, 23, 10, 8, 25, 4, 14, 2, 20, 13, 18], [20, 25, 13, 12, 24, 3, 14, 21, 6, 23, 10, 1, 15, 0, 18, 11, 17, 19, 8, 9, 2, 7, 5, 22, 4, 16], [2, 16, 20, 18, 15, 14, 1, 4, 12, 19, 8, 3, 9, 24, 5, 10, 11, 22, 23, 0, 17, 7, 21, 6, 25, 13], [16, 4, 22, 25, 3, 8, 23, 19, 20, 12, 14, 0, 1, 17, 10, 13, 15, 6, 2, 11, 5, 18, 24, 7, 9, 21], [24, 18, 16, 23, 22, 0, 12, 3, 20, 15, 7, 2, 8, 4, 1, 17, 5, 10, 19, 9, 21, 13, 25, 6, 14, 11], [7, 18, 1, 16, 0, 2, 14, 21, 4, 5, 19, 25, 20, 6, 10, 9, 22, 13, 15, 12, 23, 3, 17, 8, 24, 11], [7, 15, 4, 25, 24, 18, 6, 12, 21, 13, 1, 11, 23, 20, 5, 3, 22, 8, 16, 17, 9, 10, 2, 0, 19, 14], [0, 5, 18, 16, 10, 11, 17, 15, 9, 6, 14, 25, 19, 20, 8, 21, 3, 24, 22, 7, 4, 2, 13, 12, 23, 1], [24, 20, 16, 3, 15, 1, 22, 18, 4, 5, 14, 2, 6, 9, 11, 0, 23, 8, 25, 12, 10, 17, 21, 7, 13, 19], [18, 13, 10, 23, 6, 9, 8, 0, 16, 20, 4, 22, 7, 24, 1, 19, 17, 15, 21, 25, 3, 5, 2, 12, 11, 14], [21, 13, 22, 16, 4, 0, 17, 1, 3, 7, 24, 6, 8, 23, 2, 11, 20, 9, 12, 5, 19, 14, 10, 25, 18, 15], [0, 16, 25, 18, 12, 6, 14, 8, 3, 15, 1, 5, 23, 20, 17, 24, 19, 21, 7, 13, 11, 4, 9, 22, 2, 10], [24, 0, 11, 4, 22, 20, 18, 2, 17, 5, 1, 12, 15, 10, 25, 21, 23, 9, 3, 7, 14, 19, 8, 6, 16, 13], [10, 20, 7, 8, 15, 11, 16, 22, 0, 23, 1, 19, 9, 13, 25, 18, 12, 6, 17, 5, 14, 3, 21, 24, 2, 4], [25, 9, 23, 12, 24, 5, 11, 3, 16, 14, 1, 21, 6, 22, 20, 8, 0, 2, 10, 13, 15, 7, 18, 17, 19, 4], [4, 15, 25, 2, 6, 16, 23, 10, 13, 11, 22, 1, 17, 5, 8, 20, 0, 19, 7, 14, 21, 24, 18, 12, 3, 9], [13, 18, 16, 24, 11, 0, 6, 20, 5, 15, 7, 12, 2, 14, 17, 22, 3, 23, 21, 8, 19, 9, 25, 4, 10, 1], [22, 25, 8, 23, 14, 3, 18, 15, 9, 6, 21, 17, 0, 13, 10, 20, 4, 7, 5, 1, 24, 12, 2, 16, 19, 11], [11, 8, 22, 23, 15, 0, 2, 4, 6, 17, 9, 12, 13, 19, 10, 5, 18, 16, 20, 3, 1, 21, 24, 14, 7, 25], [5, 7, 3, 18, 22, 11, 8, 21, 23, 0, 13, 9, 14, 10, 20, 1, 25, 17, 12, 15, 24, 4, 6, 16, 2, 19], [13, 12, 16, 9, 4, 7, 21, 25, 23, 18, 3, 0, 10, 15, 14, 8, 1, 17, 20, 22, 11, 24, 2, 6, 5, 19], [16, 10, 4, 0, 24, 2, 12, 21, 5, 22, 23, 8, 6, 1, 9, 14, 18, 11, 17, 19, 3, 13, 20, 7, 25, 15], [8, 1, 3, 4, 25, 18, 0, 13, 22, 21, 19, 15, 24, 20, 12, 9, 7, 5, 6, 16, 17, 14, 10, 23, 11, 2], [17, 4, 0, 21, 18, 19, 24, 11, 20, 25, 15, 7, 10, 14, 3, 9, 6, 1, 2, 23, 13, 12, 22, 16, 8, 5], [1, 22, 18, 15, 16, 25, 4, 13, 8, 2, 12, 20, 21, 0, 11, 23, 5, 9, 6, 24, 7, 3, 10, 14, 17, 19], [1, 11, 24, 25, 16, 20, 19, 7, 2, 3, 5, 22, 8, 12, 9, 23, 0, 10, 6, 21, 14, 15, 4, 17, 13, 18], [23, 14, 8, 11, 6, 9, 21, 4, 13, 1, 19, 18, 20, 24, 7, 22, 15, 0, 12, 17, 2, 5, 3, 16, 10, 25]]
c = 'iccy{ktuv_ooa_tpfta_fvkhyxisgrimncibply}'
m = ''
for i in range(40):
    if c[i] not in LETTERS:
        m = m + c[i]
    else:
        for j in LETTERS:

            if chr(key[i][ord(j) - ord('a')] + ord('a')) == c[i]:
                print(m)
                m = m + j
print(m)
# flag{oops_key_reuse_bwcjpqdweoclkwlbkoc}

token

题目是一个简单的身份鉴别系统,用户必须输入gary的aes密文才能通过验证得到flag,但加密接口过滤了gary这个关键词,即请求加密gary无返回值。漏洞主要出现在ecb模式,题目没有限制输入长度,那么我们可以随意填充一组明文长度,然后让第二组明文为gary即可。

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

print(pad(b'gary',AES.block_size))
print(pad(b'1111111111111111gary',AES.block_size))

image

prime

坑题...选择一个介于1020和1028比特的大素数上传,后边的数组a全部赋值1即可绕过。

image
本题的迷惑性在于题目描述以及第二部分挑战,让人以为是考查超大卡迈尔数的生成算法。虽然掉进陷阱里了也趁机学了一波伪素数相关知识,给个链接。生成超大卡迈尔数的简单算法:

import random

k = 374490777411980023733618604109534049224340808164355316690083687531208402341139014648322160464971229940
n = (6*k+1)*(12*k+1)*(18*k+1)
if all([pow(random.randrange(1,n), n-1, n) == 1 for i in range(256)]):
    print('okk')
    print(n)
    print(n.bit_length())

# while True:
#     k = getRandomNBitInteger(338)
#     if isPrime(6*k+1) and isPrime(12*k+1) and isPrime(18*k+1):
#         print(k)
#         break

rsa

server是rsa的签名系统,签名接口过滤了字符串pandaman,但要成功验证pandaman的签名值才能得到flag。思路就是利用rsa的同态性把pandaman乘上一个值k再进行签名,得到s,再对k进行签名,记为s1,则pandaman的签名为\(s\cdot s_1^{-1}modn\)。对于n的获取,选择明文攻击即可,选择\(m_3=m_2^{2}=m_1^{4}\),加密三次,\(c_1^2-c_2\)\(c_2^2-c_3\)的公因数大概率就是n。完整exp:

from pwn import *
from Crypto.Util.number import *
from gmpy2 import *

m = "PANDAMAN! I LOVE PANDAMAN! PANDAMAN MY BELOVED! PANDAMAN IS MY FAVORITE PERSON IN THE WHOLE WORLD! PANDAMAN!!!!"+chr(0)

io = remote('challs.wreckctf.com',31745)
io.recvlines(3)
io.recvuntil(b'>>')
io.sendline(b'1')
io.recvuntil(b':')
io.sendline(chr(2))
c1 = eval(io.recvline()[1:-1])
print(c1)
io.recvlines(3)
io.recvuntil(b'>>')
io.sendline(b'1')
io.recvuntil(b':')
io.sendline(chr(4))
c2 = eval(io.recvline()[1:-1])
print(c2)
io.recvlines(3)
io.recvuntil(b'>>')
io.sendline(b'1')
io.recvuntil(b':')
io.sendline(chr(16))
c3 = eval(io.recvline()[1:-1])
print(c3)

tmp1 = c1**2 - c2
tmp2 = c2**2 - c3
n = gcd(tmp1,tmp2)

io.recvlines(3)
io.recvuntil(b'>>')
io.sendline(b'1')
io.recvuntil(b':')
io.sendline(m)
fake_sign = eval(io.recvline()[1:-1])
sign = fake_sign*invert(c1**8,n)%n

io.recvlines(3)
io.recvuntil(b'>>')
io.sendline(b'2')
io.recvuntil(b':')
io.sendline(str(sign))
io.interactive()

image

lsfr

对于明文加密次数无限制,故通过加密624次得到624组密文,明文已知,所以可以异或得到key。利用key对lsfr逆推得到random.getrandbits(32)的值,624组全部得到以后可以用randcrack对mt19937进行预测,得到下一组的key对flag的密文进行解密。exp:

from Crypto.Util.number import *
from pwn import *
from sage.all import *
from pylfsr import LFSR
from randcrack import RandCrack
from tqdm import tqdm

p = remote('challs.wreckctf.com', 31310)
mask = [32, 26, 20, 11, 8, 5, 3, 1]
A = Matrix(GF(2), 32, 32)

for i in range(31):
    A[i, i + 1] = 1

for i in range(len(mask)):
    A[31, 32 - mask[i]] = 1

def getflag(key):
    p.sendlineafter('>> ', '2')
    encflag = int(p.recvline()[:-1], 2)
    return long_to_bytes(encflag ^ key)

def enc(m):
    p.recvuntil(b'>>')
    p.sendline(b'1')
    print(p.recvuntil(b'Enter String: '))
    p.sendline(m)
    c = p.recvline()[:-1]
    print('done')
    c = int(c, 2)
    mm = bytes_to_long(m)
    key = bin(c ^ mm)[2:].rjust(128, '0')
    return key

def restate(key):
    key = list(map(int, list(key[:32])))
    v = vector(GF(2), key)
    state = map(str, list(A ** (-0x1337) * v)[::-1])
    state = int(''.join(list(state)), 2)
    return state


def getkey():
    r = rc.predict_getrandbits(32)
    state = [int(i) for i in list(bin(r)[2:].zfill(32))]
    l = LFSR(fpoly=[32, 26, 20, 11, 8, 5, 3, 1], initstate=state)
    for i in range(0x1337):
        l.next()
    key = []
    for i in range(200):
        key.append(str(l.next()))
    return int(''.join(key), 2)


rc = RandCrack()
for i in tqdm(range(624)):
    print(0)
    rn = restate(enc(b'a' * 16))
    print('ok')
    rc.submit(rn)

key = getkey()
print(getflag(key))
p.interactive()
posted @ 2022-10-03 09:31  ZimaB1ue  阅读(61)  评论(0编辑  收藏  举报