SQL注入詳細講解概括-GET注入、POST注入、HEAD注入
1、SQL注入流程
2、GET注入
3、POST注入
4、HEAD注入
一、SQL注入流程
1、SQL注入流程
· 尋找注入點—與數據庫交互的地方,比如登錄框,搜索框、URL地址欄、登陸界面、留言板等等
· 判斷是否存在注入點,判斷數據庫類型,確定注入方法
· 構造特殊語句,查詢數據庫數據
二、GET注入
1、What is GET注入
要搞懂GET注入之前,先搞懂什么是GET傳參。
GET傳參:用戶輸入的內容參數會被傳到地址欄(URL欄),是通過GET的方式進行傳參
· 特點:傳參內容可見,傳參長度有限,標識“?”,輸入的內容會可能被url編碼
GET注入:通過GET傳參的方式,傳輸惡意語句,進行SQL注入
2、如何進行GET注入
一般情況判斷為傳參方式為GET傳參方式,首先進行GET注入測試,判斷是否存在GET注入
如何判斷是否存在GET注入:要想知道是否存在,首先搞原理,弄清楚是如何發生的
簡單說,原本程序要執行的代碼拼接了用戶輸入的數據然后執行,就是本來用戶輸入的數據是要被查詢的,但是被數據庫當作代碼執行
OK,AND,只需知道輸入的數據有沒有被數據庫當作代碼,可以判斷存不存在注入點
AND,用戶輸入的數據一定不是輸入everying都行的,如果用戶隨便輸入的數據都被當做代碼執行,那么這個網站就失去了它的功能,這是網站開發者不允許的
SO,要輸入什么數據才能被判斷是否存在注入點呢
先來看一段代碼
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id 用戶輸入參數傳入$id 來輸入1 >>>> $id=1 >>> sql語句就變成了下面這樣 $query = "SELECT first_name, last_name FROM users WHERE user_id = 1 ok,look look 現在數據庫收到的數據是這樣的,, SELECT first_name, last_name FROM users WHERE user_id = 1 AND,輸入什么能夠讓數據庫執行一些別的操作 思考ing time(3min) 當然是數據庫語言了,,,試一試 or 怎么樣 ok,, >>> 1 or 1=1 >>> $id=1 or 1=1 SELECT first_name, last_name FROM users WHERE user_id = 1 or 1=1 輸入的內容當作代碼執行了 //這個叫做整形閉合
在來看一段,這次 把變量用引號引起來,用單引號or用雙引號,是不一樣的破解方法
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';" 上面雙引號里的是要被傳入數據庫執行的 first,輸入的數據被傳入$id這個變量 ok,那么輸入一個 1 ,$id=1 and 此語句就變成了: SELECT first_name, last_name FROM users WHERE user_id = '1' 那么要怎么做才能變成輸入的數據變成代碼執行呢? 思考ing time(5min) ok,無論輸入什么數據他都在引號里,那輸入的數據就沒有任何意義對吧, 那就來突破引號,如何突破 ,,look here 》》 1' or 1=1 ' 》》 $id=1' or 1=1 ' 拼接試一試 SELECT first_name, last_name FROM users WHERE user_id = '1' or 1=1 '' FUCK,,有問題了,,不一樣了 //這個叫做單引號閉合
那么再來思考一個問題,當變量用雙引號引起來怎么辦,不是沒用引號,也不是單引號
思考ing time(2min)
ok,easy 那就用雙引號破解唄
SELECT first_name, last_name FROM users WHERE user_id = "$id"
look here 》》》 1" or 1=1 " 》》》 $id=1" or 1=1 "
SELECT first_name, last_name FROM users WHERE user_id = "1" or 1=1 ""
success,easy //這個叫做雙引號閉合
OK,上面是能看到后端代碼的,哪個網站願意讓你看到他的后端代碼,It won't
AND,看不到源碼,怎么進行注入呢
so easy , 通過傳入的惡意代碼,觀察頁面情況 ,是否正常等,來判斷是否存在注入點,確定注入方法
首先我們找到傳參點,進行注入 嘗試 ,首先判斷閉合類型
SQL語句的閉合類型:整型閉合、單引號閉合、雙引號閉合、單引號加括號、雙引號加括號
· 整形閉合
1
|
SELECT
*
FROM
admin
WHERE
id=1; //整形閉合
|
模擬注入
?id=1' => 報錯
?id=1'' => 報錯
判斷為整形閉合
· 單引號閉合
1
|
SELECT
*
FROM
admin
WHERE
id=‘1’; //單引號閉合
|
模擬注入
?id=1' => 報錯
?id=1'' => 正常返回id=1的值
判斷為單引號或者單引號括號閉合
任何閉合方式都這樣在沒有遇到相對應的閉合時,都會把這個符號當作一個整體,注釋符也不例外
再次模擬注入
?id=1'-+ => 無報錯 => 單引號閉合
?id=1'-+ => 報錯 => 單引號括號閉合
· 雙引號閉合
1
|
SELECT
*
FROM
admin
WHERE
id=
"1"
; //雙引號閉合
|
?id=1' => 正常返回id=1的值
?id=1" => 報錯
判斷為雙引號閉合或者雙引號括號閉合
再次模擬注入
?id=1"-+ => 無報錯 => 雙引號閉合
?id=1"-+ => 報錯 => 雙引號括號閉合
· 總結
判斷閉合類型
首先嘗試:?id=1'
?id=1"
不報錯 => 整形閉合
報錯 => 單引號報錯 ,雙引號不報錯 => 嘗試 ?id=1'-+ => 無報錯單引號閉合,報錯單引號加括號閉合
報錯 => 單引號不報錯,雙引號報錯 => 嘗試 ?id=1"-+ => 無報錯雙引號閉合,報錯雙引號加括號閉合
注入點找到,確定閉合方式開始進行注入攻擊,以DVWA靶場為例
First,觀察url欄 判斷為GET傳參,先進行正常內容輸入,輸入1 ,看返回結果
第二步,測試是否存在注入,進行惡意語句測試,不知道閉合類型,先從整形閉合開始判斷 1 and 1=1
頁面正常,與id=1 時返回的內容一樣 不確定是否注入成功,因為and 1=1 恆成立 所以還得進行第二次判斷,1 and 1=2
頁面正常,與id=1 時返回的內容一樣,and 1=2 是恆不成立的,如果數據庫執行了代碼那么應該是沒有數據返回的,現在返回了數據,so,不是整形閉合
ok,既然不是整形閉合,那么去嘗試是不是單引號閉合,輸入 1',,,把錯誤顯示出來了,說明是單引號閉合,既存在注入,又確定是單引號閉合,那么可以進行注入了。AND,我輸入的是1' 為什么url的傳參是1%27,這是因為get傳參會被url編碼,這個引號被url編碼了,HTTP請求GET請求一般要進行URL編碼,為什么呢,這是為了防止URL中的參數和HTTP中的一些參數沖突,導致奇異。
OK,下面開始SQL注入
First,已經找到注入點,判斷出閉合類型,開始 通過惡意語句來獲取數據庫信息
第一步,猜解字段數,用到語法:order by X 當X大於字段數就會顯錯,小於或等於正常返回頁面
通過order by語句查出當前表有兩個字段,為什么要先查字段數? 思考ing time(1min)
answer >>>> 因為下面要想查數據庫其他的內容,不可能再用當前表吧,得通過聯合查詢吧,那么聯合查詢的必要條件>>>多個表的字段數必須相同
第二步,判斷顯位,why?
answer >>>一個表可能有多個字段,但是我們頁面上看到的可能並不是表的字段的全部,開發者可能頁面內容只輸出指定的那幾個字段,那是不是我們就看不到其他的字段呢,so,頁面顯示並不一定是全部字段。那我們就要判斷出字段顯位,同時也輸出另一個表同樣的顯位,這樣查詢的信息才可能通過 顯位 輸出到頁面。
HOW?
answer>>>有的網站,會規定這個顯位,輸出的數據指定是多少,如果開發者指定只能輸出一條數據,那么聯合查詢好像也沒什么作用了,因為聯合查詢是先輸出前面表的數據,我們需要的是后面的表的數據,有遇到難題了。。。思考ing time(2min)
ok,用聯合查詢,當一個表里沒有查詢到數據,那么不輸出這個表的數據,,,,可不可以,前面的表不輸出數據呢。。of course
1.111111111111,1,998787,6.37176,90000.88877,,,,這些數據數據庫里可以說是百年不會見吧,那我們去查他,是不是空?? of course
ok,構建惡意語句>>>> 1.11114434' union select 1,2 #
如上圖,從第一步得知有兩個字段,用來判顯位,兩個字段內容全部顯示,當然這種情況的概率是非常小的
ok,顯位判斷完畢
第三步,查詢當前數據庫
HOW?思考ing time(1min)
database()函數
1.7274972' union select 1,database() #
ok,顯位已經判斷完畢,下面才是剛剛開始
第三步,對數據庫里的庫,表,字段,數據進行查詢
HOW?
First,需要了解數據庫,此靶場的數據庫是MySQL,后續會寫怎么識別數據庫,常見數據庫太多了,Oracle Database甲骨文公司、SQL Server微軟公司、DB2IBM公司、PostgreSQL開源、MySQL開源、Access微軟等等
mysql在5.0以上版本加入了information_schema這個系統自帶庫,其中保存着關於mysql服務器所維護的其他數據信息,如數據庫名,數據庫的表,表欄的數據類型與訪問權限,,這個庫非常牛,,數據庫所有的 庫,表,字段的位置在這張表里都能找到
information_scheam這個庫下面有幾個非常重要的表,必須知道的:
information_schema.schemata >>> 這個表保存了所有數據庫里的庫的信息
information_schema.tables >>> 這個表保存了數據庫里所有表的信息
information_schema.columns >>> 這個表保存了數據庫里所有的字段信息
AND,,
column_name 字段名
table_name 表名
schema_name 庫名
OK,基礎知識已簡單了解,怎么取通過這些知識注入呢,
思考ing time(5min)
OK,Time out,,,
Look here >>> 上一步知道了聯合查詢,對不對,要查詢數據庫信息需要知道表名呀,現在已經知道了系統自帶庫、自帶表,而且通過這幾張表我們可以得到數據庫任何數據
OK,那下面就easy了,,,構建惡意語句,先看看,當前庫存在哪些表
1.2223' union select 1,table_name from information_schema.tables where table_schema=database() #
>>>
OK,當前數據庫下的表's name有已經拿到,,定睛一看,,users,,這個表有點可疑呀,,user什么意思>>用戶
SO,我們來看一下users表里有什么東西,來,上惡意代碼
1.2333' union select 1,column_name from information_schema.columns where table_schema=database() and table_name='users' #
>>>
OK,字段名已經獲取了,是不是有兩個非常重要的數據>>>user \ password
SO,獲取user and password 兩個字段的數據 ,,,>>> 惡意代碼,,,上
1.647' union select user,password from dvwa.users #
>>>
WC,NB,信息都出來,藍框時用戶名,綠框時密碼,但仔細一看有點不對,,哪里不對
password被加密了,MD5加密,,百度搜MD5解密,,把密碼解出來,,so easy
三、POST注入
1、What is POST注入
要搞懂POST注入,先搞懂POST傳參
POST傳參:用戶輸入的內容被隱藏了起來,地址欄看不到
特點:傳參內容不可見,傳參長度無限制
POST注入:通過POST傳參的方式,傳輸惡意語句,進行SQL注入,本質和GET注入是一樣的
如上圖,所示,沒有輸入地方,只有選項框,最重要的一點是,地址欄不可見傳參內容
WC,這怎么搞,這怎么進行傳參,TMD不按套路來
那好吧,來搬個救兵吧 >>>>> BURP
BURP來抓個包吧,既然地址欄不能顯示傳參內容,抓包總可以吧
OK,,來來來,,傳參內容出現了吧,,
那就在burp里測試吧
剩下的步驟就和GET注入一模一樣了,修改傳參內容
OK,還是判斷閉合類型,這里判斷是整形閉合 >> 判斷字段數 >> 判斷顯位 >> 查詢數據庫數據 so easy
直接給出結果
OK,很簡單吧,再來一個全自動好不好,用工具Sqlmap來做,不懂原理用工具叫做Very low-end script boy 當我們懂了原理用工具那么我們就是 increase of efficiency
OK,GET注入 AND POST注入 到這就差不多了,是不是很簡單,這是最基礎的,一般網站可不會這么容易,講這個只是一個入門,告訴大家SQL注入是如何發生的,從而更好 理解高級的SQL注入,一般網站會做很多防護和過濾,,比如過濾敏感字,像and/or/#/'/" 等等這些敏感字符很容易被網站過濾和攔截,,當然了,防護還有很多高級的防護,破解也有很多高級的破解
四、HEAD注入
1、HEAD注入原理
在傳參的時候,將我們的數據構建在http頭部
利用了php的全局變量$_server獲取用戶的相關信息且將數據存入數據庫,利用updatexml函數輸入sql語句,返回信息
通常HTTP消息包括客戶機向服務器的請求消息和服務器向客戶機的響應消息。這兩種類型的消息由一個起始行,一個或者多個頭域,一個只是頭域結束的空行和可選的消息體組成。HTTP的頭域包括通用頭,請求頭,響應頭和實體頭四個部分。每個頭域由一個域名,冒號(:)和域值三部分組成。域名是大小寫無關的,域值前可以添加任何數量的空格符,頭域可以被擴展為多行,在每行開始處,使用至少一個空格或制表符。
2、PHP超全局變量
超全局變量
php中許多預定義變量都是超全局變量,這意味着它們在一個腳本的全部作用域中都可用
$_REQUEST(獲取GET/POST/COOKIE)COOKIE在新版本已經無法獲取了
$_POST(獲取POST傳參)
$_GET(獲取GET傳參)
$_COOKIE(獲取COOKIE傳參)
$_SERVER(包含了諸如頭信息(header)、路徑(path)、以及腳本位置(script locations)等等信息的數組)
$_SERVER['HTTP_REFERER']獲取referer請求頭數據
$_SERVER["HTTP_USER_AGENT"]獲取用戶相關信息,包括用戶瀏覽器、操作系統等信息
$_SERVER["REMOTE_ADDR"]瀏覽網頁的用戶ip
User-Agent:服務器獲取客戶的操作系統,瀏覽器版本等,有些網站中會將獲取的信息存入數據庫中
Cookie:身份信息、進行 session 跟蹤而儲存在用戶本地終端上的數據,一般會加密.
X-Forwarded-For:XFF頭,代表客戶端,HTTP的請求端真實的IP,有些網站的防注入功能會記錄請求端真實IP地址並寫入數據庫,修改XXF頭可虛假IP
Rerferer:瀏覽器告訴WEB 服務器是從哪個頁面鏈接過來的.
Host:客戶端指定訪問的WEB服務器的域名/IP 地址和端口號
3、updatexml()函數
updatexml() 更新xml文檔的函數
語法:updatexml(目標xml內容,xml文檔路徑,更新xml內容)
OK,這里看上去是去更新了xml文檔,實際上在xml文檔路徑的位置寫了子查詢語句,然后輸入特殊字符,之后因為不符合輸入規則報錯了,其實在報錯的時候已經執行l子查詢語句
【xml文檔路徑 填寫 :0x7e】
concat()是拼接字符串函數
updatexml(1,concat(0x7e,(select database()),0x7e),1)
why? 0x7e是16進制,MySQL字符串是支持十六進制的,有個前提:開頭須寫0x ,這就是告訴MySQL下面要開始十六進制了。而7e才是十六進制的內容,7e是個特殊符號,一定不符合路徑規則
OK,今天的就先到這里,斷更了好久,今天多寫點,最后感恩