第三届数信杯个人赛第二名WP

admin 2026-01-09 03:08:37 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文档为第三届数信杯CTF个人赛WP,涵盖Web、逆向、密码学及取证方向。核心内容包括利用LFI漏洞与SSO绕过获取管理员权限、逆向PyInstaller提取RSA私钥解密勒索软件、FAT16磁盘镜像恢复与内存取证、SQLite空闲块数据隐藏分析、AES-CBC密钥恢复及TLS流量解密审计越权访问。文档详细提供了解题思路、脚本代码与具体操作步骤,具有较高的实战技术参考价值。 综合评分: 93 文章分类: CTF,WEB安全,逆向分析,密码学,应急响应


解题过程

1. 模型结构分析

首先加载 multimodal_model.pth,通过 state_dict 分析模型结构。模型包含三个线性层(Linear),权重形状分别为 [64, 20][32, 64][27, 32]。这表明模型是一个简单的 MLP:

  • Input: 20
  • Layer 1: Linear(20, 64) + ReLU
  • Layer 2: Linear(64, 32) + ReLU
  • Layer 3: Linear(32, 27) (Output)

2. 像素提取

提示提到“左上角前20个像素”。在图像处理中,这可能指第一行前20个 (x, 0) 或第一列前20个 (0, y)。经过测试,提取第一列(即 x=0, y=0 到 y=19)的 R 通道值,并进行 % 10 运算,输入模型后能得到有意义的 ASCII 码。

3. 编写解题脚本

编写 Python 脚本实现以下功能:

  1. 定义 SimpleMLP 类。
  2. 加载 multimodal_model.pth 权重。
  3. 读取 secret_image.png,提取 (0,0) 到 (0,19) 的 R 值。
  4. 执行推理并解码。

4. 解题代码

importtorch
importtorch.nnasnn
fromPILimportImage
importnumpyasnp

# 定义模型结构
classSimpleMLP(nn.Module):
    def__init__(self):
        super(SimpleMLP, self).__init__()
        self.fc1=nn.Linear(20, 64)
        self.fc2=nn.Linear(64, 32)
        self.fc3=nn.Linear(32, 27)
        self.relu=nn.ReLU()

    defforward(self, x):
        x=self.relu(self.fc1(x))
        x=self.relu(self.fc2(x))
        x=self.fc3(x)
        returnx

defsolve():
    # 1. 加载模型
    model=SimpleMLP()
    try:
        state_dict=torch.load('multimodal_model.pth')
        model.load_state_dict(state_dict)
        model.eval()
    exceptExceptionase:
        print(f"模型加载失败: {e}")
        return

    # 2. 加载图片并提取特征
    try:
        img=Image.open('secret_image.png')
        img=img.convert('RGB')
    exceptExceptionase:
        print(f"图片加载失败: {e}")
        return

    # 提取第一列前20个像素的R值 (x=0, y=0~19)
    pixels= []
    foryinrange(20):
        r, g, b=img.getpixel((0, y))
        pixels.append(r)

    print(f"提取的 R 值: {pixels}")

    # 计算特征: R % 10
    features= [r%10forrinpixels]
    print(f"特征向量: {features}")

    # 3. 模型推理
    input_tensor=torch.tensor(features, dtype=torch.float32).unsqueeze(0)

    withtorch.no_grad():
        output=model(input_tensor)

    output_values=output.squeeze().tolist()

    # 4. 解码 Flag
    flag_chars= []
    forvalinoutput_values:
        # 四舍五入取整转ASCII
        ascii_code=int(round(val))
        flag_chars.append(chr(ascii_code))

    flag="".join(flag_chars)
    print(f"\nFlag: {flag}")

if__name__=="__main__":
    solve()

5. 运行结果

运行脚本得到输出:

提取的 R 值: [253, 248, 247, 252, 249, 251, 254, 250, 255, 246, 248, 247, 249, 252, 251, 254, 250, 255, 253, 246]
特征向量: [3, 8, 7, 2, 9, 1, 4, 0, 5, 6, 8, 7, 9, 2, 1, 4, 0, 5, 3, 6]
Flag: flag{A12I_shu1xin2bei_2025}

Flag

flag{A12I_shu1xin2bei_2025}

二、数据分析

1.数据处理

第一问:流量分析

题目描述

第一天上班的你去查看公司的流量监控记录,发现了一串非常奇怪的流量信息,你判断出这是黑客攻击所产生的流量,请你分析流量,找出泄露的信息。

分析过程

  1. 流量包分析使用 Wireshark 或脚本分析 attack.pcapng 流量包。重点关注 HTTP 协议的流量,特别是 POST 请求和响应内容。
  2. 发现敏感信息泄露在分析 HTTP 响应数据时,发现第 3116 号数据包(Packet 3116)是一个登录页面的响应 HTML。在该 HTML 的 <head> 标签中,存在一段可疑的 JavaScript 代码,直接硬编码了系统默认凭证:
   <script>varsys_default_credential="Adm1n@2024#Secure!Pass";</script>
  1. 验证攻击路径继续追踪流量,发现第 3134 号数据包(Packet 3134)是一个 POST 请求,攻击者使用了上述泄露的凭证进行登录:

    对 Payload 进行 URL 解码:

    随后的第 3150 号数据包显示登录成功,服务器返回了后台管理页面的 HTML 内容。

  • usernameadmin

  • passwordAdm1n@2024#Secure!Pass

  • URL:/login

  • Payload:username=admin&password=Adm1n%402024%23Secure%21Pass

结论

泄露的管理员账号密码为:

admin/Adm1n@2024#Secure!Pass


第二问:数据脱敏恢复

题目描述

与你交接的同事由于工作上的疏忽将原先的居民信息文件误删除了,但是你发现公司的系统上依旧存在居民的信息,下载后发现进行了脱敏处理,你需要利用技术手段将居民信息快速整理出来。目标:找出家庭住址。

分析过程

  1. 文件分析打开 居民信息表.csv,观察数据内容。发现列的数据呈现乱码状,且包含 +/= 等字符,具有明显的 Base64 编码特征。
  2. 解密逻辑编写 Python 脚本处理 CSV 文件:
  • 读取 CSV 文件的每一行。
  • 对“手机号码”列的内容进行 Base64 解码。
  • 将解码后的手机与目标手机进行比对。
  1. 查找结果
  • 当脚本遍历到目标行时,成功匹配到手机。
  • 提取该行的“家庭住址”密文并进行 Base64 解码。
  • 解码后的地址为:江苏省....

结论

家庭住址为:

江苏省....


第三问:重名统计

题目描述

在得到居民信息之后,领导让你统计一下居民信息中重名的数量,方便后续的工作开展。目标:统计出现重名次数出现最多的人的姓名以及出现的次数。

分析过程

  1. 数据提取同样基于 居民信息表.csv,需要对“姓名”列进行处理。
  2. 统计逻辑编写 Python 脚本:
  • 遍历 CSV 文件,提取每一行的“姓名”字段。
  • 对“姓名”字段进行 Base64 解码,获取明文姓名。
  • 使用计数器(如 collections.Counter)统计每个姓名出现的次数。
  • 找出出现次数最多的姓名及其计数。
  1. 统计结果
  • 运行脚本后,统计结果显示出现次数最多的姓名是 …。
  • 该姓名出现的次数为 7 次。

结论

重名最多的人及次数为:

...7

2.数据应急

数据应急 – 恢复被删合同文件 WriteUp

1. 题目分析

题目描述黑客删除了磁盘中的文件,需要从提供的磁盘镜像 disk.img 中恢复被删除的合同文件,并提交合同编号。

2. 解题过程

2.1 镜像分析

首先分析 disk.img 的文件系统。读取镜像的前 512 字节(引导扇区):

withopen('disk.img',&nbsp;'rb')&nbsp;asf:
&nbsp; &nbsp;&nbsp;print(f.read(512))

输出显示包含 mkfs.fat 和 FAT16 字样,确认文件系统为 FAT16。

2.2 扫描删除文件

在 FAT16 文件系统中,被删除文件的目录项首字节会被标记为 0xE5。编写脚本扫描根目录区域,寻找扩展名为 PDF 的被删除文件。

关键参数计算:

  • 每扇区字节数 (Bytes Per Sector): 512
  • 保留扇区数 (Reserved Sectors): 32
  • FAT 表数量 (Number of FATs): 2
  • 每个 FAT 表扇区数 (Sectors Per FAT): 256
  • 根目录项数 (Root Entries): 512

根目录起始偏移 = 保留扇区大小 + FAT 表大小$$ \text{RootOffset} = 32 \times 512 + 2 \times 256 \times 512 = 278528 $$

在根目录区域扫描发现如下目录项:e55f5f5f5f5f7e3150444620006e114a975b975b0000114a975be12af1200000

解析该目录项:

  • 文件名:?_____~1.PDF (首字节 E5 代表已删除)
  • 起始簇号 (Offset 0x1A): 0xE1 0x2A -> 0x2AE1 (10977)
  • 文件大小 (Offset 0x1C): 0xF1 0x20 00 00 -> 0x20F1 (8433 字节)

2.3 恢复文件数据

根据起始簇号计算文件数据在磁盘中的物理偏移。数据区起始偏移 = 根目录起始偏移 + 根目录大小$$ \text{DataStart} = 278528 + (512 \times 32) = 294912 $$

簇大小 = 每簇扇区数 × 每扇区字节数 = $32 \times 512 = 16384$ 字节。

文件偏移 = 数据区起始偏移 + (起始簇号 – 2) × 簇大小$$ \text{FileOffset} = 294912 + (10977 – 2) \times 16384 = 180109312 $$

使用 Python 脚本从该偏移处读取 8433 字节,保存为 recovered.pdf

2.4 分析 PDF 内容

尝试直接在 recovered.pdf 中搜索 “HT-” 字符串未果。使用文本编辑器查看 PDF 源码,发现内容被压缩和编码。流对象 (Stream Object) 使用了过滤器:/Filter [ /ASCII85Decode /FlateDecode ]

这意味着数据先经过 Flate (zlib) 压缩,然后经过 ASCII85 编码。

2.5 解码与提取

编写脚本对 PDF 中的流进行解码:

  1. 提取 stream 和 endstream 之间的内容。
  2. 使用 base64.a85decode (Adobe 模式) 进行解码。
  3. 使用 zlib.decompress 进行解压。

在解码后的第一个流中发现如下内容:(\000H\000T\000-\0002\0000\0002\0005\000-\0000\0000\0001\0002\0003\0004)

这是 PDF 中的文本字符串格式,对应的内容为:HT-2025-001234

3. 最终答案

合同编号为:HT-2025-001234

数据应急 – 读取加密内容 WriteUp

1. 题目分析

题目描述在恢复出被删除文件后,发现存在加密文件,但管理员忘记了密钥。需要通过技术手段绕过验证直接读取加密文件中的内容(密码本)。

2. 解题过程

2.1 发现并恢复 ZIP 文件

在第一问中扫描磁盘根目录时,除了发现被删除的 PDF 文件外,还发现了一个被删除的大型 ZIP 文件:IN-SE~1.ZIP (对应长文件名 WIN-SERVER-PC-20251202-122722.zip 的一部分),起始簇号 3,大小约 171MB。

使用脚本恢复该 ZIP 文件:

# 恢复逻辑同 PDF,起始簇号 3,大小 179782904 字节

恢复得到 recovered.zip

2.2 分析 ZIP 内容

解压 recovered.zip,发现其中包含一个名为 WIN-SERVER-PC-20251202-122722.raw 的文件,大小为 2GB。

File: WIN-SERVER-PC-20251202-122722.raw
Size: 2147483648

根据文件名和大小判断,这可能是一个内存镜像或磁盘镜像。

2.3 分析 RAW 文件

对 WIN-SERVER-PC-20251202-122722.raw 进行分析,搜索文件签名。发现大量 BitLocker (-FVE-FS-) 和 TrueCrypt 签名,说明该镜像中包含加密卷。通常情况下,读取加密卷需要密钥。但题目提示“绕开验证直接读取”,且文件是内存镜像(或包含内存数据的系统镜像),这意味着解密后的数据可能以明文形式存在于内存中。

2.4 搜索敏感信息

直接在 WIN-SERVER-PC-20251202-122722.raw 文件中搜索关键词 flag{

importre
pattern=re.compile(rb'flag\{.*?\}')
# ... 读取文件并搜索 ...

搜索结果:Found flag: flag{33c3789a36bb61beb6d4268cd7d13038}

2.5 获取密码本内容

找到的 flag 即为密码本中的内容。

3. 最终答案

密码本内容为:33c3789a36bb61beb6d4268cd7d13038

3.数据分析

数据分析 第一问 WriteUp

题目回顾

安全团队提取到疑似勒索程序(en.exe),该程序采用”混合加密机制”对 PDF 文档进行加密。若要恢复文档,必须从勒索程序中获取攻击者遗留的 RSA 私钥。题目要求找到私钥,并将私钥中 -----BEGIN PRIVATE KEY----- 之后的 32 位字符作为答案提交。

分析过程

1. 初步侦察

首先对 en.exe 进行基础的字符串分析。通过提取文件中的可打印字符串,发现了 PyInstallerpyi-windows-manifest-filename 以及 _MEI 等特征字符串。这表明 en.exe 是一个使用 PyInstaller 打包的 Python 可执行文件。

2. 提取策略

PyInstaller 将 Python 脚本编译为字节码(.pyc)并与其他资源文件一起打包。这些内容通常被压缩存储(通常使用 zlib)。虽然可以使用 pyinstxtractor.py 等工具进行完整的解包,但为了快速定位文本形式的密钥文件,我们可以直接在二进制文件中搜索 zlib 压缩流并尝试解压。

3. 编写分析脚本

编写 Python 脚本 scan_zlib.py,其逻辑如下:

  1. 以二进制模式读取 en.exe
  2. 遍历文件内容,寻找 zlib 头部特征(如 \x78\x01\x78\x9c\x78\xda)。
  3. 尝试从每个可能的头部位置进行解压。
  4. 在解压后的数据中搜索 PEM 格式私钥的头部标记 -----BEGIN PRIVATE KEY-----
importzlib
importos

defscan_zlib(filename):
&nbsp; &nbsp;&nbsp;ifnotos.path.exists(filename):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"File not found:&nbsp;{filename}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return

&nbsp; &nbsp;&nbsp;withopen(filename,&nbsp;"rb")&nbsp;asf:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;data=f.read()

&nbsp; &nbsp;&nbsp;# Common zlib headers
&nbsp; &nbsp;&nbsp;possible_headers=&nbsp;[b'\x78\x01',&nbsp;b'\x78\x9c',&nbsp;b'\x78\xda']

&nbsp; &nbsp;&nbsp;foriinrange(len(data)):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifdata[i:i+2]&nbsp;inpossible_headers:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;decompressed=zlib.decompress(data[i:])
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;ifb"-----BEGIN PRIVATE KEY-----"indecompressed:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"Found potential key in zlib stream at offset&nbsp;{i}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(decompressed.decode('utf-8'))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass

if__name__=="__main__":
&nbsp; &nbsp;&nbsp;scan_zlib("en.exe")

4. 运行结果

运行脚本后,成功在文件偏移量 5156720 处发现了一个 zlib 压缩块,解压后得到了完整的 RSA 私钥。

私钥内容片段:

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCt4TrETrlzU4NV
083rqT3XUun3NnxgPVVioX8tJZUH9rjs0bdTPTMk877seCIsJ98yj2VAhmGbviZ7
...

5. 提取答案

根据题目要求,提取 -----BEGIN PRIVATE KEY----- 之后的 32 位字符(忽略换行符,通常 PEM 格式头后紧接着就是 Base64 编码的数据)。

私钥数据第一行:MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCt4TrETrlzU4NV

截取前 32 位:MIIEvgIBADANBgkqhkiG9w0BAQEFAASC

最终答案

MIIEvgIBADANBgkqhkiG9w0BAQEFAASC

数据分析 第二问 WriteUp

题目回顾

在成功获得攻击者的 RSA 私钥后,需要解密被勒索的 PDF 文件。题目指定解密 0999_内部文档0999.pdf.enc,并计算解密后文件的 32 位小写 MD5 值作为答案。

分析过程

1. 逆向分析加密逻辑

在第一问中我们确认了 en.exe 是 PyInstaller 打包的程序。为了理解具体的加密流程,我们进一步提取并分析了其中的 Python 字节码/脚本资源。

通过解压 en.exe 中的 zlib 流,我们定位到了核心加密脚本(对应提取出的 stream_362697.bin)。通过分析其中的字符串和逻辑,我们发现了关键信息:

  • 密钥存储方式:AES 密钥并非存储在每个加密文件的头部,而是统一加密后存储在名为 store.key 的文件中。
  • AES 密钥加密算法:使用 RSA 公钥加密生成的 AES 密钥,填充模式为 OAEP,哈希算法为 SHA256
  • 文件加密算法:使用 AES-256-ECB 模式,并使用 PKCS7 进行填充。

相关代码特征字符串:

store.key
OAEP
SHA256
AES-256-ECB
PKCS7

2. 解密流程

根据分析结果,制定解密步骤如下:

  1. 获取 AES 密钥
  • 读取 store.key 文件内容。
  • 使用第一问提取的 private.pem 私钥。
  • 使用 PKCS1_OAEP (SHA256) 算法解密 store.key,得到原始 AES 密钥。
  1. 解密 PDF 文件
  • 读取 0999_内部文档0999.pdf.enc 内容。
  • 使用获取到的 AES 密钥,采用 AES.MODE_ECB 模式进行解密。
  • 去除 PKCS7 填充。

3. 解密脚本

编写 Python 脚本 decrypt_final.py 实现上述逻辑:

fromCrypto.PublicKeyimportRSA
fromCrypto.CipherimportPKCS1_OAEP
fromCrypto.CipherimportAES
fromCrypto.HashimportSHA256
fromCrypto.Util.Paddingimportunpad
importhashlib

defdecrypt_final():
&nbsp; &nbsp;&nbsp;# 1. 解密 AES 密钥
&nbsp; &nbsp;&nbsp;withopen("private.pem",&nbsp;"rb")&nbsp;asf:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;private_key=RSA.import_key(f.read())

&nbsp; &nbsp;&nbsp;withopen("store.key",&nbsp;"rb")&nbsp;asf:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;enc_aes_key=f.read()

&nbsp; &nbsp;&nbsp;# 使用 RSA-OAEP (SHA256) 解密
&nbsp; &nbsp;&nbsp;cipher_rsa=PKCS1_OAEP.new(private_key,&nbsp;hashAlgo=SHA256)
&nbsp; &nbsp;&nbsp;aes_key=cipher_rsa.decrypt(enc_aes_key)
&nbsp; &nbsp;&nbsp;print(f"AES Key decrypted:&nbsp;{aes_key.hex()}")

&nbsp; &nbsp;&nbsp;# 2. 解密 PDF 文件
&nbsp; &nbsp;&nbsp;enc_pdf_path="pdfs/0999_内部文档0999.pdf.enc"
&nbsp; &nbsp;&nbsp;withopen(enc_pdf_path,&nbsp;"rb")&nbsp;asf:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;enc_content=f.read()

&nbsp; &nbsp;&nbsp;# 使用 AES-256-ECB 解密
&nbsp; &nbsp;&nbsp;cipher_aes=AES.new(aes_key,&nbsp;AES.MODE_ECB)
&nbsp; &nbsp;&nbsp;decrypted_padded=cipher_aes.decrypt(enc_content)

&nbsp; &nbsp;&nbsp;# 去除 PKCS7 填充
&nbsp; &nbsp;&nbsp;decrypted_content=unpad(decrypted_padded,&nbsp;AES.block_size)

&nbsp; &nbsp;&nbsp;output_path="0999_decrypted_final.pdf"
&nbsp; &nbsp;&nbsp;withopen(output_path,&nbsp;"wb")&nbsp;asf:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f.write(decrypted_content)

&nbsp; &nbsp;&nbsp;# 3. 计算 MD5
&nbsp; &nbsp;&nbsp;md5_hash=hashlib.md5(decrypted_content).hexdigest()
&nbsp; &nbsp;&nbsp;print(f"MD5:&nbsp;{md5_hash}")

if__name__=="__main__":
&nbsp; &nbsp;&nbsp;decrypt_final()

4. 运行结果

运行脚本后,成功解密出 AES 密钥和 PDF 文件。

  • AES 密钥79d44c189fcba167411543dc258c9a7ebf58daec249c65214560da7fe5f964f1
  • 解密文件 MD552fd2cb4fc43eceb3c79233038bb5f42

答案

52fd2cb4fc43eceb3c79233038bb5f42

Question 3 WriteUp

题目解析

题目要求分析 999 份 PDF 文件,找出所有指向 非 veritytech.com 域名的 URL。需要提取完整 URL,按字典序升序排序,并计算其 32 位小写 MD5 值。

解题步骤

1. 批量解密 PDF

利用 Q2 中获得的 AES 密钥 2024_VerityTech_Sec_Key_#9988,编写脚本 decrypt_all.py 对所有 .enc 文件进行批量解密。

2. 提取 URL

编写脚本 scan_urls_v4.py 从解密后的 PDF 中提取 URL。在提取过程中遇到一个关键问题:PDF 文本提取时,长 URL 可能会被换行符 \n 截断(例如 https://api.veritytech.c\nom)。解决方案:在正则匹配前,先移除文本中的换行符。

3. 过滤与清洗

提取到的 URL 包含大量噪音,主要分为两类:

  1. 合法域名*.veritytech.com,需要排除。
  2. 截断噪音:由于 PDF 排版或提取算法的边缘情况,产生了一些看起来像 URL 但实际上是合法域名截断部分的字符串(例如 https://api 是 https://api.veritytech.com 的前缀)。

过滤逻辑

  • 如果域名以 veritytech.com 结尾,排除。

  • 如果域名没有点号(.),且是 veritytech 或常见子域名(如 apidocs)的前缀,判定为噪音排除。

  • 排除内部基础设施域名

  • 主机名(如 dev-db:3306)。

  • 内部专用顶级域(如 .local.internal)。

  • 保留所有其他非 veritytech.com 的外部域名(疑似钓鱼网站)。

4. 结果汇总

最终筛选出 11 个恶意/钓鱼 URL:

  1. https://account-checker.xyz/login
  2. https://security-review.veritytech-support.info/validate
  3. https://ver1tytech.com/reset
  4. https://verify-account.veritytech-portal.xyz/auth
  5. https://verify-id.top/session
  6. https://verify-veritytech.top/auth
  7. https://veritytech-login-security.net/update
  8. https://veritytech-security-check.xyz/verify
  9. https://veritytech-support-alert.co/update
  10. https://veritytech-update-alert.co/notice
  11. https://veritytech.com.io/login

5. 计算 MD5

将上述 URL 列表用逗号连接(无空格)并计算 MD5。字符串:https://account-checker.xyz/login,https://security-review.veritytech-support.info/validate,https://ver1tytech.com/reset,https://verify-account.veritytech-portal.xyz/auth,https://verify-id.top/session,https://verify-veritytech.top/auth,https://veritytech-login-security.net/update,https://veritytech-security-check.xyz/verify,https://veritytech-support-alert.co/update,https://veritytech-update-alert.co/notice,https://veritytech.com.io/login

MD5: 0a16f1ca5e8db1270074d0d39f6edeaf

最终答案

0a16f1ca5e8db1270074d0d39f6edeaf

4.数据审计

数据审计 – 第一问 Writeup

题目要求

根据题目提供的证书关键参数(params.csv),找到 id 为 285 的参数合成的证书,判断其可以解密哪个流量包(pcap.zip),并将该流量包名称的 MD5 值作为答案提交。

解题步骤

1. 获取关键参数

读取 params.csv 文件,查找 id 为 285 的行,获取 RSA 参数 p 和 q

参数信息:

  • id: 285
  • p: 177264302295959185550899884811457697789837321132319354039496340545988969470422347313577084568610012957139649359576035974322283705879187577664768699213211347033624840318251940972496063336844685896882713624561971974788692556498019960846465311267474369690812099681875735569564330504277517754796899917257323134723
  • q: 143990163909936129648804807321551478733567016733642335522156625973321506509458427490929508866189002322826874210961910641865602374675333206288577734876005828016379170951078934469472423840724164310343028554942665656763806178050620857070820746559094989518930589089970082522430002916286799917078785172333647028571

2. 计算模数 N

RSA 算法中,模数 $N = p \times q$。在 TLS 握手过程中,服务端发送的证书包含了公钥信息,其中就包含模数 $N$。因此,包含该证书的流量包中必然包含 $N$ 的二进制数据。

3. 流量包匹配

解压 pcap.zip 得到大量 pcap 文件。为了快速定位,我们不需要尝试解密所有包,只需搜索哪个 pcap 文件中包含了计算出的 $N$ 的字节序列。

匹配脚本逻辑:

  1. 计算 $N = p \times q$。
  2. 将 $N$ 转换为大端序字节串。
  3. 遍历所有 pcap 文件,读取文件内容。
  4. 检查 $N$ 的字节串是否存在于文件内容中。

4. 匹配结果

经过脚本搜索,发现文件 mAqY0WRHsV.pcap 包含该模数。

5. 生成答案

计算文件名的 MD5 值:

  • 文件名:mAqY0WRHsV.pcap
  • MD5:1f5c34eab5696a300afff7452b7f7e6a

最终答案

1f5c34eab5696a300afff7452b7f7e6a

数据审计 – 第二问 Writeup

题目要求

根据题目对每个流量包进行解密,并得到明文。找到 “王梅” 的手机号,并将其 32 位小写 MD5 值作为答案提交。

解题步骤

1. 批量解密与搜索

由于流量包数量较多,且每个包对应不同的证书,我们编写脚本遍历所有 pcap 文件。对于每个 pcap 文件:

  1. 提取证书 Common Name 中的 ID。
  2. 根据 ID 从 params.csv 获取 $p, q$ 生成私钥。
  3. 使用 tshark 配合生成的私钥解密流量。
  4. 在解密后的数据中搜索 “…” 的相关编码。

2. 编码分析

在 HTTP 流量中,中文字符通常以 Unicode 转义序列的形式存在(如 \u738b\u6885),或者被进一步 Hex 编码。经过分析,我们在流量包 ZQYTMYb2ZY.pcap 中发现了如下 Hex 字符串:7b22757365726e616d65223a20225c75373338625c7536383835222c202270686f6e65223a20223133393330303739333635227d

3. 解码与提取

将上述 Hex 字符串解码为 ASCII 字符串:{"username": "\u738b\u6885", "phone": "13930079365"}

进一步解析 Unicode 转义:{"username": "...", "phone": "139...."}

成功找到 “…” 对应的手机号。

4. 生成答案

计算手机号 的 MD5 值:

  • 手机号:.
  • MD5:5a4df03b5f1aaad3f465fc78a84b7101

最终答案

5a4df03b5f1aaad3f465fc78a84b7101

数据审计 – 第三问 Writeup

题目要求

系统使用 JWT 进行身份认证(Authorization 头部)。为了安全,密钥设置为用户姓名的拼音。题目要求统计有多少个账号进行了越权访问(即 JWT 中的身份信息与请求体中的内容不一致),并将越权账号数量的 MD5 值作为答案提交。

解题步骤

1. 批量解密流量

由于每个流量包对应不同的证书参数(params.csv),我们需要对每个 pcap 文件进行单独处理:

  1. 提取 ID:解析 pcap 文件中的 TLS Certificate 消息,从证书的 Common Name (CN) 字段中提取 ID(格式为 batch_{id}.example.com)。
  2. 生成私钥:根据提取的 ID,从 params.csv 中查找对应的 p 和 q,计算私钥参数并生成 PEM 格式的 RSA 私钥文件。
  3. 解密流量:使用 tshark 配合生成的私钥,解密 TLS 流量,提取 HTTP 请求的 Authorization 头部和请求体(File Data)。

2. 身份核验

对提取到的数据进行分析:

  1. 解析 JWT:从 Authorization 头部提取 Bearer Token,进行 Base64 解码获取 Payload 部分(无需验证签名,只需读取内容)。
  2. 解析请求体:解析 HTTP POST 请求的 JSON 数据。
  3. 对比信息:比较 JWT Payload 中的 username 和 phone 与请求体中的 username 和 phone 是否一致。

3. 统计越权账号

遍历所有 pcap 文件后,发现以下 5 个流量包存在身份不一致的情况(越权访问):

  1. jFC0Edvnas.pcap: 用户 王丽娟
  2. pHpodYofLg.pcap: 用户 赵平
  3. qFX9ydkNoY.pcap: 用户 宋建平
  4. U8LyjJBxvY.pcap: 用户 魏云
  5. WYPfbs24cE.pcap: 用户 蔡旭

共计 5 个账号进行越权。

4. 生成答案

计算数量 5 的 MD5 值:

  • 数字:5
  • MD5(32位小写):e4da3b7fbbce2345d7772b0674a318d5

最终答案

  • e4da3b7fbbce2345d7772b0674a318d5

5.签到

填写调查问卷 flag{2989f956eb5f46ff}


免责声明:

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

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

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

本文转载自:赛查查 《第三届数信杯个人赛第二名WP》

评论:0   参与:  0