作者:ZERO 所屬團隊:Arctic Shell
參考文獻:《sql注入攻擊與防御》
使用平台:pikachu漏洞練習平台
導語:
在owasp發布的top10排行榜中注入漏洞一直是危害排名極高的漏洞,數據庫注入一直是web中一個令人頭疼的問題。
一個嚴重的SQL注入漏洞,可能會直接導致一家公司破產!
這並不是戲言,其實SQL注入漏洞最主要的形成原因是在進行數據交互中,當前端的數據傳入后端進行處理時,由於沒有做嚴格的判斷,導致其傳入的“數據”在拼接到SQL語句中之后,由於其特殊性,被當作SQL語句的一部分被執行,從而導致數據庫受損(被脫褲、被刪除、甚至整個服務器權限淪陷)。
本文將從web程序的基本原理開始理解SQL注入,然后找到SQL注入利用(本次將演示利用information-schema注入進行一次完整的入侵過程再現)。
0x1: 理解(了解)web應用的工作原理
在日常web開發中,我們會來開發出許多的web應用來服務於我們的日常生活,隨着人們生活質量的提高,web應用的形式也越來越多,但大部分的web應用依舊有着相同點,他們都具備交互性,且多半是數據庫進行驅動的,在當下的互聯網中,用數據庫驅動的web應用多如牛毛,他們通常含有一個后台數據庫以及很多頁面,而在這些頁面中包含了利用某種編程語言來寫成的服務器端腳本,這些腳本作用於web頁面來與用戶進行交互時從數據庫中提取特定的數據。
一般的web應用通常分三層,分別是表示層、邏輯層、儲存層。下面我們介紹一下這三層以及交互方式
表示層是一個web應用的最高層,將該web應用的具體功能以及信息呈現給用戶,還可以與其他層進行交互。
邏輯層(中間層)是用來處理細節以達到控制應用的目的。
而存儲層包括數據庫服務器,用來進行的信息的檢索和存貯。
通常的交互方式就是表示層向中間層發出請求,中間層通過查詢、檢索存儲層,獲取信息來響應該請求。這就是一次完成的交互過程。
三層架構有一個基本原則:表示層不能直接於數據層通信,在三層架構中,所有通信必須都通過中間層。
0x2:理解SQL注入
前面講了那么多理論的東西,究竟什么是SQL注入呢?
SQL注入就是構造SQL語句並將他們插入到傳入的參數中發動的攻擊,然后將這些參數遞交給后台SQL服務器加以解析並執行。
這里我將用一個最基本的數字型注入來演示一個基本SQL注入
一個基本的查詢頁面長這樣的:
我們輸入一個“1”試一試
這里可以看出查詢了兩個字段,猜測hello后的應該是用戶名,下面的應該是用戶所對應的郵箱
因此猜想SQL語句的構造可能是這樣的:
select 字段1,字段2 from 表 where id =1
那我們如何獲得表的其他數據呢?(俗稱爆表)
這里就可以來嘗試構造一個利用了:1 or 1=1
(在SQL中or是如果第一個條件和第二個條件中只要有一個成立,則 OR 運算符顯示一條記錄。1=1永久返回為真,所以where后輸入的值將永遠為真)
因為是post方法,我們使用hackbar進行post提交,來遍歷整個表
一次基礎的SQL注入利用就演示完成了。
0x3:如何找到SQL注入
本身我們很難獲取到web應用的源代碼,這樣會導致我們不知道設計者如何設計的,找到SQL注入,這就需要借助黑客本身的自我思維能力來進行測試。借助推理來進行測試的一般步驟:向服務器發送請求,然后查看其中的異常來獲取信息。
通過SQL錯誤來找到SQL注入
在web注入靶場中我們可以通過看源碼知道這個web應用的構造,來完成利用,而在實際的測試中,碰到的都是是不開源的web應用,導致對於內部構造完全不清楚,在實際的測試中一般黑客采用讓SQL報錯的方式來猜測內部構造,以達到找到注入點的目的。
我將通過字符型注入來演示通過SQL報錯來查找注入
字符型注入這類利用的一般方式是:先來閉合測試,構造合法的SQL來欺騙后台。
利用的一般樣式:xxx (符號閉合) or 1=1(注釋)
通常一個輸入框我們事先並不知道輸入的內容再被傳入時被當做字符還是數字,這時就可以通過報錯來進行判斷,SQL中字符和數字的區別是數字不用’’或者””來進行修飾,而字符則需要,於是我們則需要判斷兩次,先判斷這個輸入框是數字型的還是字符型的在判斷他是用哪個修飾符來進行修飾
報錯內容提示列名不存在,這個報錯提示我們SQL服務器認為這個值不是一個數字,那末就是一個列,然后SQL去尋找這個列,然后當然什么都找不到,就返回報錯了,通過這里我們可以判斷出應該輸入的類型
接着來判斷修飾符
查詢后結果
從報錯提示中可以分析出雙引號並沒有傳遞進數據庫服務器里,也就是存儲層,我們再試一個單引號
再次返回報錯,在這個報錯中我們可以看出修飾符已經進入到了SQL服務器中並執行,說明存在注入點,接着可以來開始進行注入了,知道了該用哪個修飾符進行閉合,這時構造個利用將會變的更加容易。
(每當我們傳遞一個值到SQL構造語句中的時候,如果傳遞的一個值是需要符號修飾的,那末SQL語句會自帶一個符號去進行閉合,例如:傳值為a,到SQL語句中就會使這樣:select xxx,xxx from xxx where id=’a’,那兩個單引號就是SQL構造語句中用來進行參數閉合的,簡單的說,只要你輸入一個需要符號修飾的詞語,后台就會自動給它加一個修飾符號,這也就是我們為何要把前面進行符號閉合,但是后面就會多了一個‘ 符號,我們直接注釋掉就好了。同樣的方法,利用order by 可以猜測有幾個字段,道理相同,這里就不贅述了。)
0x4: 了解information_schema
information_schema數據庫是MySQL自帶的,它提供了訪問數據庫元數據的方式。
元數據是關於數據的數據,如數據庫名或表名,列的數據類型,或訪問權限等。有些時候用於表述該信息的其他術語包括“數據詞典”和“系統目錄”。
也就是說information_schma可以查到你的所有的搭建的數據庫名、表名和列的數據類型,在一切條件未知的情況下,存在注入點的話我們可以直接嘗試對information_schema進行訪問,從而獲得更多的信息。
例如:
SCHEMATA:提供了當前MySQL實例中所有數據庫的信息
TABLES:所有數據庫表的信息
CONLUMNS:提供了列信息
0x5:了解union聯合注入
既然要從一個信息數據庫開始注入,也就是另外一個數據庫,我們就不能在當前的數據庫下進行查詢,但是當前數據庫查詢下還存在一個注入點,所以說如何才能同時查詢兩次呢?這個時候我們就可以用到union聯合注入了,而union的作用是將兩個或多個select語句查詢語句結果合並起來
幾個值得注意的地方:
1.UNION必須由兩條或兩條以上的SELECT語句組成,語句之間用關鍵字UNION分隔
2. UNION中的每個查詢的列數(字段數)必須相同
3. UNION會從查詢結果集中自動去除了重復行
比如:
主查詢:
select username,password form member where name=“zero“ (union查詢)union select id,password form member where id=1
構造 payload:
xx' union select database(),user()#
說明:
Database():這個是查詢數據庫名稱
user():這個是使用者權限
0x6: information_schema注入演示
還是使用字符型注入,在前面的報錯中我們知到了兩個字段和注入點是單引號,開始注入
首先獲取基本信息:當前數據庫名稱
xxx’union select database(),user()#
獲取到當前數據庫名稱:pikachu
通過information_schema來獲取pikachu中有多少個表以及表的名稱
xxx’union select table_schema,table_name from information_schema.tables where table_schema=”pikachu”#
獲取到表名:users
獲取列:
xxx’union select table_name,column_name from information_schema.columns where table_name=’users’#
整理獲取到的信息來獲取密碼:
xxx’union select username,password from users#
0x7: 總結
作為一名Web應用開發人員,一定要對用戶輸入的數據進行嚴格的校驗處理,否則的話,SQL Injection將會不期而至!