官术网_书友最值得收藏!

1.1.1 SQL注入基礎

1.整數型注入

整數型注入即參數為整數型,參數兩側無引號或者其他符號。SQL語句類似于select*from news where id=參數,靶場環境為CTFHub技能樹-WEB-SQL注入-整數型注入。

首先判斷是否存在整數型注入。

SQL語句:select*from news where id=1 and 1=1。

SQL語句:select*from news where id=1 and 1=2。

然后,根據回顯的不同,我們可以判斷插入的語句是否被解析為SQL語法,進而判斷是否存在整數型注入。

接著我們可以利用union聯合查詢語法注出數據。

SQL語法中的union聯合查詢用于合并兩個或多個select語句的結果集,union內部的每個select語句必須擁有相同數量的列。在某些數據庫,如Oracle數據庫中,每個select語句中列的數據類型也必須一致。union注入的步驟如下。

首先確認查詢的列數,一般有兩種方法。

第一種是利用order by語句進行查詢,代碼如下。

SQL語句:select*from news where id=1 order by 1。

SQL語句:select*from news where id=1 order by 2。

SQL語句:select*from news where id=1 order by 3。

因為輸入1 order by 3時返回錯誤,所以列數為2。

第二種是利用union語句進行查詢,代碼如下。

SQL語句:select*from news where id=1 union select 1。

SQL語句:select*from news where id=1 union select 1,2。

因為輸入1 union select 1,2時返回正常,所以列數為2。

判斷出列數后,可以直接使用union語句查詢數據庫名,代碼如下。

SQL語句:select*from news where id=-1 union select 1,database()。

這里id=-1的原因是回顯數據的時候只會顯示一條數據,需要讓第一個select語句查詢返回空。結果如圖1-1所示,數據庫名為sqli。

圖1-1 SQL注入查詢出數據庫名

MySQL5.0以上的版本中,有一個名為information_schema的默認數據庫,里面存放著所有數據庫的信息,比如表名、列名、對應權限等,我們可以通過它查詢數據庫表名,代碼如下。

SQL語句:select*from news where id=-1 union select 1,table_name from information_schema.tables where table_schema='sqli'。

執行結果如圖1-2所示。

圖1-2 SQL注入查詢出表名

雖然得到了數據庫sqli中的一個表名為news,但是數據庫中一般會有多個表,當出現需要查詢的數據不只一條,而回顯只能顯示一條數據的情況時,可以通過group_concat()函數將多條數據組合成字符串并輸出,或者通過limit函數選擇輸出第幾條數據。

這里我們使用group_concat()函數一次查詢出所有表名,代碼如下。

SQL語句:select*from news where id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'。

執行結果如圖1-3所示。

圖1-3 使用函數查詢出多個表名

得到news表與flag表,我們需要的數據就在flag表中。

查詢表中的列名同樣是通過information_schema數據庫進行查詢,代碼如下。

SQL語句:select * from news where id=-1 union select 1,group_concat(column_name)from information_schema.columns where table_schema='sqli' and table_name='flag'。

執行結果如圖1-4所示。

圖1-4 查詢列名

查詢到flag表中只有一個列flag。

數據庫名、表名、列名已經被我們通過注入查詢出來了,下面直接查詢列中的數據即可,代碼如下。

SQL語句:select*from news where id=-1 union select 1,group_concat(flag) from sqli.flag。

注入結果如圖1-5所示,成功通過SQL注入獲取flag。

圖1-5 查詢列中的數據

2.字符型注入

字符型注入即參數為字符型,參數兩側受引號或者其他符號影響。與整數型注入相比,字符型注入多了一個引號閉合的步驟。

SQL語句:select*from news where id='參數'。

靶場環境:CTFHub技能樹-WEB-SQL注入-字符型注入。

判斷參數兩邊的符號,代碼如下。

SQL語句:select*from news where id='1' and '1'='1'。

SQL語句:select*from news where id='1' and '1'='2'。

由此可以判斷存在SQL注入,并且為字符型注入,利用union注入獲取數據即可。

3.報錯注入

報錯注入是利用數據庫的某些機制,人為制造錯誤條件,在報錯信息中返回完整的查詢結果。在無法進行union注入并且回顯報錯信息時,報錯注入是不二之選。

下面介紹利用floor()函數報錯注入的方法。

首先利用floor(rand(0)*)產生預知的數字序列01101,然后利用rand()函數的特殊性和group by語法中的虛擬表,引起報錯。MySQL版本號需要大于或等于4.1,代碼如下。

執行結果如圖1-6所示。

圖1-6 利用floor()函數報錯注入

下面介紹利用extractvalue()函數報錯注入的方法。

extractvalue()函數語法如下。

第一個參數XML_document是String格式,表示XML文檔對象的名稱。第二個參數XPath_string表示Xpath格式的字符串。

extractvalue()函數的作用是從目標XML中返回包含所查詢值的字符串。當第二個參數不符合XPath語法時,會產生報錯信息,并且將查詢結果放在報錯信息中。由于extractvalue()函數是MySQL 5.1.5版本添加的,因此使用它進行報錯注入時需要滿足MySQL版本號大于或等于5.1.5。

執行結果如圖1-7所示。

圖1-7 利用extractvalue()函數報錯注入

extractvalue()函數最長報錯32位,在注入時經常需要利用切片函數,如substr()函數獲取完整數據。

下面介紹利用updatexml()函數報錯注入的方法。

updatexml()函數使用不同的XML標記匹配和替換XML塊,代碼如下。

第一個參數XML_document是string格式,表示XML文檔對象的名稱。第二個參數XPath_string代表路徑,是XPath格式的字符串。第三個參數new_value是string格式,替換查找到的符合條件的數據。

使用updatexml()函數時,如果XPath_string格式出現錯誤,MySQL會爆出語法錯誤(XPath syntax)。與extractvalue()函數相同,updatexml()函數在MySQL 5.1.5版本添加,使用它進行報錯注入時,需要滿足MySQL版本號大于或等于5.1.5。

在MySQL中,exp()函數的作用是返回e的冪次方。當傳入的參數大于或等于710時會報錯,并且會返回報錯信息。利用這種構造報錯可以回顯信息,代碼如下。

~表示按位取反操作,可以達到溢出的效果。

整型溢出是利用子查詢引起BITINT溢出,從而設法提取數據。我們知道,如果一個查詢任務成功返回,其返回值為0,那么對其進行邏輯非運算的結果就會變成1,例如對(select * from(select user())x)進行邏輯非運算,返回值就是1。我們通過組合取反運算和邏輯非運算可以構造報錯并回顯信息,代碼如下。

下面介紹一種在Oracle 8g、9g、10g版本中不需要任何權限就能構造報錯的方法。需要注意的是,在Oracle 11g及之后的版本中,官方加強了訪問控制權限,必須有網絡訪問權限,才能使用此方法,代碼如下。

ctxsys.drithsx.sn()函數在Oracle中用于處理文本,當傳入參數類型錯誤時,會返回異常,代碼如下。

CTXSYS.CTX_REPORT.TOKEN_TYPE()函數的作用與ctxsys.drithsx.sn()函數類似,用于處理文本。

XMLType在調用的時候必須以<:開頭,以>結尾。需要注意的是,如果返回的數據中有空格,返回結果會被截斷,導致數據不完整。這種情況下應先轉為十六進制編碼,再導出。

SQL Server的報錯注入主要利用的是在類型轉化錯誤時,顯示類型轉換失敗的值,類型轉換函數如下。

下面以CTFHub技能樹中的報錯注入靶場為例進行介紹。打開靶場輸入參數1',結果如圖1-8所示。

圖1-8 探測報錯注入

可以看到回顯了報錯信息,我們嘗試報錯注入。

結果如圖1-9所示。

圖1-9 報錯注入得到數據庫名

運行結果表明,成功在報錯信息中報出數據庫名為sqli。

接著爆破出sqli庫下的表,代碼如下。

執行結果如圖1-10所示。

圖1-10 報錯注入得到表名

得到news表和flag表之后可以發現,我們要找的數據在flag表中。繼續爆破出flag表下的列,代碼如下。

執行結果如圖1-11所示。

圖1-11 報錯注入得到列名

flag表下只有一個flag列,直接讀出數據,查詢flag表下的flag列,代碼如下。

執行結果如圖1-12所示。

圖1-12 報錯注入得到部分flag

成功得到flag,注意觀察回顯的數據不是完整的,這是因為extractvalue()函數和updatexml()函數一次最多只能爆出32位字符,所以需要通過字符串截取函數獲取剩余的字符。

執行結果如圖1-13所示。

圖1-13 截取后的結果

4.布爾盲注

當注入點沒有直接的回顯,只有True(真)和False(假)兩種回顯時,我們可以通過回顯的結果,推斷注入的語句執行結果是True還是False。即使沒有直接回顯數據,我們也能通過不斷調整判斷條件中的數值,逐個字符地枚舉數據庫,代碼如下。

布爾盲注最重要的步驟是構造布爾條件,下面列出一些常見的繞過方法。

● 正常情況:'or bool#、true'and bool#。

● 不使用空格、注釋:'or(bool)='1、true'and(bool)='1。

● 不使用or、and、注釋:'^!(bool)='1、'=(bool)='、'||(bool)='1、true'%26%26(bool)='1、'=if((bool),1,0)='0。

● 不使用等號、空格、注釋:'or(bool)<>'0、'or((bool)in(1))or'0。

● 其他:or(case when(bool)then 1 else 0 end)。

布爾盲注常用函數如表1-1所示。

表1-1 布爾盲注常用函數

下面以CTFHub技能樹中的布爾注入靶場為例進行介紹。

輸入一些測試數據,發現只有兩種回顯,query_success、query_error,并不會回顯具體的數據,數據結果如圖1-14、圖1-15所示。

圖1-14 正常查詢

圖1-15 錯誤查詢

我們構造一個布爾條件來判斷注入語句的執行結果。輸入1 and(1=1),執行結果如圖1-16所示。

圖1-16 布爾條件為真

輸入1 and(1=2),執行結果如圖1-17所示。

圖1-17 布爾條件為假

可以看到,當拼接后的語句正確時,回顯結果為query_success,否則回顯結果為query_error。我們通過不同的回顯結果,逐個字符地枚舉數據,代碼如下。

代碼中substr((select database()), 1, 1)='a'的意思是判斷select database()語句查詢結果的第一個字符是否為a。這樣我們只需要遍歷字符就能判斷出數據庫名的第一位字符。執行結果如圖1-18所示。

圖1-18 判斷第一個字符

可以看到,輸入1 and(substr((select database()),1,1)='s')的回顯結果為query_success,說明數據庫名的第一個字符為s。執行結果如圖1-19所示。

圖1-19 判斷出數據庫第一個字符

接著繼續枚舉第二個字符,直到枚舉出所有數據,這個過程可以通過腳本實現。

5.時間盲注

時間盲注與布爾盲注類似,區別在于時間盲注是通過頁面的響應時間判斷語句的真假,一般格式如下。

兩個常用的延時函數如下。

其他導致延時效果的方法如下。

利用笛卡兒積延時注入的代碼如下。

下面以CTFHub技能樹時間盲注靶場為例進行介紹。

無論輸入什么,回顯結果都是空的,我們無法通過回顯結果來判斷SQL語句是否執行成功。正常數據回顯如圖1-20所示。

這時候可以利用時間盲注來注出數據。輸入1 and sleep(0),執行結果如圖1-21所示。

圖1-20 正常數據回顯

圖1-21 睡眠0秒響應時間

輸入1 or sleep(5),執行結果如圖1-22所示。

圖1-22 睡眠5秒響應時間

根據響應包的時間可知,輸入的延時語句確實被執行了。輸入我們構造好的語句,當語句執行結果正確時執行延時函數,錯誤時不執行延時函數,這樣就可以通過響應包的時間逐個字符枚舉出數據。

例如,構造一個SQL語句,當if語句中的判斷結果正確時延時3秒,代碼如下。

執行結果如圖1-23所示。

圖1-23 語句正確時延時3秒

if語句中的判斷結果錯誤時無延時,代碼如下。

執行結果如圖1-24所示。

圖1-24 語句錯誤時無延時

利用延時來判斷結果,我們通過此方法枚舉數據庫名的第一個字符,代碼如下。

執行結果如圖1-25所示。

圖1-25 語句錯誤時無延時

當枚舉到s字符時延時了3秒,說明數據庫名的第一個字符為s,如圖1-26所示。

圖1-26 判斷出數據庫名的第一個字符

接著繼續枚舉第二個字符,直到枚舉出所有數據,這個過程可以通過腳本實現。

值得一提的是,因為延時的原因,時間盲注的枚舉速度慢,在有其他方法能夠注出數據時一般不建議使用時間盲注。

主站蜘蛛池模板: 桂东县| 达拉特旗| 福鼎市| 从化市| 乳山市| 东乡族自治县| 揭东县| 乌审旗| 武陟县| 吐鲁番市| 安化县| 昌江| 三原县| 合水县| 通渭县| 徐水县| 遂川县| 丹江口市| 梓潼县| 昌吉市| 云南省| 宁河县| 瑞安市| 安化县| 介休市| 台南市| 丘北县| 民权县| 庆城县| 桓台县| 托克托县| 昌吉市| 巴彦县| 纳雍县| 嘉善县| 习水县| 锡林浩特市| 凯里市| 中宁县| 翁牛特旗| 田林县|