DVWA sqli淺析
0x00
首先簡單介紹一下DVWA。
Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is damn vulnerable.
GitHub - RandomStorm/DVWA: Damn Vulnerable Web Application (DVWA)
簡單來說就是一個web漏洞實驗環境,可以用來練習web向各種漏洞的利用。
第一次寫專欄,這一篇試試水,知乎上大牛很多,如果有寫的不對的地方,還請各位大牛指導!
0x01
環境怎麼安裝就不啰嗦了,可以去官網或者github上查找教程。
Sqli的實驗有三種難度,low,medium和high。
實驗的目的是要我們通過sqli漏洞,從資料庫中拿到各個賬號的密碼。
0x02
從low的看起。
一般我們測試sql注入的注入點,通常是在參數值後面加一個單引號,如果這時候資料庫報錯,就可以確認此處有注入。
正常情況下,參數為id,然後後台將id的值放入sql語句中查詢,然後返回查詢的結果
我們在id欄位的值後面添加一個單引號測試,發現sql語法錯誤,於是我們可以確定,此處存在注入點。
於是開始琢磨怎麼拿數據,方法有很多種,常見的有利用union select,報錯注入,時間盲注,布爾盲注,思路和步驟上大體差不多,只是具體的payload和適用環境有所區別。
以下以用union select為例,簡單說明一下注入思路。
- 確定查詢欄位數。
- 確定資料庫名。
- 確定表名。
- 確定列名。
- 導出數據。
在提交payload的時候,我們需要用注釋符注釋掉源代碼中payload之後的sql語句,從而避免語法錯誤。mysql的注釋符有#和--,用#注釋的時候需要用url編碼,用--的時候可以不編碼,但是要在之後加一個空格,或者加號(本質上都是空格)。
order by 3時報錯,order by 2時正確回顯。可以得出應該是兩列。
確立欄位數之後我們需要確立回顯點。用union select 1,2 來確定查詢的數據會回顯在頁面的什麼位置上。
看下圖,第二條查詢記錄中first name和last name之後的1,2就代表回顯位置,這裡的1,2的意思是select的第一列和第二列。並且這個環境這裡是簡單的模式,通常遇到的情況是每次回顯只能回顯一條select的結果,那這種情況下,就要使原本的查詢無結果,比如此處另id=0,從而使union select的查詢結果存放在第一位顯示。
接下來都是固定格式的payload,注資料庫名。如果環境是每次只能顯示一條查詢結果的話,通過後面添加limit子句逐條將名稱爆出。表名同理。欄位名。然後就可以注數據了,利用payload:union select 欄位名 1,欄位名2 from 資料庫名.表名 limit ...的格式逐個注出用戶名,密碼。
這裡發現密碼都是MD5存儲的,感興趣的可以爆一爆.比如admin的MD5值是5f4dcc3b5aa765d61d8327deb882cf99
解密一下,結果是password
在存在回顯的情況下,還有一種常見的注入方式是報錯注入(double query)。利用sql錯誤發生時的回顯將數據注出來。剩下的兩種盲注我們暫時不說。DVWA中有另外一個單獨的sql盲注專題。盲注通常使用在不管sql語句對錯與否都不發生回顯的狀況下。
這裡報錯信息會回顯,所以我們講一下報錯注入,這種注入的payload格式也比較固定。payload如下:
and(select 1 from(select count(*),concat((select (select (語句)) from information_schema.tables limit 0,1),0x7e,floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
我們在本實驗環境下用這種方法來試一試結果。
以注資料庫名為例。
表名欄位名及數據後不贅述,大家感興趣的可以自己實驗,payload結合上面這條和之前用union注入所用的格式即可。
當存在注入點的時候,我們還可以通過一些sql自帶的函數干一些其他羞羞的事情。
- user() 獲取資料庫用戶名
- database() 獲取當前資料庫名
- version() 獲取MYSQL資料庫版本
- load_file() MYSQL讀取本地文件的函數
- @@datadir 讀取資料庫路徑
- @@basedir MYSQL 安裝路徑
- @@version_compile_os 操作系統
- into_outfile() 寫一句話木馬
0x03
然後我們來看medium難度下的實驗環境。我們還是試探性用單引號測試。結果報錯了。報錯信息中我們發現單引號前面存在,於是猜測單引號被轉義了,主機側代碼可能做了addslashes().為了更好的解釋問題,此處我們直接查看源代碼。$id = $_GET["id"];$id = mysql_real_escape_string($id);$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
這裡對傳入的參數進行了叫做mysql_real_escape_string()的預處理操作,這個函數可以轉義 SQL 語句中使用的字元串中的特殊字元。所以我們看到,單引號被轉義了,沒有辦法構成代碼。
那麼怎麼辦呢?
我們仔細看發現,這裡的id參數是直接引用的,並沒有加任何的引號,所以get請求中id後面的所有內容,可以直接參与sql語句的構建。比如如果id的內容是id = 0 union select database(),user().那拼接到php代碼中就是
select first_name,last_name from users where user_id=0 union select database(),user().
這完全是一條合法的sql語句,它甚至都不用在payload的結尾加上注釋符。所以實質上這裡的mysql_real_escape_string()並沒有構成任何的防禦作用,這個函數只有當參數被引號包圍起來的時候,才起作用(當然也能突破,這個我們後面再說)。
所以我們還是順著上一章提到的思路,確定欄位數,確定回顯位置,爆資料庫名,爆表名,爆欄位名,爆數據內容。
在這些步驟中,可能會遇到一丟丟小的麻煩,那就是當我們爆表名的時候,payload中where子句需要明確表所在的資料庫的名字,這個參數是用單引號引起來的,那麼引號既然被轉義了,我們怎麼才能把這個參數弄進去呢?
這裡我們可以將資料庫的名稱用十六進位表示,來繞過引號,資料庫同樣是可以解析的。成功繞過,後面列名同理。用引號引起來的數據都可以用16進位表示來繞過這個限制。在平常的學習實驗還有ctf中,大多注入題都會存在一些防禦手段,基本都是針對payload的關鍵字進行過濾。
- 過濾select等關鍵字
- 過濾空格
- 轉義單引號
以下是一些繞waf的思路和總結:
- waf 繞過的技巧
- 深入了解SQL注入繞過waf和過濾機制
- Bypass WAF Cookbook
0x04
終於到了搞基的部分,啊不,是高級的部分。
直接看代碼。這段代碼我看了很久很久很久,都沒想出來怎麼破,實在沒有辦法谷歌了一下。
然後查到的結果說,這段代碼就是沒有辦法注入的,它代表著平台作者所認為的sql注入的正確防禦姿勢。
<?php if (isset($_GET["Submit"])) { // Retrieve data $id = $_GET["id"]; $id = stripslashes($id); $id = mysql_real_escape_string($id); if (is_numeric($id)){ $getid = "SELECT first_name, last_name FROM users WHERE user_id = "$id""; $result = mysql_query($getid) or die("<pre>" . mysql_error() . "</pre>" ); $num = mysql_numrows($result); $i=0; while ($i < $num) { $first = mysql_result($result,$i,"first_name"); $last = mysql_result($result,$i,"last_name"); echo "<pre>"; echo "ID: " . $id . "<br>First name: " . $first . "<br>Surname: " . $last; echo "</pre>"; $i++; } }}?>
我們來簡單分析一下,防禦步驟一共有三步。
- 用stripslashes()去掉參數中的斜杠
- 用mysql_real_escape_string()將參數中的特殊字元轉義
- 用is_numeric()判斷參數是否為數字型,是則進行查詢,否則drop.
谷歌下有人提到sqlmap在此處可以注出數據,但是sqlmap是有緩存的,當url在它的記錄中,它會直接返回緩存中的結果,也就是low,medium難度下得到的結果,並不是在high level下獲取的數據。
0x05
DVWA上關於sql注入的還有一個盲注的篇章,下次有機會再分享。
在實際中,sql還有很多其他的形式
- insert,update,delete語句的注入
- 存儲型的二次注入
- stacked注入
另外一個專門的sqli練習平台上有很好的歸類,大家有興趣的可以自己安裝來練習。
GitHub - Audi-1/sqli-labs: SQLI labs to test error based, Blind boolean based, Time based.
第一次做這樣的分享,肯定有很多寫的不好的地方,還請各位大神多多指正。這兩個虛擬機,有感興趣的朋友願意一起玩兒的,歡迎交流。
推薦閱讀:
※為什麼信用卡在銷卡之後要將磁條剪斷?
※「點開我的鏈接我就能控制你的電腦」之Facebook Messenger版(需安裝軟體)
※谷歌更新Gmail郵箱安全功能