注意,这一关必须要使用Burpsuite来抓包
目录
Low
1.抓包
打开代理,然后在登录框随便输入账号密码
可以拦截抓包:
也可以在历史中找到对应的包:

2.发送到爆破模块
右键
3.选择爆破模式
将Attack type的值设置为Cluster bomb
爆破模式介绍
4.添加载荷
选中需要爆破的地方,点击add(在前后输入§
也行)

5.添加字典
可以选择导入字典也可以手动输入
6.爆破查看

果然成功了:
查看源码
php
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// 安全风险:使用GET方法传递用户名和密码,会暴露在URL中并可能被日志记录
// 建议:使用POST方法处理敏感信息
// Get username
$user = $_GET[ 'username' ];
// 安全风险:未对用户名进行任何过滤或转义处理,直接用于SQL查询
// 存在严重的SQL注入漏洞
// Get password
$pass = $_GET[ 'password' ];
// 安全风险:密码通过GET传输,存在泄露风险
// 安全风险:使用MD5哈希密码,安全性极低,易被彩虹表破解
// 安全风险:未使用盐值增强密码哈希安全性
// 建议:使用password_hash()和password_verify()
$pass = md5( $pass );
// Check the database
// 安全风险:直接拼接用户输入构建SQL查询,存在严重SQL注入漏洞
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
// 安全风险:数据库错误直接显示给用户,可能泄露数据库结构等敏感信息
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
// 安全风险:直接输出用户可控的$avatar变量,可能存在XSS攻击
// 示例:如果avatar存储为javascript:alert('xss'),可能执行脚本
echo "<img src=\"{$avatar}\" />";
// 安全风险:缺少会话管理机制,无法在后续请求中保持登录状态
// 建议:使用session_start()初始化会话并存储用户认证信息
}
else {
// Login failed
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
可以发现,这个登录框甚至都没有做sql注入防护,那么使用万能密码就可以直接登录...
- 总的来说,这个只实现了验证账号密码是否匹配的功能,几乎没有安全性可言
Medium
查看源码
php
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// 安全风险:使用GET方法传输用户名和密码,数据会暴露在URL中,可能被日志记录
// 建议:使用POST方法传输敏感信息
// Sanitise username input
$user = $_GET[ 'username' ];
// 安全风险:仅使用mysqli_real_escape_string()不足以完全防止SQL注入
// 建议:使用参数化查询(prepared statements)
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
// 同样存在SQL注入防护不足的问题
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// 安全风险:使用MD5哈希密码,安全性不高,易被破解
// 安全风险:未使用盐值(salt)增强哈希安全性
// 建议:使用password_hash()和password_verify()函数
$pass = md5( $pass );
// Check the database
// 安全风险:通过字符串拼接构建SQL查询,存在SQL注入风险
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
// 安全风险:查询错误时直接输出数据库错误信息,可能泄露敏感信息
// 建议:生产环境中不显示具体错误信息,记录到日志
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
// 安全风险:直接输出从数据库获取的avatar路径,可能存在XSS攻击风险
echo "<img src=\"{$avatar}\" />";
// 安全风险:缺少会话管理机制,无法在后续请求中保持身份验证状态
// 建议:使用session_start()和$_SESSION存储用户认证状态
}
else {
// Login failed
// 安全风险:登录失败时使用sleep(2),导致响应时间差异,可能被用于暴力破解
// 建议:移除sleep()或对成功和失败响应使用相同的延迟处理
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
- 不难发现,相比于Low级别的代码,Medium级别的代码主要增加了
mysql_real_escape_string
函数,这个函数会对字符串中的特殊符号(x00,n,r,,',",x1a)进行转义,把其中的字符串给过滤掉了,勉强能够抵御一般的sql注入攻击,那Low等级时候用到的注入就失效了,需要注意的是中级的暴力破解相对来说较慢是因为有个sleep函数,在破解失败后会使程序停止运行两秒。所以我们直接用爆破方法即可,和low级的一样。
High
1.抓包
发送到爆破模块,发现登陆时有token
要么删除token后发包 ;要么使用同一个token,重放 ;要么使用burp得插件CSRF Token Tracker ,根据规则从reponse报文中找到token并使其他模块发送报文时自动更新新的token。
2.在bp的extensions
中找到CSRF Token Tracker
,并安装
其中host 为包 中的host,name 为token字段名
ps:使用CSRF Token Tracker 插件的原理:你提交用户名密码后,burp捕获这个数据包,然后你将这个数据包
send to repeater
,send发送给服务端,服务端发送响应包, CSRF Token Tracker 插件捕获响应包中的user_token
。
3.构造字典
和low一样添加字典
使用单线程
4.成功爆破

查看源码
php
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 安全改进:添加了CSRF令牌验证,有助于防止跨站请求伪造攻击
// 注意:需确认checkToken()实现是否安全,以及令牌生成逻辑是否可靠
// Sanitise username input
$user = $_GET[ 'username' ];
// 安全风险:使用GET方法传输用户名和密码,敏感信息会暴露在URL中
$user = stripslashes( $user );
// 安全问题:stripslashes()仅移除反斜杠,不能有效防止SQL注入
// 注意:与mysqli_real_escape_string()配合使用可能产生意外结果
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// 安全风险:仍依赖字符串拼接构建SQL查询,mysqli_real_escape_string()防护有限
// 建议:使用参数化查询(prepared statements)彻底防止SQL注入
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// 安全风险:密码哈希使用MD5算法,安全性极低,易被破解
// 安全风险:未使用盐值(salt)增强密码哈希安全性
// 建议:使用password_hash()和password_verify()函数
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
// 安全风险:通过字符串拼接构建SQL查询,仍存在SQL注入风险
// 即使使用了转义函数,在特定编码或场景下仍可能被绕过
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// 安全风险:数据库错误直接显示给用户,可能泄露数据库结构等敏感信息
// 建议:生产环境中记录错误日志,不向用户展示具体错误信息
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
// 安全风险:直接输出$user变量,未进行HTML转义,可能存在XSS攻击
// 建议:使用htmlspecialchars($user, ENT_QUOTES)转义输出
echo "<img src=\"{$avatar}\" />";
// 安全风险:直接使用数据库中的$avatar路径,若该值用户可控则存在XSS风险
// 建议:验证路径合法性并使用htmlspecialchars()转义
// 安全风险:缺少会话管理机制,登录状态无法在后续请求中保持
// 建议:使用session_start()初始化会话并存储用户认证信息
}
else {
// Login failed
sleep( rand( 0, 3 ) );
// 安全改进:使用随机延迟替代固定延迟,降低暴力破解效率
// 仍存在:成功与失败响应时间差异可能被利用的风险
echo "<pre><br />Username and/or password incorrect.</pre>";
}
// 代码问题:数据库连接关闭方式过于复杂,可简化为mysqli_close($GLOBALS["___mysqli_ston"])
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
// 注意:需确认generateSessionToken()生成的令牌是否足够随机且安全
?>
原理就是 token是前端生成的所以我们可以提前获取他的值(利用的就是token的复用 我们上一次访问失败之后可以把他的token搞到下一次去使用 这样就可以使用这个token去爆破)但是现实场景我们是不知道他的token的 无法提取获取。总的来说就是下面两点:
令牌暴露在前端: 无论后端如何生成,令牌最终会作为 HTML 内容返回给前端(否则前端无法提交),攻击者可通过访问页面直接提取。
复用无限制:代码未实现 "一次有效" 机制,同一令牌可在同一会话中反复使用,无需每次重新获取,失败不失效。
Impossible
查看源码
php
<?php
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
// 安全改进:使用POST方法传输登录数据(用户名、密码)
// 相比GET方法,POST数据在URL中不可见,避免了浏览器历史、服务器日志泄露敏感信息的风险
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 保留并延续了CSRF令牌验证机制,有效防御跨站请求伪造攻击
// 确保登录请求来自当前用户的合法会话,而非第三方伪造
// Sanitise username input
$user = $_POST[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_POST[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// 注:MD5哈希密码仍是安全短板,但本版代码的核心改进在其他维度
// Default values
$total_failed_login = 3; // 设定最大失败登录次数(3次)
$lockout_time = 15; // 设定账户锁定时长(15分钟)
$account_locked = false;
// 安全改进:新增账户锁定机制的基础配置,从源头限制暴力破解频率
// Check the database (Check user information)
$data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// 安全改进:使用PDO参数化查询(prepare + bindParam)
// 彻底杜绝SQL注入风险!参数化查询将用户输入与SQL语句逻辑分离,输入无法被解析为SQL命令
// Check to see if the user has been locked out.
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {
// 安全改进:实现账户锁定逻辑------当失败次数达到阈值时,触发锁定判断
// 注:此处虽有用户枚举风险(可通过返回信息判断用户名是否存在),但核心锁定功能已生效
// Calculate when the user would be allowed to login again
$last_login = strtotime( $row[ 'last_login' ] );
$timeout = $last_login + ($lockout_time * 60);
$timenow = time();
// Check to see if enough time has passed, if it hasn't locked the account
if( $timenow < $timeout ) {
$account_locked = true;
// 安全改进:锁定逻辑生效------在锁定时长内,即使密码正确也无法登录
// 有效阻止"短时间高频次暴力破解",大幅提升破解成本
}
}
// Check the database (if username matches the password)
$data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR);
$data->bindParam( ':password', $pass, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// 再次使用PDO参数化查询,确保密码验证环节无SQL注入风险
// 同时通过LIMIT 1优化查询效率,避免冗余数据返回
// If its a valid login...
if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
// Get users details
$avatar = $row[ 'avatar' ];
$failed_login = $row[ 'failed_login' ];
$last_login = $row[ 'last_login' ];
// Login successful
echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
echo "<img src=\"{$avatar}\" />";
// Had the account been locked out since last login?
if( $failed_login >= $total_failed_login ) {
echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>{$last_login}</em>.</p>";
}
// 安全改进:新增安全预警------当用户登录时,提示之前存在暴力破解尝试
// 提升用户安全感知,便于用户及时修改密码等操作
// Reset bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
// 安全改进:登录成功后重置失败次数
// 确保合法用户后续登录不受之前失败记录影响,平衡安全性与用户体验
} else {
// Login failed
sleep( rand( 2, 4 ) );
// 保留随机延迟机制,避免攻击者通过响应时间差异判断"用户名/密码是否正确"
// 相比固定延迟,随机延迟更难被暴力破解工具利用
// Give the user some feedback
echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";
// 安全改进:模糊错误提示------不明确告知"是用户名错"还是"密码错",也不直接确认"账户是否存在"
// 有效降低用户枚举风险,同时告知锁定规则,提升用户体验
// Update bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
// 安全改进:登录失败后累加失败次数
// 为后续账户锁定逻辑提供数据支撑,形成"失败-累加-锁定"的完整防御链
}
// Set the last login time
$data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
// 安全改进:记录每次登录(无论成功/失败)的时间
// 一方面用于账户锁定的超时计算,另一方面可作为审计日志,便于后续安全分析
}
// Generate Anti-CSRF token
generateSessionToken();
// 延续令牌生成机制,确保每次页面加载都有有效令牌,维持CSRF防御能力
?>
- 彻底防御 SQL 注入:全面采用 PDO 参数化查询,替代字符串拼接,从根源杜绝注入风险;
- 强化暴力破解防御:新增 "失败次数累加 + 超时锁定" 机制,大幅提升破解成本,同时保留随机延迟;
- 优化敏感数据传输:改用 POST 方法传输用户名 / 密码,避免 URL 泄露;
- 降低信息泄露风险:模糊错误提示,减少用户枚举可能;
- 增强用户安全感知:登录成功时提示暴力破解预警,同时重置失败次数,平衡安全与体验;
- 完善审计与配置:记录登录时间,支持可配置的锁定阈值与时长,便于维护与扩展。
对于制作靶场的作者当时来说md5已经十分牢固,但是随着时代发展,md5变得不那么牢固了,但是这仍然是优秀代码的典范