DVWA教程

DVWA(Damn Vulnerable Web Application)是一个用于安全测试的PHP/MySQL Web应用,旨在帮助安全专家和开发者提升技能和测试工具。本文详细介绍了DVWA中的Brute Force、Command Injection、CSRF、File Inclusion、File Upload、Insecure CAPTCHA、Weak Session IDs、SQL Injection(包括Blind SQL注入)和XSS(包括Reflected和Stored)等多个安全漏洞,各个级别从low到impossible的防护措施和漏洞利用方法。通过对不同级别的代码分析,揭示了如何防范这些常见Web安全威胁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DVWA 简介

DVWA(Damn Vulnerable Web Application)一个用来进行安全脆弱性鉴定的PHP/MySQL Web 应用,旨在为安全专业人员测试自己的专业技能和工具提供合法的环境,帮助web开发者更好的理解web应用安全防范的过程。

Brute Force(暴力(破解))

Brute Force,即暴力(破解),是指黑客利用密码字典,使用穷举法猜解出用户口令,是现在最为广泛使用的攻击手法之一,如2014年轰动全国的12306“撞库”事件,实质就是暴力破解攻击。
在这里插入图片描述下面将对四种级别的代码进行分析:

low等级

对爆破攻击行为毫无设防。
代码:

<?php

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( '<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>";
        echo "<img src=\"{
     $avatar}\" />";
    }
    else {
   
        // Login failed
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

isset函数用来检测变量是否设置,并且不是 NULL
用户可以完全控制该参数,传参时给Login赋值即可满足条件继续执行
没有任何的防爆破机制,且对参数username、password没有做任何过滤和检查
用户输入的用户名将原封不动传递到SQL语句中
用户输入的密码将进行md5散列后传递到SQL语句中

漏洞利用 —利用burpsuite爆破

  1. 设置代理,抓包:
    在这里插入图片描述2. 添加到intruder模块,选择要爆破的参数

    	Sniper 单参数爆破,多参数时同一个字典按顺序替换各参数,总之初始值有一个参数不变
    	Battering ram 多参数同时爆破,但用的是同一个字典
    	Pichfork 多参数同时爆破,但用的是不同的字典
    	Cluster bamb 多参数做笛卡尔乘积模式爆破
    

    在这里插入图片描述3. 选中Payloads,载入字典,点击Start attack进行爆破
    在这里插入图片描述4.根据响应包长度可推测admin为正确用户,password为正确密码,手工验证登陆成功。
    在这里插入图片描述

medium等级

对爆破攻击行为防护不足,防护做法欠考虑。
代码:

<?php

if( 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
        echo "<p>Welcome to the password protected area {
     $user}</p>";
        echo "<img src=\"{
     $avatar}\" />";
    }
    else {
   
        // Login failed
        sleep( 2 );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

根据代码可以得知
isset函数用来检测变量是否设置,并且不是 NULL。
用户可以完全控制该参数,传参时给Login赋值即可满足条件继续执行。
用户名部分使用mysqli_real_escape_string(str)函数用户名的特殊符号(\x00,\n,\r,\,‘,“,\x1a)(ascii码0,换行,回车,回退)进行转义,除宽字节注入外,可以抵抗其余SQL注入。
用户输入的密码将进行md5散列后传递到SQL语句中。
如果密码输错了,则延时两秒之后才能再次提交。
漏洞利用
爆破部分尝试与low代码部分一样。

hight等级

对爆破攻击行为有一定防护,但有疏忽。
代码:

<?php

if( isset( $_GET[ 'Login' ] ) ) {
   
    // Check Anti-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( '<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>";
        echo "<img src=\"{
     $avatar}\" />";
    }
    else {
   
        // Login failed
        sleep( rand( 0, 3 ) );
        echo "<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,每次都需要更新token。
.用户名部分使用,stripslashes(str)函数去除用户名中出现的反斜线。然后再使用mysqli_real_escape_string(str)函数用户名的特殊符号(\x00,\n,\r,\,‘,“,\x1a)(ascii码0,换行,回车,回退)进行转义,完全抵抗SQL注入。
用户输入的密码将进行md5散列后传递到SQL语句中。
如果密码输错了,则延时0-3秒之后才能再次提交。
漏洞利用
抓包,发现有多个参数(减小难度,默认用户已知为admin )
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200805202854825.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JhaWR1XzI5MjQ0OTMx,size_16,color_FFFFFF,t_70
查看页面源代码,发现user_token
在这里插入图片描述
第一个变量 password 的值直接载入字典
在这里插入图片描述第二个变量 user_token
option 选项中找到 Grep-Extract ,点击 Add, 然后点击 Refetch repose 即可看到抓取到的源代码了,我们找到 user_token 的值,选中它,发现红色方框中出现了变量值两边的内容,之后点击ok。
在这里插入图片描述回到 Payloads 加载,注意每个变量都需加载各自的字典。
第一个变量按照上面步骤即可,第二个变量找到 Recursive grep 选项,
首次的token值需要手动加进去。
在这里插入图片描述此外,recursive grep payloads cannot be used with multiple request threads ,所以我们需要将线程设置为1.
在这里插入图片描述Options 中找到 Redirections 设置为 Always,
在这里插入图片描述进行爆破:
在这里插入图片描述

impossible等级

对爆破攻击行为正确防护。
代码:

<?php

if( isset( $_POST[ 'Login' ] ) ) {
   
    // Check Anti-CSRF token
    checkToken( $_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!
        //echo "<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 = $row[ 'last_login' ];
        $last_login = strtotime( $last_login );
        $timeout    = strtotime( "{
     $last_login} +{
     $lockout_time} minutes" );
        $timenow    = strtotime( "now" );

        // 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();

    // 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();

?> 

漏洞利用
impossible级别在 high 的基础上对用户的登录次数有所限制,当用户登录失败达到3次,将会锁住账号爆破也就无法继续,同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入

Command Injection(命令行注入)

Command Injection,即命令注入,是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。PHP命令注入攻击漏洞是PHP应用程序中常见的脚本漏洞之一,国内著名的Web应用程序Discuz!、DedeCMS等都曾经存在过该类型漏洞。

在这里插入图片描述

命令执行漏洞的产生原因一般就是将用户输入未经过滤或者过滤不严就直接当作系统命令进行执行,我们可以通过批处理中的一些技巧来一次执行多条命令,这样就可以执行任意命令。在命令执行中,常用的命令连接符号有五个:

&&:前一个指令执行成功,后面的指令才继续执行,就像进行与操作一样

||:前一个命令执行失败,后面的才继续执行,类似于或操作

&:直接连接多个命令,同时执行

|:管道符,将前一个命令的输出作为下一个命令的输入

;:直接连接多个命令,不管前面命令执行成功没有,后面的命令继续执行

CTF或渗透测试中常用于进行命令注入攻击拼接的命令:

ls :列出当前文件夹的内容;

sleep 5:观察是否存在时间差来检测是否存在漏洞;

whoami:当前的用户;

cat /etc/shadow:影子文件,存储linux中用户的密码信息

ls -alh /home/:查看用户

ls -alh /home/用户名/ : 查看具体用户的目录

usname -a:查看系统信息
low等级

代码:

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
   
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
   
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
   
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{
     $cmd}</pre>";
}

?> 
  • php_uname(mode)
    这个函数会返回运行php的操作系统的相关描述,参数mode可取值”a” (此为默认,包含序列”s n r v m”里的所有模式),”s ”(返回操作系统名称),”n”(返回主机名),” r”(返回版本名称),”v”(返回版本信息), ”m”(返回机器类型)。
  • stristr(string,search,before_search)
    stristr函数搜索字符串在另一字符串中的第一次出现,返回字符串的剩余部分(从匹配点),如果未找到所搜索的字符串,则返回 FALSE。参数string规定被搜索的字符串,参数search规定要搜索的字符串(如果该参数是数字,则搜索匹配该数字对应的 ASCII 值的字符),可选参数before_true为布尔型,默认为“false” ,如果设置为 “true”,函数将返回 search 参数第一次出现之前的字符串部分。
    该函数是不区分大小写的。如需进行区分大小写的搜索,请使用 strstr() 函数
  • linux和windows 都 可以用 && 、 & 、; 等 执行其他 命令

漏洞利用
&&来执行多条命令语句
127.0.0.1&&ipconfig
在这里插入图片描述

medium等级

代码:

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
   
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Set blacklist
    $substitutions = array(
        '&&' => '',
        ';'  => '',
    );

    // Remove any of the charactars in the array (blacklist).
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
   
        // Windows
        $cmd = shell_
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值