boxmoe_header_banner_img

欢迎来到烨的世界~

加载中

文章导读

CISCN线上赛re+crytpto部分WP


avatar
liuye 2025年12月29日 538

简述

较菜,酌情参考

部分题解

ECDSA

操作:

算法:基于ECDSA(椭圆曲线数字签名算法)

task.py: 生成脚本,展示了私钥和签名的生成过程

public.pem: 公钥文件(PEM格式)

signatures.txt: 60个消息及其对应的ECDSA签名

找一下task.py中的核心过程

digest_int = int.from_bytes(sha512(b”Welcome to this challenge!”).digest(), “big”)

curve_order = NIST521p.order

priv_int = digest_int % curve_order

私钥是从固定字符串 “Welcome to this challenge!” 的SHA512哈希值生成的

由此我们可知私钥生成逻辑

写一个python来还原出私钥

脚本

###show_private_key.pyfrom ecdsa import SigningKey, VerifyingKey, NIST521pfrom ecdsa.util import sigdecode_stringfrom hashlib import sha512import binascii# 读取公钥with open(“public.pem.txt”, “r”) as f:    pub_key_pem = f.read().encode() vk = VerifyingKey.from_pem(pub_key_pem)curve_order = NIST521p.order print(f”\n[信息] 曲线: NIST521p”)print(f”[信息] 曲线阶数: {curve_order}”)print(f”[信息] 曲线阶数位数: {curve_order.bit_length()} 位”) # 步骤1: 计算SHA512哈希print(f”\n[步骤1] 计算固定字符串的SHA512哈希…”)message = b”Welcome to this challenge!”print(f”  消息: {message}”)digest = sha512(message).digest()digest_int = int.from_bytes(digest, “big”)print(f”  SHA512摘要 (十六进制): {digest.hex()}”)print(f”  SHA512摘要 (整数): {digest_int}”)print(f”  SHA512摘要位数: {digest_int.bit_length()} 位”) # 步骤2: 计算私钥print(f”\n[步骤2] 计算私钥 (对曲线阶数取模)…”)priv_int = digest_int % curve_orderprint(f”  私钥 (整数): {priv_int}”)print(f”  私钥 (十六进制): {hex(priv_int)}”)print(f”  私钥位数: {priv_int.bit_length()} 位”) # 步骤3: 转换为字节格式print(f”\n[步骤3] 转换为字节格式…”)priv_bytes = priv_int.to_bytes(66, ‘big’)print(f”  私钥 (字节长度): {len(priv_bytes)} 字节”)print(f”  私钥 (字节hex): {priv_bytes.hex()}”) # 步骤4: 创建签名密钥对象print(f”\n[步骤4] 创建签名密钥对象…”)try:    sk = SigningKey.from_string(priv_bytes, curve=NIST521p)    vk_test = sk.verifying_key    print(”  ✓ 签名密钥对象创建成功”)except Exception as e:    print(f”  ✗ 签名密钥对象创建失败: {e}”)    sk = None    vk_test = None # 步骤5: 验证私钥print(f”\n[步骤5] 验证私钥是否正确…”)if sk and vk_test:    if vk_test.to_string() == vk.to_string():        print(”  ✓ 私钥验证成功! 私钥正确!”)                # 步骤6: 输出flag        flag_hex = hex(priv_int)[2:]  # 去掉0x前缀        print(f”\n” + “=” * 80)        print(f”私钥 (十六进制,去掉0x): {flag_hex}”)        print(f”\n” + “=” * 80)        print(f”Flag: flag{{{flag_hex}}}”)        print(“=” * 80)                # 额外验证: 测试签名        print(f”\n[额外验证] 使用恢复的私钥进行签名测试…”)        test_msg = b”test message”        test_sig = sk.sign(test_msg)        try:            vk.verify(test_sig, test_msg)            print(”  ✓ 签名验证成功,进一步确认私钥正确”)        except Exception as e:            print(f”  ⚠ 签名验证失败: {e}”)    else:        print(”  ✗ 私钥验证失败! 生成的公钥与提供的公钥不匹配”)        print(”  尝试方法2: 从签名恢复私钥…”)        method1_success = Falseelse:    print(”  ✗ 无法创建签名密钥对象”)    method1_success = False 
这里输出的是私钥 私钥 (整数): 5848022551640848821625446477716994866936098118919819708066070751486112077287088053042703802506281474548029927685263069362024455626067783968769070994600931326 但是会看题目发现要交的不是私钥是私钥的私钥的MD5值   

flag值:

flag{581bdf717b780c3cd8282e5a4d50f3a0}

wasm-login

操作内容:

ndex.html 的登录逻辑:

调用 WASM 中的 authenticate 函数

const authResult = authenticate(username, password);

const authData = JSON.parse(authResult);

模拟发送到服务器

console.log(‘发送到服务器的数据:’, authData);

模拟服务器响应

simulateServerRequest(authData)

校验函数simulateServerRequest 函数

const check = CryptoJS.MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex);

if (check.startsWith(“ccaf33e3512e31f3”)){

resolve({ success: true });

}else{

resolve({ success: false });

}

服务器对 authenticate 返回的 JSON 数据进行 MD5 哈希

如果 MD5 值以 ccaf33e3512e31f3 开头,则登录成功

Flag 格式:flag{完整的32位MD5值}

WASM 逻辑还原:

authenticate 函数流程:

1.密码 Base64 编码

使用自定义 Base64 字母表对 password 进行编码

字母表:NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO

填充字符:=

这里写一个base64自定义表脚本即可

2.时间戳获取

timestamp = Date.now().toString()

时间戳作为 HMAC 的密钥

推测成功登录时间戳在 2025-12-22 00:29 附近

2025/12/21 是第三个周末的周日,凌晨 0:xx 已是 22 号(周一)

并且MD5hash:以 `ccaf33e3512e31f3` 开头

这里写一个爆破脚本使hash值符合以 `ccaf33e3512e31f3` 开头并且时间在 2025-12-22 00:29 附近即可

2.构建消息

message = {“username”:…, “password”:<b64>}

其中 <b64> 是经过自定义 Base64 编码的密码

4.计算签名

signature = hmacSHA256(secret=timestamp, message)

使用”怪异”的 HMAC 实现

签名结果同样使用自定义 Base64 编码

这里怪异HMAC在下方有解释

5.返回结果

{“username”:…, “password”:<b64>, “signature”:<b64>}

Base64编码换标编码即可(代码在代码区)

时间戳爆破

怪异 HMAC-SHA256 实现:

ipad = key ^ 0x76 (重复 0x76)

opad = key ^ 0x3C (重复 0x3C)

脚本

 Base编码CUSTOM_BASE64_ALPHABET = “NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO”PADDING = ‘=’ def custom_base64_encode(data: bytes) -> str:    “””自定义 Base64 编码”””    if len(data) == 0:        return “”        # 将字节转换为二进制字符串    binary = ”.join(format(b, ’08b’) for b in data)        # 填充到 6 的倍数    padding_bits = (6 – len(binary) % 6) % 6    binary += ‘0’ * padding_bits        # 每 6 位转换为一个字符    result = []    for i in range(0, len(binary), 6):        index = int(binary[i:i+6], 2)        result.append(CUSTOM_BASE64_ALPHABET[index])        # 添加填充    padding_chars = len(data) % 3    if padding_chars == 1:        result.append(PADDING)        result.append(PADDING)    elif padding_chars == 2:        result.append(PADDING)    return ”.join(result)   时间戳爆破#!/usr/bin/env python3# -*- coding: utf-8 -*-“””仅用于爆破时间戳的脚本在 00:29:08 ±2 分钟窗口内枚举毫秒时间戳,寻找满足 MD5 前缀条件的时间戳””” import jsonimport hashlibfrom datetime import datetime, timezone, timedelta # 自定义 Base64 字母表CUSTOM_BASE64_ALPHABET = “NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO”PADDING = ‘=’ def custom_base64_encode(data: bytes) -> str:    “””自定义 Base64 编码”””    if len(data) == 0:        return “”    binary = ”.join(format(b, ’08b’) for b in data)    padding_bits = (6 – len(binary) % 6) % 6    binary += ‘0’ * padding_bits    result = []    for i in range(0, len(binary), 6):        index = int(binary[i:i+6], 2)        result.append(CUSTOM_BASE64_ALPHABET[index])    padding_chars = len(data) % 3    if padding_chars == 1:        result.append(PADDING)        result.append(PADDING)    elif padding_chars == 2:        result.append(PADDING)    return ”.join(result) def custom_hmac_sha256(secret: str, message: str) -> bytes:    “””怪异的 HMAC-SHA256 实现”””    key_bytes = secret.encode(‘utf-8′)    if len(key_bytes) > 64:        key_bytes = hashlib.sha256(key_bytes).digest()    if len(key_bytes) < 64:        key_bytes = key_bytes + b’\x00’ * (64 – len(key_bytes))    ipad = bytes(b ^ 0x76 for b in key_bytes)    opad = bytes(b ^ 0x3C for b in key_bytes)    message_bytes = message.encode(‘utf-8’)    inner = hashlib.sha256(ipad + message_bytes).digest()    outer = hashlib.sha256(inner + opad).digest()    return outer def authenticate(username: str, password: str, timestamp: int) -> str:    “””复现 authenticate 函数逻辑”””    password_b64 = custom_base64_encode(password.encode(‘utf-8’))    timestamp_str = str(timestamp)    message = json.dumps({“username”: username, “password”: password_b64}, separators=(‘,’, ‘:’))    signature_bytes = custom_hmac_sha256(timestamp_str, message)    signature_b64 = custom_base64_encode(signature_bytes)    result = {        “username”: username,        “password”: password_b64,        “signature”: signature_b64    }    return json.dumps(result, separators=(‘,’, ‘:’)) def brute_force_timestamp():    “””爆破时间戳:在 00:29:08 ±2 分钟窗口内枚举毫秒时间戳”””    username = “admin”    password = “admin”    target_prefix = “ccaf33e3512e31f3”        # 时间戳范围:00:29:08 ±2 分钟    base_time = datetime(2025, 12, 22, 0, 29, 8, 0, tzinfo=timezone(timedelta(hours=8)))    base_timestamp = int(base_time.timestamp() * 1000)    window_ms = 2 * 60 * 1000  # ±2 分钟 = 120000 毫秒    start_timestamp = base_timestamp – window_ms    end_timestamp = base_timestamp + window_ms        # 枚举所有毫秒时间戳    for timestamp in range(start_timestamp, end_timestamp + 1):        try:            # 调用 authenticate            auth_result = authenticate(username, password, timestamp)                        # 计算 MD5            check = hashlib.md5(auth_result.encode(‘utf-8’)).hexdigest()                        # 检查前缀            if check.startswith(target_prefix):                return timestamp        except Exception:            continue        return None if __name__ == “__main__”:    result = brute_force_timestamp()    if result:        print(result)    else:        print(“未找到匹配的时间戳”) 脚本成功找到时间戳:1766334550699 
def custom_hmac_sha256(secret: str, message: str) -> bytes:    “””    怪异的 HMAC-SHA256 实现    – key 填充/哈希到 64 字节    – ipad = key ^ 0x76    – opad = key ^ 0x3C    – inner = sha256(ipad || message)    – outer = sha256(inner || opad)    “””    # 将 secret 转换为字节    key_bytes = secret.encode(‘utf-8′)        # key 填充/哈希到 64 字节    if len(key_bytes) > 64:        # 如果 key 超过 64 字节,先哈希        key_bytes = hashlib.sha256(key_bytes).digest()        # 填充到 64 字节    if len(key_bytes) < 64:        key_bytes = key_bytes + b’\x00’ * (64 – len(key_bytes))        # ipad = key ^ 0x76    ipad = bytes(b ^ 0x76 for b in key_bytes)        # opad = key ^ 0x3C    opad = bytes(b ^ 0x3C for b in key_bytes)        # inner = sha256(ipad || message)    message_bytes = message.encode(‘utf-8’)    inner = hashlib.sha256(ipad + message_bytes).digest()        # outer = sha256(inner || opad)    outer = hashlib.sha256(inner + opad).digest()    return outer  
汇总#!/usr/bin/env python3# -*- coding: utf-8 -*-“””爆破时间戳脚本 – 展示如何找到正确的时间戳在 00:29:08 ±2 分钟窗口内枚举毫秒时间戳,计算 MD5(final_json),寻找前缀 ccaf33e3512e31f3″”” import jsonimport hashlibfrom datetime import datetime, timezone, timedelta # 自定义 Base64 字母表CUSTOM_BASE64_ALPHABET = “NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO”PADDING = ‘=’ def custom_base64_encode(data: bytes) -> str:    “””自定义 Base64 编码”””    if len(data) == 0:        return “”        binary = ”.join(format(b, ’08b’) for b in data)    padding_bits = (6 – len(binary) % 6) % 6    binary += ‘0’ * padding_bits        result = []    for i in range(0, len(binary), 6):        index = int(binary[i:i+6], 2)        result.append(CUSTOM_BASE64_ALPHABET[index])        padding_chars = len(data) % 3    if padding_chars == 1:        result.append(PADDING)        result.append(PADDING)    elif padding_chars == 2:        result.append(PADDING)        return ”.join(result) def custom_hmac_sha256(secret: str, message: str) -> bytes:    “””    怪异的 HMAC-SHA256 实现    – key 填充/哈希到 64 字节    – ipad = key ^ 0x76    – opad = key ^ 0x3C    – inner = sha256(ipad || message)    – outer = sha256(inner || opad)    “””    key_bytes = secret.encode(‘utf-8′)        if len(key_bytes) > 64:        key_bytes = hashlib.sha256(key_bytes).digest()        if len(key_bytes) < 64:        key_bytes = key_bytes + b’\x00’ * (64 – len(key_bytes))        ipad = bytes(b ^ 0x76 for b in key_bytes)    opad = bytes(b ^ 0x3C for b in key_bytes)        message_bytes = message.encode(‘utf-8’)    inner = hashlib.sha256(ipad + message_bytes).digest()    outer = hashlib.sha256(inner + opad).digest()        return outer def authenticate(username: str, password: str, timestamp: int) -> str:    “””复现 authenticate 函数逻辑”””    password_b64 = custom_base64_encode(password.encode(‘utf-8’))    timestamp_str = str(timestamp)    message = json.dumps({“username”: username, “password”: password_b64}, separators=(‘,’, ‘:’))    signature_bytes = custom_hmac_sha256(timestamp_str, message)    signature_b64 = custom_base64_encode(signature_bytes)        result = {        “username”: username,        “password”: password_b64,        “signature”: signature_b64    }        return json.dumps(result, separators=(‘,’, ‘:’)) def brute_force_timestamp(show_details=False, show_progress=True):    “””    爆破时间戳    在 00:29:08 ±2 分钟窗口内枚举毫秒时间戳    “””    print(“=” * 80)    print(“时间戳爆破脚本”)    print(“=” * 80)        # 参数设置    username = “admin”    password = “admin”    target_prefix = “ccaf33e3512e31f3″            # 方法1: 使用已知的时间戳作为基准    # 已知: 时间戳 1766334550699 对应 2025-12-22 00:29:10.699 北京时间    # 北京时间 2025-12-22 00:29:08 = UTC 2025-12-21 16:29:08    # UTC 时间戳 = 1766334550699 – (10.699 – 8) * 1000 = 1766334550699 – 2699 = 1766334548000        # 更准确的方法:直接构造时间    # 2025-12-22 00:29:08 北京时间    base_time_beijing = datetime(2025, 12, 22, 0, 29, 8, 0, tzinfo=timezone(timedelta(hours=8)))    base_timestamp = int(base_time_beijing.timestamp() * 1000)        # ±2 分钟 = ±120 秒 = ±120000 毫秒    window_ms = 2 * 60 * 1000  # 2分钟 = 120000毫秒    start_timestamp = base_timestamp – window_ms    end_timestamp = base_timestamp + window_ms        print(f”\n爆破参数:”)    print(f”  用户名: {username}”)    print(f”  密码: {password}”)    print(f”  目标 MD5 前缀: {target_prefix}”)    print(f”\n时间戳范围:”)    print(f”  基准时间: {base_time_beijing.strftime(‘%Y-%m-%d %H:%M:%S’)} (北京时间)”)    print(f”  基准时间戳: {base_timestamp}”)    print(f”  搜索窗口: ±2 分钟 (±{window_ms} 毫秒)”)    print(f”  开始时间戳: {start_timestamp}”)    print(f”  结束时间戳: {end_timestamp}”)        start_time = datetime.fromtimestamp(start_timestamp / 1000, tz=timezone(timedelta(hours=8)))    end_time = datetime.fromtimestamp(end_timestamp / 1000, tz=timezone(timedelta(hours=8)))    print(f”  开始时间: {start_time.strftime(‘%Y-%m-%d %H:%M:%S.%f’)[:-3]} (北京时间)”)    print(f”  结束时间: {end_time.strftime(‘%Y-%m-%d %H:%M:%S.%f’)[:-3]} (北京时间)”)        total_timestamps = end_timestamp – start_timestamp + 1    print(f”  总时间戳数: {total_timestamps:,} 个”)        print(f”\n开始爆破…”)    print(“-” * 80)        found = False    tested_count = 0    last_progress_time = start_timestamp        # 枚举所有毫秒时间戳    for timestamp in range(start_timestamp, end_timestamp + 1):        tested_count += 1                try:            # 调用 authenticate            auth_result = authenticate(username, password, timestamp)                        # 计算 MD5            check = hashlib.md5(auth_result.encode(‘utf-8’)).hexdigest()                        # 显示详细信息(可选)            if show_details and tested_count <= 5:                current_time = datetime.fromtimestamp(timestamp / 1000, tz=timezone(timedelta(hours=8)))                print(f”\n[测试 {tested_count}]”)                print(f”  时间戳: {timestamp}”)                print(f”  时间: {current_time.strftime(‘%Y-%m-%d %H:%M:%S.%f’)[:-3]} (北京时间)”)                print(f”  authenticate 返回: {auth_result}”)                print(f”  MD5: {check}”)                print(f”  前缀匹配: {check.startswith(target_prefix)}”)                        # 检查前缀            if check.startswith(target_prefix):                current_time = datetime.fromtimestamp(timestamp / 1000, tz=timezone(timedelta(hours=8)))                                print(“\n” + “=” * 80)                print(“找到匹配!”)                print(“=” * 80)                print(f”\n时间戳: {timestamp}”)                print(f”时间: {current_time.strftime(‘%Y-%m-%d %H:%M:%S.%f’)[:-3]} (北京时间)”)                print(f”UTC 时间: {datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc).strftime(‘%Y-%m-%d %H:%M:%S.%f’)[:-3]}”)                                print(f”\nauthenticate 返回的 JSON:”)                print(f”  {auth_result}”)                                # 解析 JSON 显示详细信息                auth_data = json.loads(auth_result)                print(f”\nJSON 解析:”)                print(f”  username: {auth_data[‘username’]}”)                print(f”  password (Base64): {auth_data[‘password’]}”)                print(f”  signature (Base64): {auth_data[‘signature’]}”)                                print(f”\nMD5 哈希:”)                print(f”  完整值: {check}”)                print(f”  前缀: {check[:16]} (匹配目标: {target_prefix})”)                print(f”  后缀: {check[16:]}”)                                print(f”\n爆破统计:”)                print(f”  测试时间戳数: {tested_count:,}”)                print(f”  搜索范围: {total_timestamps:,} 个时间戳”)                print(f”  命中率: {1/total_timestamps*100:.6f}%”)                                print(f”\nFlag: flag{{{check}}}”)                print(“=” * 80)                                found = True                return timestamp, check                        except Exception as e:            if show_details:                print(f”错误 (时间戳 {timestamp}): {e}”)            continue                # 显示进度(每1000个时间戳或每10秒)        if show_progress:            if tested_count % 1000 == 0 or (timestamp – last_progress_time) >= 10000:                current_time = datetime.fromtimestamp(timestamp / 1000, tz=timezone(timedelta(hours=8)))                progress = (tested_count / total_timestamps) * 100                print(f”进度: {progress:.2f}% | 时间: {current_time.strftime(‘%H:%M:%S.%f’)[:-3]} | 已测试: {tested_count:,}/{total_timestamps:,}”, end=’\r’)                last_progress_time = timestamp        if not found:        print(f”\n未找到匹配的时间戳”)        print(f”共测试了 {tested_count:,} 个时间戳”)        return None, None if __name__ == “__main__”:    import sys        # 解析命令行参数    show_details = “–details” in sys.argv or “-d” in sys.argv    show_progress = “–no-progress” not in sys.argv        print(“\n提示: 使用 –details 或 -d 参数可以显示前5个时间戳的详细信息”)    print(“=” * 80 + “\n”)        timestamp, check = brute_force_timestamp(show_details=show_details, show_progress=show_progress)        if timestamp:        print(f”\n成功找到时间戳: {timestamp}”)        print(f”对应的 Flag: flag{{{check}}}”)    else:        print(“\n未找到匹配的时间戳”)  

flag值

flag{ccaf33e3512e31f36228f0b97ccbc8f1}

EzFlag

操作内容:

提示输入: 输出 “Enter password: “

读取输入: 使用 std::getline 从标准输入读取密码

密码比较: 与硬编码密码 “V3ryStr0ngp@ssw0rd” 进行比较

误处理: 密码错误时输出 “Wrong password!” 并退出

往下看

跟进f

交叉引用这里看到了

K=std::string::basic_string(&K”012ab9c3478d56ef”,&v1);

写一个python脚本还原flag

脚本

K = “012ab9c3478d56ef”V5 = [0, 1, 1, 2, 3, 5, 8, 13, 5, 2, 7, 9, 0, 9, 9, 2, 11, 13, 8, 5, 13, 2, 15, 1]
v11 = 1
out = [K[V5[(v11 := v11 * 8 + i + 64) % 24]]
for i in range(32)]
print(f”flag{{{‘-‘.join([”.join(out[s:e]) for s, e in [(0,8), (8,13), (13,18), (18,23), (23,32)]])}}}”)

flag值

flag{06326741-d2190-9f291-47a27-606326741}

babygame

操作内容:

先用工具解包得到

然后启动游戏引擎分析源码

基础密钥static var key = “FanAglFanAglOoO!”

密文    if encrypted.hex_encode() == “d458af702a680ae4d089ce32fc39945d”:

加密方式:aes-ebc

但是解密发现不对

仔细搜索相关发现

这里有个密钥会变换

所以重新写一个脚本

脚本

from Crypto.Cipher import AESfrom binascii import unhexlify k = “FanBglFanBglOoO!”ct = unhexlify(“d458af702a680ae4d089ce32fc39945d”) pt = AES.new(k.encode(), AES.MODE_ECB).decrypt(ct)print(f”{k}\n{pt.hex()}\n{pt.decode(‘utf-8′, errors=’ignore’)}”) 

flag值

flag{wOW~youAregrEaT!}

Eternum

操作内容:

首先查壳有upx先脱壳子

我一般习惯用工具脱就没用upx -d

然后脱壳完

这是一个使用 protobuf 协议的网络通信程序,从 `run.sh` 可以看出程序需要连接到服务器:

kworker 192.168.8.160:13337

程序使用自定义加密协议,不是 TLS:

具体格式

帧格式:

8 字节魔数: ET3RNUMX

4 字节长度: 大端序,表示 payload 长度

payload: 加密数据

Payload 格式(AES-256-GCM):

nonce(12 字节) || ciphertext+tag

加密参数:

– 算法:AES-256-GCM

– 密钥:32 字节可打印字符串(在二进制文件中)

– Nonce:payload 前 12 字节

– AAD:空

Protobuf 协议定义

发现了完整的 protobuf 协议定义:

– 包名Eternum/etop.proto

– 消息类型:

– CommandRequest/ CommandResponse – 命令执行

– FileUploadRequest / FileUploadResponse – 文件上传

– HeartbeatRequest/ HeartbeatResponse – 心跳包

解密后的 payload 包含可读字符串:`COMMAND_REQUEST`、`whoami`、`id`、`ls` 等

函数相关

– main.main – 主函数

– main.(*kqdMQIvkNC).oaj_YwiXiz

– main.(*pvDCtJ).cSteTzwwEkcU

– main.yKp7OA13e

– main.iBLLOwr3

– main.(*pvDCtJ).Replace

– iupHvc2q4.(*H1eV17y).Connect – 连接函数

j7nnctf.TjP9j4 – 包含 逻辑

还有些网络相关函数就不再单独截图

iupHvc2q4.(*H1eV17y).Connect TCP 连接

rNBqGdO.TCPAJNJ TCP 相关类型

sTubFdE5H包中的多个 MultipathTCP 函数

分析能明白Flag 隐藏在解密后的网络流量中

这里很巧的是

– 服务器执行命令:`base32 /var/opt/s*/` 读取机密文件

– 客户端返回的响应中包含 base32 编码的数据

Base32 字符串看起来 padding 不合法(长度不是 8 的倍数)

关键:去掉第一个字符 ‘I’ 后,前缀变成 `MZWGCZ33`,正好是 `”flag{“` 的 base32 编码

对去掉第一个字符后的字符串进行 base32 解码即可得到 flag

所以去壳后直接

提取帧:从 tcp.pcap`中提取 ET3RNUMX 帧:

1. 解析 pcap 文件格式

2. 提取 TCP payload

3. 按连接重组数据流

4. 查找 `ET3RNUMX` 魔数

5. 提取帧:8 字节魔数 + 4 字节长度 + payload

提取 AES 密钥

1. 在二进制文件中搜索 32 字节的可打印字符串(正则:`[A-Za-z0-9]{32}`)

2. 使用第一个帧验证密钥:

– 提取 nonce(前 12 字节)

– 提取密文(剩余部分)

– 尝试解密

– 验证解密结果包含 `COMMAND`、`REQUEST`、`RESPONSE` 等字符串

解密所有帧

使用找到的密钥对所有帧进行 AES-256-GCM 解密:

– Nonce:每个帧的前 12 字节

– 密文:剩余部分

– AAD:空

在解密后的数据中查找 base32 编码的字符串并解码即可得到flag

脚本

import base64import reimport structfrom typing import Dict, List, Tuple try:    from cryptography.hazmat.primitives.ciphers.aead import AESGCM    CRYPTO_AVAILABLE = Trueexcept ImportError:    CRYPTO_AVAILABLE = False    print(“错误: 需要安装 cryptography: pip install cryptography”)    exit(1) MAGIC = b”ET3RNUMX”  def parse_pcap_frames(pcap_path: str) -> List[bytes]:    “””从 pcap 文件中解析出 ET3RNUMX 帧”””    data = open(pcap_path, “rb”).read()    if len(data) < 24:        raise ValueError(“bad pcap”)     # pcap global header (little-endian in this challenge)    off = 24    frames: List[bytes] = []    buffers: Dict[Tuple[str, int, str, int], bytearray] = {}     def ip_str(b: bytes) -> str:        return “.”.join(str(x) for x in b)     while off + 16 <= len(data):        ts_sec, ts_usec, incl_len, orig_len = struct.unpack_from(“<IIII”, data, off)        off += 16        if incl_len == 0:            continue        if off + incl_len > len(data):            break         pkt = data[off:off + incl_len]        off += incl_len         # Ethernet        if len(pkt) < 14:            continue        eth_type = struct.unpack(“>H”, pkt[12:14])[0]        if eth_type != 0x0800:            continue         # IPv4        if len(pkt) < 14 + 20:            continue        ip = pkt[14:]        ver_ihl = ip[0]        version = ver_ihl >> 4        ihl = (ver_ihl & 0x0F) * 4        if version != 4 or len(ip) < ihl:            continue        proto = ip[9]        if proto != 6:  # TCP            continue        src = ip_str(ip[12:16])        dst = ip_str(ip[16:20])         # TCP        tcp = ip[ihl:]        if len(tcp) < 20:            continue        sport, dport = struct.unpack(“>HH”, tcp[:4])        data_offset = (tcp[12] >> 4) * 4        if len(tcp) < data_offset:            continue        payload = tcp[data_offset:]        if not payload:            continue         key = (src, sport, dst, dport)        buf = buffers.setdefault(key, bytearray())        buf.extend(payload)         # frame decode from buffer        while True:            idx = buf.find(MAGIC)            if idx == -1:                if len(buf) > 7:                    del buf[:-7]  # keep last 7 bytes for next search                break            if idx > 0:                del buf[:idx]  # remove data before magic            if len(buf) < 12:                break            length = struct.unpack(“!I”, buf[8:12])[0]            if len(buf) < 12 + length:                break            frame = bytes(buf[12:12 + length])            del buf[:12 + length]            frames.append(frame)     return frames  def extract_aes_key_from_binary(bin_path: str, first_frame: bytes) -> bytes:    “””从二进制文件中提取 AES-256 密钥”””    blob = open(bin_path, “rb”).read()     # 查找 32 字节的可打印字符串作为候选密钥    cands = set(re.findall(rb”[A-Za-z0-9]{32}”, blob))    if not cands:        raise ValueError(“no key candidates found”)     print(f”找到 {len(cands)} 个 32 字节密钥候选”)        nonce = first_frame[:12]    ct = first_frame[12:]     for k in cands:        try:            aes = AESGCM(k)            pt = aes.decrypt(nonce, ct, b””)        except Exception:            continue        # 验证:解密后的 protobuf 通常包含这些标记        if b”COMMAND” in pt or b”viper” in pt or b”REQUEST” in pt or b”RESPONSE” in pt:            print(f”找到正确密钥: {k.decode(‘ascii’, errors=’ignore’)}”)            return k     raise ValueError(“key not found”)  def find_flag_in_plaintexts(pts: List[bytes]) -> str:    “””在解密后的明文数据中查找 flag”””    # 查找 base32 编码的片段    for pt in pts:        # 查找 base32 格式的字符串(A-Z2-7=)        for frag in re.findall(rb”[A-Z2-7=]{20,}”, pt):            s = frag.decode(“ascii”, errors=”ignore”)            # 尝试偏移 0..7 个字符来修复对齐问题            for shift in range(0, 8):                t = s[shift:]                try:                    dec = base64.b32decode(t)                    # 查找 flag 格式                    m = re.search(rb”flag\{[^}]+\}”, dec)                    if m:                        return m.group(0).decode(“ascii”)                except Exception:                    continue    raise ValueError(“flag not found”)  def main() -> None:    pcap_path = “tcp.pcap”    bin_path = “kworker”  # 可以使用去壳后的文件     print(“=== 步骤 1: 解析 pcap 文件 ===”)    frames = parse_pcap_frames(pcap_path)    if not frames:        raise ValueError(“no frames parsed”)    print(f”解析到 {len(frames)} 个帧”)     print(“\n=== 步骤 2: 提取 AES 密钥 ===”)    key = extract_aes_key_from_binary(bin_path, frames[0])    aes = AESGCM(key)     print(“\n=== 步骤 3: 解密所有帧 ===”)    pts: List[bytes] = []    for i, fr in enumerate(frames):        if len(fr) < 12:            continue        nonce = fr[:12]        ct = fr[12:]        try:            pt = aes.decrypt(nonce, ct, b””)            pts.append(pt)            # 显示前几个解密后的数据(用于调试)            if i < 3:                print(f”帧 {i} 解密后(前100字节): {pt[:100]}”)        except Exception as e:            print(f”帧 {i} 解密失败: {e}”)     print(“\n=== 步骤 4: 查找 flag ===”)    flag = find_flag_in_plaintexts(pts)    print(f”\n找到 flag: {flag}”)  if __name__ == “__main__”:    main()

flag值

flag{b7c58700-2b01-4dd4-8526-a4a47a65a1a9}



评论(0)

查看评论列表

暂无评论


发表评论