WRECKCTF 2022
国外的小比赛,一个人随便打打(继续逆向入门),rank:
Re
flag-checker
签到,无壳64位程序,ida直接打开反编译,按照字符顺序组合一下即得到flag。
advanced-flag-checker
ida反编译以后进行异或即可,但要注意密文c有负数,不好处理所以在ida里用r键先转为字节再用脚本处理。每一段异或恢复的字符需要转为小端。
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)
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))
prime
坑题...选择一个介于1020和1028比特的大素数上传,后边的数组a全部赋值1即可绕过。
本题的迷惑性在于题目描述以及第二部分挑战,让人以为是考查超大卡迈尔数的生成算法。虽然掉进陷阱里了也趁机学了一波伪素数相关知识,给个链接。生成超大卡迈尔数的简单算法:
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()
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()