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 静态服务目录。 - 影响:
- 路径穿越:文件名
../../xxx可写到容器内任意位置(web 根、可执行目录)。 - 任意后缀:
.html/.svg文件上传后直接可访问 → 存储型 XSS / 钓鱼页面。 - 配合任意文件写入,可覆盖
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 防护(无内网地址黑名单、无协议白名单、默认跟随重定向)。 - 影响:
- 读任意文件:
file:///etc/passwd、file:///pilot-server/pilot.cfg(拿数据库口令)。 - 探测内网:请求
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=fanqiepilot-server/pilot.cfg:1-19→ 三个库的明文 host/user/passwordpilot-server/Dockerfile.app→ 三个ENV xxx_PASSWORD=fanqie / Fanqie.1433sqldata/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 ...
- 成因:
- 黑名单不可靠:大小写绕过(
.PhP、.pHp)、双扩展名(.jpg.php经os.path.splitext取的是.php会被拦,但可传.php./.php空格/换行等)、.htaccess、.user.ini未在名单。 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再爆破)。
- L1:验证码由前端提交固定值
- 影响:验证码形同虚设,配合爆破类攻击。
- 修复:验证码应服务端校验、一次性使用、加入次数限制与混淆。
4. 组合利用链(重点)
单点漏洞拼接后可形成完整的"取系统/取数据"链路:
- 取数据库口令 → 拖库
V-08 任意文件下载 或 V-10 SSRFfile://→ 读pilot.cfg/Dockerfile.app→ 拿到 MySQL/MSSQL/PG 明文口令 → 直接连库读全部数据。 - 拿系统权限
V-01 / V-02 / V-03 / V-04 任一直接 RCE → 容器内 root → 可读全部环境变量与挂载。 - 伪造管理员身份 → 垂直越权
V-11 硬编码 JWT 密钥 → 伪造admintoken → 绕过 V-15/V-16/V-17 所有认证校验 → 任意改用户资料/提权。 - 未授权提权
V-12 直接改任意用户 UID=1 → 获得管理员账号 → 配合 V-13 拖全库。 - 获取用户会话
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 仅为代码逻辑推演。审计对象为教学靶场,报告中全部"漏洞"即该靶场的教学内容,请仅在隔离环境中学习使用。