AI解2026年Solar应急响应公益月赛-3月赛两道题

第一题 Tunnel Traffic

WireGuard 流量解密

我给的提示词:

请分析文件夹中的file.pcapng流量包中的WireGuard流量,可以memdump.lime获取用于解密的信息,最后拿到flag

1. 题目目标

分析当前目录中的 file.pcapng(流量包)与 memdump.lime(内存镜像),提取可用于 WireGuard 解密的信息,最终拿到 flag。


2. 样本与环境

当前目录关键文件:

  • file.pcapng
  • memdump.lime

工具环境(实测):

  • tshark 4.6.3
  • python(含 cryptography 库)
  • tar(Windows 内置 bsdtar)
  • PowerShell

3. 整体思路

  1. pcapng 里先确认 WireGuard 会话与握手信息,拿到接收端公钥(wg.receiver_pubkey)。
  2. memdump.lime 中提取可能的 X25519 私钥,筛选出公钥能匹配握手公钥的私钥。
  3. 生成 WireGuard keylog 文件,验证私钥确实对应抓包中的两个 WireGuard 会话。
  4. 从解密后的内层 HTTP 数据里重组上传文件 backup.tar.gz
  5. 解包后读取敏感文件,获取真实 flag。

4. 第一步:识别 WireGuard 流量

4.1 看 UDP 会话,定位可疑隧道端口

powershell 复制代码
& 'C:\Program Files\Wireshark\tshark.exe' -r .\file.pcapng -q -z conv,udp

可观察到典型 WireGuard 端口会话(例如 192.168.1.100 <-> 10.10.0.1:51820),以及伪装在 443/udp 的可疑会话(203.0.113.50 <-> 198.51.100.10:443)。

4.2 强制按 WireGuard 解码并查看消息类型

powershell 复制代码
& 'C:\Program Files\Wireshark\tshark.exe' `
  -r .\file.pcapng `
  -d udp.port==51820,wg `
  -Y "wg" `
  -T fields -e frame.number -e ip.src -e ip.dst -e wg.type

wg.type 含义:

  • 1 = Handshake Initiation
  • 2 = Handshake Response
  • 4 = Transport Data

5. 第二步:从握手提取目标公钥

从握手 initiation 包中提取 wg.receiver_pubkey(接收端静态公钥):

powershell 复制代码
& 'C:\Program Files\Wireshark\tshark.exe' `
  -r .\file.pcapng `
  -d udp.port==51820,wg `
  -Y "wg.type==1" `
  -T fields -e frame.number -e ip.src -e ip.dst -e wg.receiver_pubkey

得到两个关键公钥(后续用于匹配内存里的私钥):

  • YYr6pRKCo/6rb6LaFxdHlhhSZQ8dOGZpk0tLjUkHbG4=
  • fz+M3RfABjYGC25QMMdInlixOPbcC9Lpy0ZxdjPwtGA=

6. 第三步:从 memdump.lime 扫描并匹配 WireGuard 私钥

下面脚本思路:

  1. 遍历内存中所有长度 32 字节窗口;
  2. 用 X25519 私钥 clamp 规则进行快速过滤;
  3. 将候选私钥推导公钥;
  4. 与抓包中的两个 wg.receiver_pubkey 做匹配;
  5. 输出命中的私钥与偏移。

6.1 Python 脚本(可直接复制执行)

python 复制代码
import base64
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives import serialization

targets = {
    base64.b64decode("YYr6pRKCo/6rb6LaFxdHlhhSZQ8dOGZpk0tLjUkHbG4="),
    base64.b64decode("fz+M3RfABjYGC25QMMdInlixOPbcC9Lpy0ZxdjPwtGA="),
}

data = Path("memdump.lime").read_bytes()
L = len(data)
found = []
checked = 0

for i in range(0, L - 32):
    b0 = data[i]
    b31 = data[i + 31]
    # X25519 clamped private key condition:
    # b0 & 7 == 0
    # b31 & 128 == 0
    # b31 & 64  == 64
    if (b0 & 0x07) != 0:
        continue
    if (b31 & 0x80) != 0:
        continue
    if (b31 & 0x40) == 0:
        continue

    sk_bytes = data[i:i+32]
    checked += 1
    try:
        sk = x25519.X25519PrivateKey.from_private_bytes(sk_bytes)
        pk = sk.public_key().public_bytes(
            encoding=serialization.Encoding.Raw,
            format=serialization.PublicFormat.Raw
        )
    except Exception:
        continue

    if pk in targets:
        found.append((
            i,
            base64.b64encode(sk_bytes).decode(),
            base64.b64encode(pk).decode()
        ))

print("candidate_checked=", checked)
print("found=", len(found))
for off, sk, pk in found:
    print(off, sk, "=>", pk)

6.2 运行命令

powershell 复制代码
@'
<把上面的 Python 代码粘贴到这里>
'@ | python -

6.3 期望结果

命中 2 把私钥(示例结果):

  • 偏移约 42520246BsXEpwOw1tzUl5nIXsPpsQsCXt27QKvqVlbmQ8qH2E=
  • 偏移约 4264069GOgd5lIim6WkWh6tj3XDzMtlgdIueC4IMV7i2rKM4mo=

7. 第四步:验证私钥与公钥对应关系

python 复制代码
from base64 import b64decode, b64encode
from cryptography.hazmat.primitives.asymmetric import x25519

keys = [
    "6BsXEpwOw1tzUl5nIXsPpsQsCXt27QKvqVlbmQ8qH2E=",
    "GOgd5lIim6WkWh6tj3XDzMtlgdIueC4IMV7i2rKM4mo=",
]

for k in keys:
    sk = x25519.X25519PrivateKey.from_private_bytes(b64decode(k))
    pk = sk.public_key().public_bytes_raw()
    print(k, "->", b64encode(pk).decode())

输出应分别对应:

  • YYr6pRKCo/6rb6LaFxdHlhhSZQ8dOGZpk0tLjUkHbG4=
  • fz+M3RfABjYGC25QMMdInlixOPbcC9Lpy0ZxdjPwtGA=

这一步证明了从内存提取到的私钥就是 WireGuard 会话私钥。


8. 第五步:生成 WireGuard keylog 并让 tshark 识别

创建 wg_keys.log

powershell 复制代码
Set-Content .\wg_keys.log @"
LOCAL_STATIC_PRIVATE_KEY = 6BsXEpwOw1tzUl5nIXsPpsQsCXt27QKvqVlbmQ8qH2E=
LOCAL_STATIC_PRIVATE_KEY = GOgd5lIim6WkWh6tj3XDzMtlgdIueC4IMV7i2rKM4mo=
"@

验证 tshark 已认出对应私钥(known_privkey=True):

powershell 复制代码
& 'C:\Program Files\Wireshark\tshark.exe' `
  -r .\file.pcapng `
  -d udp.port==51820,wg `
  -o "wg.keylog_file:.\wg_keys.log" `
  -Y "frame.number==20 || frame.number==69" `
  -T fields -e frame.number -e wg.receiver_pubkey -e wg.receiver_pubkey.known_privkey

说明:

  • tshark 4.6.3 上会遇到 WireGuard dissector 的 assertion warning(已知兼容性问题),但不影响本题密钥匹配与后续证据链结论。

9. 第六步:从内层 HTTP 请求重组上传文件

在本题中,已可得到解密后的内层客户端请求数据(inner_c2s_reassembled.bin),内容是一个 multipart/form-data 的 POST /upload,其中包含 backup.tar.gz

下面脚本用于从 HTTP multipart 中提取上传文件:

python 复制代码
from pathlib import Path
import re

raw = Path("inner_c2s_reassembled.bin").read_bytes()
sep = b"\r\n\r\n"
idx = raw.find(sep)
if idx < 0:
    raise SystemExit("No HTTP header/body separator found")

headers = raw[:idx].decode("latin1", errors="replace")
body = raw[idx + 4:]

m = re.search(r"boundary=([^\r\n;]+)", headers)
if not m:
    raise SystemExit("No boundary found")
boundary = m.group(1).encode()

start_marker = b"--" + boundary
parts = body.split(start_marker)
file_bytes = None

for p in parts:
    if b'filename="backup.tar.gz"' in p:
        p2 = p.lstrip(b"\r\n")
        i = p2.find(b"\r\n\r\n")
        if i < 0:
            continue
        data = p2[i + 4:]
        data = data.rstrip(b"\r\n")
        if data.endswith(b"--"):
            data = data[:-2].rstrip(b"\r\n")
        file_bytes = data
        break

if file_bytes is None:
    raise SystemExit("backup.tar.gz not found in multipart")

Path("recovered_from_inner_c2s_backup.tar.gz").write_bytes(file_bytes)
print("Recovered bytes:", len(file_bytes))

运行后得到:

  • recovered_from_inner_c2s_backup.tar.gz

10. 第七步:解包并提取 flag

10.1 解压并查看内容

powershell 复制代码
New-Item -ItemType Directory -Path .\tmp_recovered -Force | Out-Null
tar -xzf .\recovered_from_inner_c2s_backup.tar.gz -C .\tmp_recovered
Get-ChildItem -Recurse .\tmp_recovered

关键文件:

  • tmp_recovered\backup\.config\credentials.json
  • tmp_recovered\backup\README.md

10.2 读取潜在 flag

powershell 复制代码
Get-Content -Raw .\tmp_recovered\backup\.config\credentials.json
Get-Content -Raw .\tmp_recovered\backup\README.md

可见:

  • credentials.json 中 token 为:flag{ba00e1df-c9f3-4ac7-8cf5-a61b5936ce18}
  • README.md 中为:falg{a6bd8da6143c3f161d323f6415ba322}(拼写错误,疑似干扰项)

11. 结论与最终答案

真实 flag 为:

text 复制代码
flag{ba00e1df-c9f3-4ac7-8cf5-a61b5936ce18}

理由:

  1. 该值位于解密后上传压缩包中的敏感凭据文件 credentials.json,语义完整且格式规范。
  2. README.md 中字符串为 falg{...},明显为拼写错误的诱饵。

第二题:bash_dump

我给的提示词是:

你现在是一个应急响应专家,小赵遇到了一个linux勒索病毒,他拿着公司提供的dump工具登上机器,发现黑客正在执行内网横向,没有

发现勒索病毒进程,只发现了黑客的bash进程.所以他就将bash进程使用公司的dump工具dump下来,然后他找到了被勒索的文件,并根据勒索信特征在网络上找到了相关分析文章,向作者拿到了勒索病毒的hash:03d44cc44664aad7816fa73dc38ce8e2a67864911ceb75c49f90fabfdb821ab5,并在一个类似vt的平台上下载到了样本.请分析文件夹中的文件,最终要找到flag

1. 题目概述

题目给了三类关键证据:

  • 攻击者 bash 进程内存 dump:dumped_4053_*.bin
  • 勒索样本:03d44cc44664aad7816fa73dc38ce8e2a67864911ceb75c49f90fabfdb821ab5
  • 被加密文件:flag.solarsec.locked

目标是恢复最终 flag


2. 取证与线索提取

dumped_4053_[23d1000-253e000].bin 中提取字符串后,看到多条勒索程序执行痕迹:

text 复制代码
./my_ransomware_23333 cae77ea5367e8d40bfa607179104 1155513bd65ceeeba70af9f06372
./my_ransomware_666666666 c2e77ea5367e8d40bfa607179104 1155513bd65ceeeba70af9f06372
./my_r@nsomw4re_lollollollol 0ae7dea53e7e8d40bfa907179104 1155513bd65ceeeba70af9f06a72
./my_r@nsomw4re_hahahahaha cae7dea53e7e8d40bfa607179104 1155513bd65ceeeba70af9f06a72

可判断勒索程序命令行需要两个 16 进制参数(疑似公钥坐标)。


3. 样本逆向结论

逆向样本主流程可得:

  1. 参数数量必须为 3(程序名 + x + y),否则打印 usage
  2. 固定读取文件:flag.solarsec
  3. 调用 ecc_enc(...) 进行加密
  4. 输出 flag.solarsec.locked

输出文件格式:

text 复制代码
"LOCKED"(6字节) + 原文长度(8字节小端) + 密文

ecc_enc 内置椭圆曲线参数(112-bit):

  • p = 0xe2e20e64347abef0fef83d1a5901
  • n = 0xe2e20e64347abea8720594c31783
  • a = 0x592a5290d72d5d58c73266dc96b2
  • b = 0x630e734697aa226547d498f14e5b
  • Gx = 0x881fb0676400f362aac2b6ae22e6
  • Gy = 0x5ceeaeea8ed10a70ee90737c9efb

从实现看,是 Crypto++ 的 ECIES 变体(KDF2(SHA1) + XOR + HMAC-SHA1)。


4. 关键突破点

从 dump 提取的 4 组候选坐标中,只有以下一组在曲线上:

  • Qx = cae7dea53e7e8d40bfa607179104
  • Qy = 1155513bd65ceeeba70af9f06a72

并且 Q = dG 属于该子群。

同时 n 可分解为小因子乘积,适合用 Pohlig-Hellman 求离散对数恢复私钥 d


5. 解题代码(可直接复制运行)

保存为 solve.py 后执行:python solve.py

python 复制代码
#!/usr/bin/env python3
import re
import math
import hmac
import hashlib
from pathlib import Path

# ========= Domain Parameters (from ransomware binary) =========
p = int("e2e20e64347abef0fef83d1a5901", 16)
n = int("e2e20e64347abea8720594c31783", 16)
a = int("592a5290d72d5d58c73266dc96b2", 16)
b = int("630e734697aa226547d498f14e5b", 16)
G = (
    int("881fb0676400f362aac2b6ae22e6", 16),
    int("5ceeaeea8ed10a70ee90737c9efb", 16),
)

# n factorization
FACTORS = [23, 347, 579079, 5412049, 3501919, 52536195991]

INF = None


def inv_mod(x, m):
    return pow(x % m, -1, m)


def on_curve(P):
    if P is INF:
        return True
    x, y = P
    return (y * y - (x * x * x + a * x + b)) % p == 0


def point_neg(P):
    if P is INF:
        return INF
    x, y = P
    return (x, (-y) % p)


def point_add(P, Q):
    if P is INF:
        return Q
    if Q is INF:
        return P

    x1, y1 = P
    x2, y2 = Q

    if x1 == x2:
        if (y1 + y2) % p == 0:
            return INF
        lam = ((3 * x1 * x1 + a) * inv_mod(2 * y1, p)) % p
    else:
        lam = ((y2 - y1) * inv_mod(x2 - x1, p)) % p

    x3 = (lam * lam - x1 - x2) % p
    y3 = (lam * (x1 - x3) - y1) % p
    return (x3, y3)


def scalar_mul(k, P):
    if P is INF:
        return INF
    if k < 0:
        return scalar_mul(-k, point_neg(P))

    R = INF
    A = P
    while k:
        if k & 1:
            R = point_add(R, A)
        A = point_add(A, A)
        k >>= 1
    return R


def point_key(P):
    return ("INF",) if P is INF else (P[0], P[1])


def bsgs(base, target, order):
    m = int(math.isqrt(order) + 1)

    table = {}
    R = INF
    for j in range(m):
        k = point_key(R)
        if k not in table:
            table[k] = j
        R = point_add(R, base)

    step = point_neg(scalar_mul(m, base))
    gamma = target
    for i in range(m + 1):
        k = point_key(gamma)
        if k in table:
            return i * m + table[k]
        gamma = point_add(gamma, step)
    return None


def crt(congruences):
    # congruences: [(a1,m1), (a2,m2), ...], pairwise coprime
    M = 1
    for _, mi in congruences:
        M *= mi

    x = 0
    for ai, mi in congruences:
        Mi = M // mi
        inv = pow(Mi, -1, mi)
        x = (x + ai * Mi * inv) % M
    return x, M


def pohlig_hellman(G, Q, n, factors):
    congruences = []
    for q in factors:
        h = n // q
        Gi = scalar_mul(h, G)
        Qi = scalar_mul(h, Q)
        di = bsgs(Gi, Qi, q)
        if di is None:
            raise RuntimeError(f"BSGS failed on factor {q}")
        di %= q
        if scalar_mul(di, Gi) != Qi:
            raise RuntimeError(f"Sub-check failed on factor {q}")
        congruences.append((di, q))

    d, mod = crt(congruences)
    if mod != n:
        raise RuntimeError("CRT modulus mismatch")
    d %= n
    if scalar_mul(d, G) != Q:
        raise RuntimeError("Final d verification failed")
    return d


def kdf2_sha1(z, out_len, shared_info=b""):
    out = b""
    counter = 1
    while len(out) < out_len:
        out += hashlib.sha1(z + counter.to_bytes(4, "big") + shared_info).digest()
        counter += 1
    return out[:out_len]


def extract_candidate_points_from_dump(dump_path):
    data = Path(dump_path).read_bytes()
    text = "".join(chr(c) if 32 <= c < 127 else " " for c in data)
    pairs = re.findall(r"([0-9a-f]{28})\s+([0-9a-f]{28})", text)

    uniq = []
    for x, y in pairs:
        t = (x, y)
        if t not in uniq:
            uniq.append(t)
    return uniq


def choose_valid_pubkey(candidates):
    valid = []
    for xh, yh in candidates:
        P = (int(xh, 16), int(yh, 16))
        if on_curve(P) and scalar_mul(n, P) is INF:
            valid.append(P)
    if len(valid) != 1:
        raise RuntimeError(f"Expected 1 valid pubkey, got {len(valid)}")
    return valid[0]


def decrypt_locked(locked_path, d):
    raw = Path(locked_path).read_bytes()
    if raw[:6] != b"LOCKED":
        raise RuntimeError("Bad magic")
    orig_len = int.from_bytes(raw[6:14], "little")
    ct = raw[14:]

    field_len = (p.bit_length() + 7) // 8  # 14 bytes
    if ct[0] != 0x04:
        raise RuntimeError("Unexpected ephemeral point format")
    ep_len = 1 + 2 * field_len

    Rx = int.from_bytes(ct[1:1 + field_len], "big")
    Ry = int.from_bytes(ct[1 + field_len:1 + 2 * field_len], "big")
    R = (Rx, Ry)
    if not on_curve(R):
        raise RuntimeError("Ephemeral point not on curve")

    C = ct[ep_len:-20]
    T = ct[-20:]

    S = scalar_mul(d, R)
    if S is INF:
        raise RuntimeError("Shared secret is INF")

    xS = S[0].to_bytes(field_len, "big")
    K = kdf2_sha1(xS, len(C) + 16, b"")
    ks = K[:len(C)]
    mk = K[len(C):]

    tag = hmac.new(mk, C, hashlib.sha1).digest()
    if tag != T:
        raise RuntimeError("HMAC verification failed")

    pt = bytes(ci ^ ki for ci, ki in zip(C, ks))
    return pt[:orig_len]


def main():
    dump_file = "dumped_4053_[23d1000-253e000].bin"
    locked_file = "flag.solarsec.locked"

    cands = extract_candidate_points_from_dump(dump_file)
    Q = choose_valid_pubkey(cands)

    print("[*] Qx =", hex(Q[0]))
    print("[*] Qy =", hex(Q[1]))
    print("[*] computing private key with Pohlig-Hellman ...")

    d = pohlig_hellman(G, Q, n, FACTORS)
    print("[*] d =", d)

    pt = decrypt_locked(locked_file, d)
    Path("flag.dec").write_bytes(pt)

    m = re.search(rb"flag\{[^}]+\}", pt)
    if m:
        print("[+] FLAG =", m.group(0).decode())
    else:
        print("[-] FLAG not found, plaintext below:")
        print(pt.decode("utf-8", "replace"))


if __name__ == "__main__":
    main()

6. 最终结果

text 复制代码
flag{bash_history_is_important_in_emergency_response@solarsec_202603}
相关推荐
LuoQuHen2 小时前
第四章:Agent架构全景图—— 从最小可行体到完整分层设计
ai·agent
xiami_world2 小时前
AI Agent生成PPT技术解析:从一键生成到意图理解,Agent模式如何重构PPT工作流?
人工智能·经验分享·ai·信息可视化·powerpoint
我是胡杨学长2 小时前
ChatGPT 连续三月流量下滑,是热度凉了还是 AI 泡沫要来了?
人工智能·ai·chatgpt
刘 大 望2 小时前
开发自定义MCP Server并部署
java·spring·ai·语言模型·aigc·信息与通信·ai编程
第十个灵魂3 小时前
OpenClaw Skill安装方式汇总
ai·skill·openclaw
ん贤3 小时前
AI 大模型落地系列|Eino 编排篇:从自动执行到人工接管,如何避免Agent一把梭
人工智能·ai·golang·eino
实在智能RPA3 小时前
深度解析企业级AI Agent安全架构与落地实践
人工智能·安全·ai·安全架构
CoderJia程序员甲3 小时前
GitHub 热榜项目 - 日榜(2026-03-30)
人工智能·ai·大模型·github·ai教程
Agent产品评测局3 小时前
企业超自动化落地,如何打通全业务流程的数据孤岛?技术路径全景盘点与选型指南
运维·人工智能·ai·chatgpt·自动化