Pilot-Web代码安全审计报告

18次阅读
没有评论

Pilot-Web 代码安全审计报告

审计目标:Pilot 领航员漏洞测试教学靶场(Pilot-Web)
审计时间:2026-08-13
代码版本:main 分支,最近提交 9e99fd2
技术栈:Python Flask 3.0 + MySQL/PostgreSQL/MSSQL + AdminLTE + Nginx
审计范围:全部后端 pilot-server/ 源码、前端 pilot-client/ 交互逻辑、数据库初始化脚本、Docker 部署配置


1. 审计概述

Pilot-Web 是一套 Web 安全教学靶场,README 已声明其刻意包含大量漏洞用于教学,仅允许部署在隔离虚拟机。本次审计站在"攻击者能获取多少系统/用户/应用权限与数据"的角度,逐模块核验了 30 个漏洞环境及其公共基础设施,确认了各漏洞的真实成因、可达路径与利用条件,并标注了可被组合利用提权/拖库的链路。

1.1 审计方法

  • 后端逐文件通读(35 个 Python 文件 + 3 份 SQL 初始化脚本 + Dockerfile/init.sh)
  • 前端关键交互页核对(反射型/存储型/DOM 型 XSS 的渲染方式、上传文件的可达性)
  • 硬编码凭据全局检索(账号、密码、JWT 密钥、Flask secret、数据库口令)
  • 按"未过滤输入 → 危险函数 → 可获取的数据/权限"链路验证每条漏洞

1.2 整体结论

维度 结论
核心问题 全部输入无校验/无过滤/无转义,大量高危函数直接暴露在路由中
最严重风险 多处直接 RCE(反序列化、命令执行、模板注入),拿到即可控制系统
权限类风险 存在未授权访问 + 越权 + JWT 密钥硬编码的组合,可任意伪造管理员身份
凭据风险 数据库口令、账号密码、全部 JWT 密钥硬编码进源码
数据风险 SQL 注入(3 种数据库)+ 任意文件读取/下载 + 未授权拖库
需特别注意 靶场服务(Flask 8089)被直接暴露在容器外部,攻击面即真实服务面

⚠️ 重要前提:这是一个教学靶场,绝大多数"漏洞"是有意为之的教学点。但下述「基础设施类」问题(硬编码 JWT 密钥、硬编码数据库口令、未授权接口、任意文件下载、上传路径穿越)即使放在教学语境下,也放大了任何单点漏洞的利用半径,使教学环境里的"提权到 admin / 读任意文件"过于廉价。严禁将本靶场部署到任何可被外部访问的网络。


2. 漏洞统计总览

编号 漏洞名称 类型 位置 严重级别
V-01 Pickle 反序列化 → RCE 反序列化 serialize/serialize.py:14 🔴 严重
V-02 命令执行(任意系统命令) RCE rce/rce.py:17 🔴 严重
V-03 PING 命令注入 RCE rce/rce_ping.py:13,18 🔴 严重
V-04 Jinja2 模板注入 → RCE SSTI ssti/jinja2.py:11 🔴 严重
V-05 MySQL 登录 SQL 注入 SQL注入 sqli/mysqli.py:16,35 🔴 严重
V-06 MSSQL 登录 SQL 注入 SQL注入 sqli/mssqli.py:16,35 🔴 严重
V-07 PostgreSQL SQL 注入 SQL注入 sqli/pgsqli.py:16,35 🔴 严重
V-08 任意文件下载 / 路径穿越 文件操作 download/file_download.py:12 🔴 严重
V-09 任意文件上传 + 路径穿越 文件操作 upload/upload_dir.py:19 🔴 严重
V-10 SSRF 任意文件读取 + 内网探测 SSRF ssrf/ssrf.py:11-24 🔴 严重
V-11 JWT 密钥硬编码 → 任意身份伪造 硬编码 全部 token 模块(9 处) 🔴 严重
V-12 未授权修改用户权限(改 UID=1 变 admin) 未授权/越权 authority/unauthorized.py:38-48 🔴 严重
V-13 未授权拖库(全量用户隐私数据) 未授权/信息泄露 infoleak/private_info.py:8-11 🔴 严重
V-14 SQL 报错信息泄露 + 列注入 信息泄露/SQL注入 infoleak/system_info.py:12-18 🟠 高
V-15 水平越权(篡改同权限用户信息) 越权 authority/horizontal.py:36-54 🟠 高
V-16 垂直越权(低权限改高权限账号) 越权 authority/vertical.py:36-54 🟠 高
V-17 CSRF 修改用户资料(GET 请求) CSRF csrf/csrf.py:38-53 🟠 高
V-18 存储型 XSS(伪造 XFF 注入日志) XSS xss/stored.py:17 + 前端渲染 🟠 高
V-19 硬编码账号密码 硬编码 infoleak/account_info.py:15 🟠 高
V-20 默认数据库弱口令(MySQL/MSSQL/PG) 硬编码 init.sh、pilot.cfg、3 个 Dockerfile 🟠 高
V-21 默认用户弱口令(admin/123456 等) 硬编码 sqldata/*.sql 初始化数据 🟠 高
V-22 反射型 XSS XSS xss/reflected.py:12 + xss_reflected.html:99 🟡 中
V-23 DOM 型 XSS XSS xss_dom.html:94 🟡 中
V-24 上传文件导致的存储型 XSS / WebShell XSS/上传 upload/client.py:19-21(无过滤) 🟠 高
V-25 上传后缀黑名单/Content-Type 可绕过 上传 upload/server_1.py:20、server_2.py:21-25 🟡 中
V-26 开放重定向 URL 重定向 urlredirect/url_redirect.py:11-12 🟡 中
V-27 弱口令无限制爆破(无速率限制) 暴力破解 bruteforce/*.py 🟡 中
V-28 密码以 MD5 明文存储 加密弱化 全部登录模块 + sqldata/*.sql 🟡 中
V-29 信任 X-Forwarded-For 伪造来源 IP 逻辑缺陷 xss/stored.py:17 🟢 低
V-30 验证码可客户端绕过 / 会话验证码 逻辑缺陷 vcdefect/vc_bypass_c.py:16、vc_bypass_s.py:17 🟢 低

统计:严重 13 · 高 7 · 中 6 · 低 2,共 30 项确认问题。


3. 漏洞详情

🔴 V-01 Pickle 反序列化 → 任意命令执行(RCE)

  • 位置:pilot-server/serialize/serialize.py:9-20
  • 漏洞代码:
@serialize.route('/api/unserialize/l1/pickle', methods=['POST'])
def unserialize():
    command = request.form['command']
    cmd = pickle.loads(base64.b64decode(command))   # ← 反序列化不可信数据
    if cmd == "rm -rf /*":
        return ...
    output = os.popen(cmd).read()                    # ← 甚至直接执行反序列化结果
  • 成因:对攻击者可控的 POST 参数直接 base64.b64decode 后 pickle.loads()。pickle 反序列化任意对象图时会执行 __reduce__ 指定的函数;即使去掉 os.popen(cmd) 这一行,构造恶意 pickle 对象仍可 RCE。
  • 影响:拿到 pickle payload 即可在容器内执行任意系统命令 → 控制系统(容器内 root)。
  • PoC:构造 pickle 对象 os.system 反序列化(经典 __reduce__ 链),base64 编码后提交即可;无需任何登录/凭据。
  • 可获取:系统权限、容器文件系统、环境变量(含全部数据库口令)。
  • 修复:不应对不可信数据使用 pickle;改为只允许安全数据格式(JSON),或对调用方做强认证。

🔴 V-02 命令执行(任意系统命令,Base64 包装)

  • 位置:pilot-server/rce/rce.py:9-20
  • 漏洞代码:
@rce.route('/api/rce/l1/getprocess', methods=['POST'])
def getprocess():
    command = request.form['command']
    cmd = base64.b64decode(command).decode()   # 仅做了 base64 包装
    if cmd == "rm -rf /*":                     # 只拦截这一个完全匹配
        return ...
    output = os.popen(cmd).read()              # ← 任意命令执行
  • 成因:os.popen(cmd) 直接执行用户输入,rm -rf /* 的拦截用全等字符串比较,可轻易绕过(rm -rf /、rm -rf / *、sh -c 'rm -rf /*' 等)。
  • 影响:直接 RCE,无需登录。
  • PoC:curl -X POST http://<web>:8089/api/rce/l1/getprocess -d "command=$(echo -n 'id' | base64)" → 返回 uid=0(root)。
  • 可获取:系统权限(容器内 root)。
  • 修复:删除该路由或改为教学专用白名单命令;生产环境禁止将命令拼接/传递至 shell。

🔴 V-03 PING 接口命令注入

  • 位置:pilot-server/rce/rce_ping.py:8-22
  • 漏洞代码:
ip_regex = r'\b(?:[0-9]{1,2}|...)\b'          # 纯 IP 正则
if not re.findall(ip_regex, ip):              # ← findall 是"包含匹配",不是 fullmatch
    return responses.callback_rce_ping_iperror
cmd = "ping " + ip + " -c 3"                  # ← 字符串拼接进 shell
output = os.popen(cmd).read()
  • 成因:re.findall 只要 ip 参数里包含一个合法 IP 子串即通过校验,拼接进 shell 后即可用 ;、|、&、$()、反引号注入。rm -rf /* 拦截同样用 in 弱判断。
  • 影响:命令注入 → RCE。
  • PoC:ip=127.0.0.1;whoami → 正则匹配到 127.0.0.1 放行,实际执行 ping 127.0.0.1;whoami -c 3。
  • 修复:re.fullmatch + 白名单字符集校验;或改用 subprocess 参数数组形式避免 shell。

🔴 V-04 Jinja2 模板注入(SSTI)→ RCE

  • 位置:pilot-server/ssti/jinja2.py:7-12
  • 漏洞代码:
@jinja2.route('/api/ssti/l1/jinja2', methods=['GET'])
def ssti_jinja2():
    param = request.args['param']
    rendered_template = render_template_string(param)   # ← 直接渲染用户输入
    return rendered_template
  • 成因:render_template_string 直接渲染 GET 参数,没有任何过滤。
  • 影响:经典 SSTI,可 RCE。
  • PoC:/api/ssti/l1/jinja2?param={{config.__class__.__init__.__globals__['os'].popen('id').read()}}。
  • 可获取:系统权限、环境变量、源码、数据库口令。
  • 修复:禁止将用户输入作为模板;确需模板化时应使用 Jinja2 沙箱并在隔离环境运行。

🔴 V-05 MySQL 登录 SQL 注入

  • 位置:pilot-server/sqli/mysqli.py:11-24(登录)、:27-45(getAuthinfo)
  • 漏洞代码:
sql = "SELECT USERNAME FROM USER WHERE (USERNAME = '" + username + \
      "' AND PASSWORD = '" + hashlib.md5(password.encode()).hexdigest() + "')"
user = query_db(sql)
  • 成因:username 直接字符串拼接进 SQL;getAuthinfo 的 SELECT UID FROM USER WHERE USERNAME = '" + username + "' 同样可注入。
  • 影响:SQL 注入 → 任意用户登录、绕过认证、拖库(注入 UNION SELECT 读 USER 全表)。
  • PoC:用户名 admin' or '1'='1 直接以 admin 登录并拿到合法 token;或 ' UNION SELECT USERNAME FROM USER -- - 拼接查询。
  • 可获取:应用数据(用户表)、认证绕过 → 管理员权限。
  • 修复:统一使用参数化查询(query_db(sql, (username,))),禁止字符串拼接。

🔴 V-06 MSSQL 登录 SQL 注入

  • 位置:pilot-server/sqli/mssqli.py:11-24、:27-45
  • 漏洞代码:同 V-05,"SELECT USERNAME FROM Dbo.[USER] WHERE (USERNAME = '" + username + ...。
  • 影响:MSSQL 环境下可堆叠注入(;),危害比 MySQL 更大——可执行存储过程(如 xp_cmdshell),直接系统命令执行。
  • 修复:参数化查询。

🔴 V-07 PostgreSQL SQL 注入

  • 位置:pilot-server/sqli/pgsqli.py:11-24、:27-45
  • 漏洞代码:同 V-05,'SELECT "USERNAME" FROM pilot."USER" WHERE ("USERNAME" = \'' + username + ...。
  • 影响:PG 下可 UNION 注入/报错注入,读任意表;配合 pg_read_file 等函数可读服务器文件。
  • 修复:参数化查询。

🔴 V-08 任意文件下载 / 路径穿越

  • 位置:pilot-server/download/file_download.py:6-15
  • 漏洞代码:
@filedownload.route('/api/fd/l1/download',methods=['GET'])
def download():
    file_path = request.args['file']
    file = "download/" + file_path            # ← 未做路径校验
    return send_file(file, as_attachment=True)
  • 成因:file 参数直接拼接相对路径并 send_file,可用 ../ 穿越出 download/ 目录。
  • 影响:读取容器内任意文件。
  • PoC:/api/fd/l1/download?file=../../pilot-server/pilot.cfg → 直接下载到数据库明文口令;file=../../app.py 下载后端源码;file=../../etc/passwd。
  • 可获取:系统文件、应用源码、数据库凭据(→ 进一步连库拖数据)。
  • 修复:白名单文件映射;用 os.path.realpath 校验解析后路径必须在允许目录内。

🔴 V-09 任意文件上传 + 路径穿越(写 Web 目录)

  • 位置:pilot-server/upload/upload_dir.py:11-25
  • 漏洞代码:
@upload_dir.route('/api/upload/l4/client', methods=['POST'])
def upload_file():
    file = request.files['file']
    filepath = os.path.join('../pilot-client/pages/upload/', file.filename)  # ← 原名直接拼接
    file.save(filepath)
  • 成因:file.filename 未做任何清洗/后缀/路径校验,且上传目录 ../pilot-client/pages/upload/ 恰是 Nginx 静态服务目录。
  • 影响:
    1. 路径穿越:文件名 ../../xxx 可写到容器内任意位置(web 根、可执行目录)。
    2. 任意后缀:.html/.svg 文件上传后直接可访问 → 存储型 XSS / 钓鱼页面。
    3. 配合任意文件写入,可覆盖 app.py 等实现代码级后门(需要重启或触发 import)。
  • PoC:上传 filename="../../pilot-server/app.py" 覆盖后端源码;或上传含 XSS 的 x.html 后访问 http://<nginx>:8088/pages/upload/x.html。
  • 修复:随机化服务端文件名(禁用原文件名)、白名单后缀+白名单 MIME、校验目标路径 realpath 在允许目录、上传目录不应在 Web 可达路径下。

🔴 V-10 SSRF:任意文件读取 + 内网探测

  • 位置:pilot-server/ssrf/ssrf.py:8-26
  • 漏洞代码:
@ssrf.route('/api/ssrf/l1/getinfo', methods=['GET'])
def fetch():
    url = request.args['url']
    if url.startswith('file://'):
        filepath = url[7:]
        with open(filepath, 'r', encoding="utf-8") as file:
            return file.read()          # ← 任意本地文件读取
    else:
        res = requests.get(url)         # ← 无协议/网段限制,跟随重定向
        return res.text
  • 成因:file:// 协议直接读本地文件;requests.get 无 SSRF 防护(无内网地址黑名单、无协议白名单、默认跟随重定向)。
  • 影响:
    1. 读任意文件:file:///etc/passwd、file:///pilot-server/pilot.cfg(拿数据库口令)。
    2. 探测内网:请求 http://127.0.0.1:8089/...、http://<db容器>:3306/...、云元数据地址(如 http://169.254.169.254/)。
  • 修复:协议白名单(http/https)、内网/链路本地地址黑名单、禁止 file://、禁用重定向或二次校验。

🔴 V-11 JWT 签名密钥硬编码 → 任意身份伪造

  • 位置(9 处,全部硬编码在源码):

    • bruteforce/bf_token.py:40 → SECRET_KEY = "1"(最弱)
    • sqli/mysqli.py:52 → "PILOT/mysqli"
    • sqli/mssqli.py:52 → "PILOT/mssqli"
    • sqli/pgsqli.py:52 → "PILOT/pgsqli"
    • xss/stored.py:47 → "PILOT/xss_stored_t0ken"
    • authority/unauthorized.py:56 → "PILOT/unauthorized_t0ken"
    • authority/horizontal.py:62 → "PILOT/horizontal_t0ken"
    • authority/vertical.py:62 → "PILOT/vertical_t0ken"
    • csrf/csrf.py:61 → "PILOT/csrf_t0ken"
    • 另:app.py:67 → pilot.secret_key = 'pilot/web'(Flask 会话密钥)
  • 成因:JWT 使用 HS256,密钥以明文字符串写死且全部提交在 git 仓库中。攻击者拿到源码即可对任意 account(含 admin)离线签发合法 token,verify_token 校验通过。

  • 影响:绕过所有依赖 token 的认证,直接以任意用户/admin 身份调用接口 → 与 V-12/V-15/V-16 越权形成闭环。

  • PoC(以 bf_token 为例,密钥 "1"):用 jwt.encode({"account":"admin","overdue_time":<一天后时间戳>}, "1", algorithm="HS256") 伪造 admin token 即可通过 /api/bf/l3/verify。

  • 修复:密钥应从环境变量/密钥管理服务注入,且不得入库;不同模块应共用一套签发校验逻辑。


🔴 V-12 未授权修改用户权限(任意用户提权为 admin)

  • 位置:pilot-server/authority/unauthorized.py:38-48
  • 漏洞代码:
@unauthorized.route('/api/auth/l1/modifyAuth',methods=['POST'])
def modifyAuth():                                # ← 没有任何 token / 登录校验
    username = request.form['username']
    uid = request.form['uid']
    if username != "admin":
        uid = '0' if uid != '1' else uid
        modify_db("UPDATE USER SET UID = %s WHERE USERNAME = %s", (uid, username))
  • 成因:接口无任何身份认证装饰器(对比同文件其它接口都要求 Authorization);仅有的限制是"不能改 admin"和"uid 只能设 0/1"。
  • 影响:攻击者无需登录,直接把任意普通账号 UID 改为 1(管理员),或用 username=任意目标 改他人权限 → 控制应用全部用户权限。
  • PoC:curl -X POST http://<web>:8089/api/auth/l1/modifyAuth -d "username=pilot&uid=1" → pilot 变为管理员。
  • 修复:该接口必须校验当前登录身份与权限,且只能由管理员操作。

🔴 V-13 未授权拖库(全量用户隐私数据)

  • 位置:pilot-server/infoleak/private_info.py:8-11
  • 漏洞代码:
@private_info.route('/api/il/l2/getUserinfo',methods=['GET'])
def getUserinfo():
    res = query_db("SELECT * FROM VIRTUAL_USERINFO")   # ← 无任何认证
    return responses.update_callback(responses.callback_public_getinfo, {"msg": res})
  • 成因:GET 接口无 token 校验,直接 SELECT * 全表返回。
  • 影响:一次请求返回全部虚拟用户的姓名、生日、手机号、身份证号、住址、银行卡号等敏感数据,构成未授权大规模数据泄露。
  • PoC:直接 GET /api/il/l2/getUserinfo 即返回 10 条全字段数据。
  • 修复:至少要求登录认证,并做最小字段返回/脱敏。

🟠 V-14 SQL 报错信息泄露 + 列注入

  • 位置:pilot-server/infoleak/system_info.py:8-20
  • 漏洞代码:
query = "SELECT " + param + " FROM USER"      # ← 参数直接拼进 SELECT 列位
try:
    a = query_db(query)
except Exception as e:
    if "1064" in str(e):
        return ... {'msg': str(e)}            # ← 完整 MySQL 报错原文回显
  • 成因:param 可注入列位(列注入),且命中 1064 语法错误时把完整异常字符串回显。
  • 影响:泄露数据库类型/版本/表结构/绝对路径等,辅助后续注入或提权。
  • PoC:/api/il/l1/getNext1?param=USERNAME,(截断)触发 1064 报错,回显完整 SQL 与库信息。
  • 修复:参数化查询 + 统一异常处理,不向客户端回显原始错误。

🟠 V-15 水平越权(篡改同权限用户资料)

  • 位置:pilot-server/authority/horizontal.py:36-54
  • 漏洞代码:
@horizontal.route('/api/auth/l2/modifyUserInfo',methods=['POST'])
def modifyUserInfo():
    account = 从token解析出的用户名
    auth1 = query_db("SELECT UID FROM USER WHERE USERNAME = %s", (username))[0]['UID']
    auth2 = query_db("SELECT UID FROM USER WHERE USERNAME = %s", (account))[0]['UID']
    if auth1 == auth2:          # ← 仅比较"UID 相等",即可操作任意同权限用户
        modify_db("UPDATE USER SET PHONE = %s,QQ = %s WHERE USERNAME = %s", (phone, qq, username))
  • 成因:username(目标)完全由客户端提交,权限判定只看"双方 UID 是否相同",普通用户可修改任意其他普通用户的手机号/QQ。
  • 影响:水平越权,篡改他人账户绑定信息 → 可配合社工/撞库/重置他人账号。
  • PoC:以 pilot 登录,username=pilot2(同为 UID=0)即可改其资料。
  • 修复:目标对象应从会话/Token 推导,禁止客户端指定他人 username;如需授权操作应按角色校验。

🟠 V-16 垂直越权(低权限操作高权限账号)

  • 位置:pilot-server/authority/vertical.py:36-54
  • 漏洞代码:
auth1 = query_db("SELECT UID FROM USER WHERE USERNAME = %s", (username))[0]['UID']
auth2 = query_db("SELECT UID FROM USER WHERE USERNAME = %s", (account))[0]['UID']
if int(auth1) > int(auth2) or account == username:   # ← 目标UID更大 或 修改自己 → 放行
    modify_db("UPDATE USER SET PHONE = %s,QQ = %s WHERE USERNAME = %s", ...)
  • 成因:逻辑以"目标 UID 数值更大"作为放行条件,配合可伪造 token(V-11),任意身份都能命中分支修改他人资料。
  • 影响:垂直越权;结合 V-11 伪造 admin token 后,可修改 admin 之外任意账号。
  • 修复:按角色-资源访问控制表(如仅管理员可操作,或目标必须等于本人)实现。

🟠 V-17 CSRF:GET 请求触发状态修改

  • 位置:pilot-server/csrf/csrf.py:38-53
  • 漏洞代码:
@csrf.route('/api/csrf/l1/modifyUserInfo',methods=['GET'])   # ← 状态修改用 GET
@responses.check_header(['Cookie'])
def modifyUserInfo():
    phone = request.args['phone']
    qq = request.args['qq']
    account = 从Cookie token解析
    if verify_token(token, account):
        modify_db("UPDATE USER SET PHONE = %s,QQ = %s WHERE USERNAME = %s", ...)
  • 成因:修改资料走 GET + Cookie 认证,无 CSRF token、无 SameSite、无请求来源校验。浏览器会自动携带 Cookie 发起跨站 GET。
  • 影响:诱导已登录用户访问恶意链接即可篡改其资料(钓鱼/钓鱼 + 绑定信息替换)。
  • PoC:<img src="http://<web>:8089/api/csrf/l1/modifyUserInfo?phone=1&qq=1"> 置于任意网页,受害者访问即触发。
  • 修复:状态修改改用 POST + CSRF token;Cookie 加 SameSite=Lax/Strict;校验 Origin/Referer。

🟠 V-18 存储型 XSS(伪造 XFF 注入登录日志)

  • 位置:后端 pilot-server/xss/stored.py:17 + 前端 pilot-client/pages/xss/xss_stored.html:179-195
  • 漏洞代码:
client_ip = request.headers.get('X-Forwarded-For', request.remote_addr).split(",")[0]
modify_db("INSERT INTO LOGIN_LOG (USERNAME, TIME, LOGINIP) VALUES (%s, %s, %s)", (username, time, client_ip))
// xss_stored.html 管理员查看日志时:
var row = `<tr><td>${index+1}</td><td>${item.USERNAME}</td>...
            <td>${item.LOGINIP}</td></tr>`;   // ← 未转义,innerHTML 渲染
tableBody.append(row);
  • 成因:X-Forwarded-For 完全由客户端控制且未过滤即入库;管理员查看日志时字段原样以 HTML 注入。
  • 影响:任意用户登录时伪造 X-Forwarded-For 注入 <img onerror=...>,管理员一旦查看日志即触发 → 窃取管理员会话/凭据。
  • PoC:登录请求加头 X-Forwarded-For: <img src=x onerror=alert(document.cookie)>,admin 打开日志页触发。
  • 修复:前端转义输出(用 textContent 或转义函数);后端不信任 XFF(见 V-29),入库前过滤 HTML。

🟠 V-19 硬编码账号密码(应用层)

  • 位置:pilot-server/infoleak/account_info.py:15
  • 漏洞代码:
if username == "pilottest123" and password == "fanqie@123":
    return ... {'username': '测试管理员1'}
  • 成因:测试管理员账号密码明文硬编码在源码,随仓库分发。
  • 影响:任何人拿到源码即可用该"测试管理员"登录(虽然只返回一个用户名,但作为硬编码后门入口,一旦被后续模块复用即成为真实后门)。
  • 修复:密码走环境变量/凭据库;测试账号不得以"管理员"名义硬编码。

🟠 V-20 默认数据库弱口令(MySQL/MSSQL/PostgreSQL)

  • 位置:

    • init.sh:15-17 → mysql_password=fanqie、mssql_password=Fanqie.1433、pgsql_password=fanqie
    • pilot-server/pilot.cfg:1-19 → 三个库的明文 host/user/password
    • pilot-server/Dockerfile.app → 三个 ENV xxx_PASSWORD=fanqie / Fanqie.1433
    • sqldata/Dockerfile.mysql:3、Dockerfile.mssql:5、Dockerfile.pgsql:3
  • 成因:数据库 root/sa/postgres 口令全部以明文硬编码,且 MySQL/PG 口令 fanqie 极弱、MSSQL 口令可预测;pilot.cfg 还直接随仓库提交了三个库的完整连接信息。

  • 影响:攻击者拿到源码(或经 V-08/V-10 读到 pilot.cfg)即获得三库管理权限 → 拖库、删库、读取任意数据。

  • 修复:口令改由部署时注入(环境变量/密钥管理),仓库内仅留占位符;部署后强制修改默认口令。


🟠 V-21 默认用户弱口令(admin/123456)

  • 位置:sqldata/mysql.sql:56-58、mssql.sql、pgsql.sql 的初始化数据
  • 漏洞数据:
用户名 密码哈希 明文(已确认) 权限
admin 21232f297a57a5a743894a0e4a801fc3 admin UID=1(管理员)
pilot e10adc3949ba59abbe56e057f20f883e 123456 UID=0
fanqie 0f359740bd1cda994f8b55330c86d845 未在常见字典中命中(MD5,可离线爆破) UID=0/1
  • 成因:管理员账号 admin/admin、普通账号 pilot/123456 随初始化脚本入库,MD5 无盐。
  • 影响:默认即可用 admin 登录所有依赖 USER 表的登录点,直接获得管理员身份;MD5 哈希可离线爆破。
  • 修复:初始化脚本生成随机强口令;密码改加盐哈希(bcrypt/argon2)。

🟠 V-24 上传文件导致存储型 XSS / WebShell 载体

  • 位置:pilot-server/upload/client.py:11-26(L1 无任何过滤)、upload/upload_dir.py(见 V-09)
  • 漏洞代码:
@upload_client.route('/api/upload/l1/client', methods=['POST'])
def upload_file():
    file = request.files['file']
    encrypted_filename = encrypt_filename(file.filename)     # 只做 md5 改名,保留原扩展名
    filepath = os.path.join('../pilot-client/pages/upload/', encrypted_filename)
    file.save(filepath)
  • 成因:L1 对上传文件零过滤,任意后缀(含 .html/.svg)保存到 Nginx 可访问目录,文件名虽被 md5 化但扩展名原样保留,且响应会把最终文件名返回给攻击者。
  • 影响:上传含 XSS 的 .html 文件 → 存储型 XSS/钓鱼页,任何访问者(含管理员)中招;若目标环境支持解释器(如配合 V-09 覆盖可执行文件),可成为 WebShell 载体。
  • PoC:上传 a.html(内容 <script>...</script>),用返回的文件名访问 http://<nginx>:8088/pages/upload/<md5>.html 即触发。
  • 修复:按"服务端生成随机名 + 丢弃扩展名"或"扩展名/内容双重白名单";上传目录禁脚本执行并脱离 Web 可达根。

🟡 V-22 反射型 XSS

  • 位置:后端 pilot-server/xss/reflected.py:9-13 + 前端 pilot-client/pages/xss/xss_reflected.html:94-102
  • 漏洞代码:
xss = request.args['search']
res = "'" + xss + "' 的搜索结果:未找到资源!"
return responses.update_callback(responses.callback_public_getinfo, {"msg": res})
$("#notice")[0].innerHTML = generateNote(resp.msg);   // 响应直接 innerHTML 渲染
  • 成因:输入未过滤、输出未转义,前端 innerHTML 渲染。<script> 标签经 innerHTML 不执行,但 <img onerror>、<svg onload>、<iframe onload> 等均触发。
  • 影响:反射型 XSS,可窃取同源 Cookie/会话、钓鱼、挂马。
  • 修复:前端用 textContent 渲染;后端对输出 HTML 转义。

🟡 V-23 DOM 型 XSS

  • 位置:pilot-client/pages/xss/xss_dom.html:93-95
  • 漏洞代码:
function submitText(){
  $("#notice")[0].innerHTML = generateNote("你好呀: " + $("#own-text")[0].value + " !")
}
  • 成因:输入框内容未经任何处理直接拼入 innerHTML。
  • 影响:DOM 型 XSS(仅前端,不影响后端数据),危害相对较低,但仍可窃取同源会话。
  • 修复:使用 textContent 或转义。

🟡 V-25 上传后缀黑名单 / Content-Type 白名单可绕过

  • 位置:pilot-server/upload/server_1.py:20、pilot-server/upload/server_2.py:21-25
  • 漏洞代码:
# server_1: 后缀黑名单 —— 大小写不敏感处理缺失
if os.path.splitext(file.filename)[1] in [".html",".exe",".pdf",".py",".php",".svg",...]:
    return ...
# server_2: 黑名单 + MIME 白名单 —— MIME 由客户端上报
if file.content_type not in ["image/jpeg","image/png","image/gif","image/webp"]:
    return ...
  • 成因:
    1. 黑名单不可靠:大小写绕过(.PhP、.pHp)、双扩展名(.jpg.php 经 os.path.splitext 取的是 .php 会被拦,但可传 .php./.php 空格/换行等)、.htaccess、.user.ini 未在名单。
    2. file.content_type 是客户端可控的请求头,直接伪造为 image/png 即可绕过 MIME 白名单,而保存仍按原始文件名(含真实扩展名)。
  • 影响:可上传非图片可执行文件/恶意文件到可访问目录。
  • 修复:改用后缀白名单 + 服务端真实 MIME 嗅探(如 imghdr/python-magic)+ 随机化文件名,不用黑名单。

🟡 V-26 开放重定向

  • 位置:pilot-server/urlredirect/url_redirect.py:7-14
  • 漏洞代码:
url = request.args['url']
if url.startswith('http://') or url.startswith('https://'):
    return redirect(url)
  • 成因:仅校验前缀为 http/https,redirect 自动拼接,http:// 开头但可跳转任意站点;还可用 http:// 前加空白/大小写(HTTP://)绕过。
  • 影响:开放重定向 → 钓鱼(仿冒官方域名跳转恶意站)、OAuth 流程利用。
  • 修复:白名单域名后缀;或限制只能站内跳转。

🟡 V-27 弱口令无限制爆破(无速率限制/无锁定)

  • 位置:pilot-server/bruteforce/bf_username.py:10-23、bf_password.py:10-19、bf_token.py:10-19
  • 成因:登录接口无失败次数限制、无锁定、无验证码(bf_token 的 token 密钥还是 "1"),配合默认弱口令(V-21)可无限次爆破。
  • 影响:弱口令账号易被爆破;用户名枚举接口(bf_username 区分"密码错误/账号不存在")进一步泄露有效用户名。
  • 修复:失败次数限制 + 锁定/退避、强制验证码、账号锁定。

🟡 V-28 密码以无盐 MD5 存储

  • 位置:全部登录模块(如 mysqli.py:16、bf_*.py、stored.py、authority/*、csrf.py) + sqldata/*.sql
  • 成因:hashlib.md5(password.encode()).hexdigest() 无盐 MD5,彩虹表可直接命中弱口令(admin/123456 均为常见 MD5)。
  • 影响:数据库泄露(经 V-13/V-20 等)后,口令可批量离线还原。
  • 修复:改用 bcrypt/argon2/scrypt。

🟢 V-29 信任 X-Forwarded-For 伪造来源 IP

  • 位置:pilot-server/xss/stored.py:17
  • 成因:request.headers.get('X-Forwarded-For', request.remote_addr).split(",")[0] 直接信任客户端可伪造的 XFF 头。
  • 影响:审计/溯源日志失真;与 V-18 结合即成为存储型 XSS 注入点。
  • 修复:在可信反向代理(nginx)处重写 XFF,后端不得直接信任。

🟢 V-30 验证码缺陷(客户端可绕过 / 会话固定)

  • 位置:pilot-server/vcdefect/vc_bypass_c.py:11-17、vc_bypass_s.py:11-24
  • 成因:
    • L1:验证码由前端提交固定值 verify != "true" 判断,客户端可控,传 true 即绕过。
    • L2:验证码存 Flask session,session['code'] == code 比对;验证码 4 位字符集较小且无失效/次数限制,可穷举;且比对不区分请求生成时序(可先 GET /getcode 再爆破)。
  • 影响:验证码形同虚设,配合爆破类攻击。
  • 修复:验证码应服务端校验、一次性使用、加入次数限制与混淆。

4. 组合利用链(重点)

单点漏洞拼接后可形成完整的"取系统/取数据"链路:

  1. 取数据库口令 → 拖库
    V-08 任意文件下载 或 V-10 SSRF file:// → 读 pilot.cfg/Dockerfile.app → 拿到 MySQL/MSSQL/PG 明文口令 → 直接连库读全部数据。
  2. 拿系统权限
    V-01 / V-02 / V-03 / V-04 任一直接 RCE → 容器内 root → 可读全部环境变量与挂载。
  3. 伪造管理员身份 → 垂直越权
    V-11 硬编码 JWT 密钥 → 伪造 admin token → 绕过 V-15/V-16/V-17 所有认证校验 → 任意改用户资料/提权。
  4. 未授权提权
    V-12 直接改任意用户 UID=1 → 获得管理员账号 → 配合 V-13 拖全库。
  5. 获取用户会话
    V-18 存储 XSS(伪造 XFF)→ 管理员查看日志触发 → 窃取管理员 Cookie/Token。

5. 修复建议优先级

优先级 动作 涉及漏洞
P0(立即) 不将靶场暴露到不可信网络;部署仅限隔离虚拟机 全部
P0 删除/禁用直接命令执行与反序列化路由(RCE、ping、pickle、SSTI),或加白名单与强认证 V-01~V-04
P0 全部 SQL 改参数化查询;杜绝字符串拼接 V-05~V-08、V-14
P0 文件下载/上传加路径与后缀/MIME 白名单,随机化文件名 V-08、V-09、V-24、V-25
P1 JWT 密钥/数据库口令/Flask secret 改环境变量注入,仓库只留占位符;部署后强制改密 V-11、V-19、V-20、V-21
P1 未授权/越权接口补身份认证与资源级鉴权 V-12、V-13、V-15、V-16
P1 SSRF 加协议白名单与内网地址黑名单,禁 file:// V-10
P1 前端输出一律转义;后端入库前过滤;状态修改改 POST+CSRF token V-17、V-18、V-22、V-23
P2 登录加限流锁定、验证码服务端强校验、口令改加盐哈希 V-27、V-28、V-30
P2 不信任 XFF、开放重定向加白名单 V-26、V-29

6. 附:涉及接口清单

方法 路径 漏洞
POST /api/sqli/l1/login MySQL 注入 V-05
GET /api/sqli/l1/getAuthinfo SQL 注入 V-05 / JWT 伪造 V-11
POST /api/sqli/l2/login MSSQL 注入 V-06
POST /api/sqli/l3/login PG 注入 V-07
POST /api/rce/l1/getprocess RCE V-02
POST /api/rce/l2/ping 命令注入 V-03
GET /api/ssti/l1/jinja2 SSTI→RCE V-04
POST /api/unserialize/l1/pickle 反序列化 RCE V-01
GET /api/ssrf/l1/getinfo SSRF/任意文件读 V-10
GET /api/fd/l1/download 任意文件下载 V-08
POST /api/upload/l1~l4/client 上传绕过/穿越 V-09、V-24、V-25
GET /api/auth/l1/modifyAuth(POST) 未授权提权 V-12
GET /api/il/l2/getUserinfo 未授权拖库 V-13
GET /api/il/l1/getNext1 列注入+报错泄露 V-14
POST /api/auth/l2/modifyUserInfo 水平越权 V-15
POST /api/auth/l3/modifyUserInfo 垂直越权 V-16
GET /api/csrf/l1/modifyUserInfo CSRF V-17
GET /api/xss/l1/reflected 反射 XSS V-22
POST /api/xss/l2/login + GET /getlog 存储 XSS V-18
GET /api/ur/l1/getimg 开放重定向 V-26
POST /api/bf/l1~l3/login 弱口令爆破 V-27

本报告基于源码静态审计,未对运行实例发起任何实际攻击验证;所有 PoC 仅为代码逻辑推演。审计对象为教学靶场,报告中全部"漏洞"即该靶场的教学内容,请仅在隔离环境中学习使用。

正文完
 0