当前位置: 首页 > news >正文

DVWA靶场-暴力破解

DVWA是一个适合新手锻炼的靶机,是由PHP/MySQL组成的 Web应用程序,帮助大家了解web应用的攻击手段

DVWA大致能分成以下几个模块,包含了OWASP Top 10大主流漏洞环境。

Brute Force——暴力破解
Command Injection——命令注入
CSRF——跨站请求伪造
File Inclusion——文件包含
File Upload——文件上传漏洞
Insecure CAPTCHA——不安全的验证
SQL Injection——sql注入
SQL Injection(Blind)——sql注入(盲注)
Weak Session IDs——不安全的SessionID
XSS(DOM)——DOM型XSS  
XSS(Reflected)——反射型XSS
XSS(Stored)——存储型XSS
CSP Bypass ——绕过内容安全策略
JavaScript——JS攻击

本文所用dvwa版本为2.3

GitHub - digininja/DVWA: Damn Vulnerable Web Application (DVWA)

可以自行通过phpstudy搭建以供练习

DVWA Security分为四个等级,low,medium,high,impossible,不同的安全等级对应了不同的漏洞等级。
1.low-最低安全等级,无任何安全措施,通过简单毫无安全性的编码展示了安全漏洞。
2.medium-中等安全等级,有安全措施的保护,但是安全性并不强。
3.high-高等安全等级,有安全措施保护,是中等安全等级的加深,这个等级下的安全漏洞可能不允许相同程度的攻击。
4.impossible-最高(不可能)安全等级,存在相当强的安全保护措施,这个级别应能防止所有的漏洞,主要用于比较不同等级下的源代码。

开此篇文章的主要目的是为了对代码进行分析,查看其应对攻击的方法及绕过手法

进入暴力破解的界面,有点基础的都知道,在bp中通过字典,导入用户名和密码进行爆破,这样的操作大家见多了,再写没啥意思,想看的可以去pikachu靶场的暴力破解去了解具体操作,本文主要结合代码进行分析

pikachu靶场-暴力破解-CSDN博客

low等级
<?phpif( isset( $_GET[ 'Login' ] ) ) {    // Get username$user = $_GET[ 'username' ];    //通过get方式获取username// Get password$pass = $_GET[ 'password' ];    //通过get方式获取password$pass = md5( $pass );            //收到密码后进行md5加密// Check the database$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 ) {//mysqli_num_rows()函数是一个用于获取查询结果中行数的mysqli扩展函数。它接受一个查询结果对象作为参数,并返回该结果集中的行数。通常用于检查查询结果是否包含了符合条件的记录,以便在代码中进行相应的处理。// Get users details$row    = mysqli_fetch_assoc( $result );$avatar = $row["avatar"];// Login successful$html .= "<p>Welcome to the password protected area {$user}</p>";$html .= "<img src=\"{$avatar}\" />";}else {// Login failed$html .= "<pre><br />Username and/or password incorrect.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>

以上是一个简单的PHP登录验证功能。当用户提交登录表单时,通过GET方法获取用户名和密码,然后将密码进行MD5加密。接着查询数据库,检查用户名和加密后的密码是否匹配数据库中的记录。如果匹配成功,则显示用户的欢迎信息和头像;如果匹配失败,则显示错误信息。

无任何防护,直接进行暴力破解即可

medium等级
<?phpif( isset( $_GET[ 'Login' ] ) ) {// Sanitise username input$user = $_GET[ 'username' ];$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 = ((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( '<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$html .= "<p>Welcome to the password protected area {$user}</p>";$html .= "<img src=\"{$avatar}\" />";}else {// Login failedsleep( 2 );$html .= "<pre><br />Username and/or password incorrect.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>

相比于low的代码,这里获取到username和password后,有一段转义的代码

	$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()函数对用户输入的用户名进行转义,确保特殊字符在SQL语句中不会被误解为SQL语句的一部分,从而保护数据库安全。

加了这个转义只是对sql注入进行防范,对暴力破解还是没有起到效果

high等级
<?phpif( isset( $_GET[ 'Login' ] ) ) {// Check Anti-CSRF tokencheckToken( $_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( '<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$html .= "<p>Welcome to the password protected area {$user}</p>";$html .= "<img src=\"{$avatar}\" />";}else {// Login failedsleep( rand( 0, 3 ) );$html .= "<pre><br />Username and/or password incorrect.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}// Generate Anti-CSRF token
generateSessionToken();?>

这一等级开始,登录设置了token

选中这个checktoken函数,跳转到这个函数

// Token functions --
function checkToken( $user_token, $session_token, $returnURL ) {  # Validate the given (CSRF) tokenglobal $_DVWA;if (in_array("disable_authentication", $_DVWA) && $_DVWA['disable_authentication']) {return true;}if( $user_token !== $session_token || !isset( $session_token ) ) {dvwaMessagePush( 'CSRF token is incorrect' );dvwaRedirect( $returnURL );}
}

跟进到checkToken方法,设置了三个参数,如果user_token不等于session_token或者session_token为空,调用dvwaRedirect方法。

function dvwaRedirect( $pLocation ) {session_commit();header( "Location: {$pLocation}" );exit;
}

这段代码定义了一个名为dvwaRedirect()的函数,用于实现页面重定向的功能。函数接受一个参数$pLocation,即要重定向到的目标位置。

在函数内部,首先调用session_commit()函数来提交并保存当前会话的所有数据。然后通过header()函数设置HTTP响应头,将浏览器重定向到指定的目标位置。最后使用exit函数来终止脚本的执行,确保页面重定向的立即生效。

这个函数通常用于在应用程序中需要进行页面跳转的情况,如登录成功后跳转到用户首页,或者在某些条件下需要跳转到其他页面。通过调用这个函数,可以实现简单的页面重定向功能。

可以看到这个提交界面多了一串user_token,并且每次刷新都重新生成,pikachu靶场的那篇文章有演示在前端发现token怎么进行爆破操作

从medium等级开始,登录失败就会延时,提高了暴力破解的成本

impossiable等级
<?phpif( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// 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 );// Default values$total_failed_login = 3;$lockout_time       = 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();// Check to see if the user has been locked out.if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {// User locked out.  Note, using this method would allow for user enumeration!//$html .= "<pre><br />This account has been locked due to too many incorrect logins.</pre>";// 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) . "<br />";print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";*/// Check to see if enough time has passed, if it hasn't locked the accountif( $timenow < $timeout ) {$account_locked = true;// print "The account is locked<br />";}}// 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();// 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$html .= "<p>Welcome to the password protected area <em>{$user}</em></p>";$html .= "<img src=\"{$avatar}\" />";// Had the account been locked out since last login?if( $failed_login >= $total_failed_login ) {$html .= "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";$html .= "<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 failedsleep( rand( 2, 4 ) );// Give the user some feedback$html .= "<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();?>

这是最高难度的代码,它主要采取了以下几种措施来防止暴力破解

POST方式获取用户名密码

设置token

对sql语句进行转义

PDO预编译

设置登录次数,超过次数进行锁定

登录失败后延时

 一般来说,这种情况进行爆破是不可能的了,或者说成本过高

相关文章:

  • 【Java EE】线程安全的集合类
  • STM32点亮LED灯与蜂鸣器发声
  • .net6Api后台+uniapp导出Excel
  • @Autowired 与@Resource的区别
  • <机器学习初识>——《机器学习》
  • 第十三届蓝桥杯嵌入式省赛程序设计详细题解
  • openvpn证书过期解决
  • SingleSpa微前端基本使用以及原理
  • vue的导入
  • HTML 01
  • requests模块的其他方法
  • HTML静态网页成品作业(HTML+CSS)——电影网首页网页设计制作(1个页面)
  • 力扣hot100:76.最小覆盖子串(滑动窗口)
  • Android UI:ViewTree中的操作
  • 惬意上手Redis
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 2018一半小结一波
  • Brief introduction of how to 'Call, Apply and Bind'
  • Bytom交易说明(账户管理模式)
  • CSS相对定位
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • HTTP--网络协议分层,http历史(二)
  • Js基础知识(四) - js运行原理与机制
  • Mybatis初体验
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • Python打包系统简单入门
  • socket.io+express实现聊天室的思考(三)
  • Vue 动态创建 component
  • 服务器之间,相同帐号,实现免密钥登录
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 如何实现 font-size 的响应式
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 用jquery写贪吃蛇
  • Java数据解析之JSON
  • postgresql行列转换函数
  • 浅谈sql中的in与not in,exists与not exists的区别
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • #微信小程序:微信小程序常见的配置传旨
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (二)Linux——Linux常用指令
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (六)激光线扫描-三维重建
  • (生成器)yield与(迭代器)generator
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .NET CLR基本术语
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET 发展历程
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径