DVWA安装使用介绍,见:【工具-DVWA】DVWA的安装和使用
本渗透系列包含最新DVWA的14个渗透测试样例:
1.Brute Force(暴力破解)
2.Command Injection(命令注入)
3.CSRF(跨站请求伪造)
4.File Inclusion(文件包含)
5.File Upload(文件上传)
6.Insecure CAPTCHA(不安全的验证码)
7.SQL Injection(SQL注入)
8.SQL Injection(Blind)(SQL盲注)
9.Weak Session IDs(有问题的会话ID)
10.XSS(DOM)(DOM型xss)
11.XSS(ref)(反射型xss)
12.XSS(Stored)(存储型xss)
13.CSP Bypass(Content Security Policy内容安全策略,旁路/绕过)
14.JavaScript
安全级别分低、中、高、安全四个级别来分析Brute Force的渗透测试过程。
使用账号【admin' OR '1'='1】+正确密码【password】,发现登陆失败
使用账号【admin' OR '1'='1】+空密码【】或者错误密码 ,发现登陆成功
使用账号【admin' OR '1'='2】+任何密码,都能登陆成功
咦?这是为何呢?找源码看看。
网站路径:DVWA-master\vulnerabilities\brute下面有源码
index.php:根据安全级别,来选择使用的php代码。
switch( $_COOKIE[ 'security' ] ) {
case 'low':
$vulnerabilityFile = 'low.php';
break;
case 'medium':
$vulnerabilityFile = 'medium.php';
break;
case 'high':
$vulnerabilityFile = 'high.php';
break;
default:
$vulnerabilityFile = 'impossible.php';
$method = 'POST';
break;
}
low.php:发现SQL执行使用的拼接,且对参数username、password没有做任何过滤,存在SQL注入漏洞和Brute Force漏洞。
//用来检测变量是否设置
if( isset( $_GET[ 'Login' ] ) ) {
// Get username
$user = $_GET[ 'username' ];
// Get password
$pass = $_GET[ 'password' ];
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
//当查询结果为1个,则认为登陆成功
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Login successful
$html .= "Welcome to the password protected area {$user}
";
$html .= "
";
}
else {
// Login failed
......
}
......
}
回到之前的问题,为何使用账号【admin' OR '1'='1】+正确密码【password】,会登陆失败。
执行SQL,发现使用password的密码的用户有2个,当查询结果不为1个时都认为登陆失败:
PS:所以,一般SQL注入时,会使用空密码,来避免因密码重复而导致认证失败。
当用户名为【admin' OR '1'='2】时,由于AND优先级高于OR,所以,OR之后的条件相当于不存在,所以只能查询出admin一个用户出来,这个和password不正确的情况是一样的。
发现:依然可以成功,但是,慢了很多。
发现:无效!!
medium.php:与low相比,添加了mysql_real_escape_string函数,来对特殊字符进行转义;同时登陆失败会休眠2秒
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
//对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义
$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' ];
//对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义
$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 );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
$html .= "Welcome to the password protected area {$user}
";
$html .= "
";
}
else {
// Login failed 登陆失败休眠2秒
sleep( 2 );
$html .= "
Username and/or password incorrect.
";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
PS:mysql_real_escape_string函数可以绕过哟,sleep(2)休眠时间真是短,贴心......
发现:请求中多了一个参数user_token,按原来的暴力请求,会返回CSRF token不正确的异常。
发现:页面中多了一个隐藏字段user_token,它会随着请求提交,且每次登陆后,该值会变化
1、抓取请求,发送到Intruder,选择Password和user_token,使用Pitchfork(一一映射方式),即每次登陆密码都对应新的user_token
2、线程数修改为1,因为一个user_token只能支持一次登陆测试,只要测试过一次,那么token就会失效。
3、从页面中抓取user_token
点击Refetch response,然后双击你需要抓取的内容,burp会自动帮你填充start和end的内容:
4、Payload1设置照旧,payload2,选择Recursive grep,payload内容会自动加载刚刚配置的截取规则,first request需要填写最新的user_token,去页面里看代码或者使用刚刚response里的value(在这之后没有使用过这个token)。
5、测试成功,user_token会自动截取页面的值,进行请求:
high.php:与medium相比主要就多了一个CSRF-token的效验和生成,还有就是stripslashes去掉反斜线,避免转义绕过
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token,验证CSRF-token是否正确
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ '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 = $_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)) ? "" : ""));
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Login successful
$html .= "Welcome to the password protected area {$user}
";
$html .= "
";
}
else {
// Login failed,登陆失败休眠0-3秒
sleep( rand( 0, 3 ) );
$html .= "
Username and/or password incorrect.
";
}
}
// Generate Anti-CSRF token,产生CSRF-token
generateSessionToken();
发现没效果......
核心代码:限制登陆失败次数,以及账号锁定机制,同时使用了更安全的PDO机制,来限制传参执行数据库操作。
// Default values
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
// Check the database (Check user information),不能使用PDO扩展本身执行任何数据库操作
$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();
// Check to see if the user has been locked out.当登陆失败次数超过3次,将锁定账号15分钟
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {
// User locked out. Note, using this method would allow for user enumeration!
//$html .= "
This account has been locked due to too many incorrect logins.
";
// Calculate when the user would be allowed to login again
$last_login = strtotime( $row[ 'last_login' ] );
$timeout = $last_login + ($lockout_time * 60);
$timenow = time();
/*
print "The last login was: " . date ("h:i:s", $last_login) . "
";
print "The timenow is: " . date ("h:i:s", $timenow) . "
";
print "The timeout is: " . date ("h:i:s", $timeout) . "
";
*/
// Check to see if enough time has passed, if it hasn't locked the account,
if( $timenow < $timeout ) {
$account_locked = true;
// print "The account is locked
";
}
}
暴力破解可应用场景:
登陆渗透过程:
爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!