最近一直忙著實習面試,在準備面試的過程確實也補充了很多自己以前的知識盲區(qū),雖然有點考前惡補的意思,但能學到知識總歸是好的。當然,面試的結(jié)果也還算滿意,雖然還沒有拿到自己心中T0的大廠offer,但也獲得了很多其他乙方包括某個大廠甲方的實習offer。師哥們也陸續(xù)去實習了,老師這邊壓著比較多的項目正在推進,大都是滲透方向的,包括這個月的業(yè)務,所以趁著實習面試的尾聲,好好的干一波嘿嘿。
一、Result
這個月的業(yè)務挖洞成果對自己來說還是很滿意的,除了漏洞數(shù)量外,漏洞質(zhì)量也比較高。具體就不多說了,言歸正傳,在本月的業(yè)務滲透測試里發(fā)現(xiàn)一個很有趣的sql注入,也是自己之前的一個盲區(qū)——order by排序注入,一直想著在實戰(zhàn)中接觸這種類型的注入場景,還真就來了。
二、Process
話不多說,上接口:
https://xxxx.com/xxx/xxx/xxx/getAreaSpotPageList?&pageindex=1&pagesize=20&passAreaId=2&orderby=pass_area_spot_id+desc&userId=1181163&hotelId=5&brandId=5
這是一個比較正常的get類型的獲取數(shù)據(jù)接口,但orderby參數(shù)比較惹眼,因為參數(shù)的值為pass_area_spot_id+desc,desc表示的是sql中的降序排序,卻以orderby參數(shù)值的方式輸入,所以就對該參數(shù)進行了測試:
Step 1:
令orderby=pass_area_spot_id+desc,返回數(shù)據(jù)如下:

Step 2:
令orderby=pass_area_spot_id+asc,返回數(shù)據(jù)如下:

(解釋一下,兩種情況下返回內(nèi)容不一致)
Step 3:
這里其實大致可以判斷存在排序注入的可能,但還是有歧義,需要進一步進行判斷,這里采用基于時間的盲注輔助判斷:
Payload1:pass_area_spot_id+desc,(select*from(select+sleep(3)union/**/select+1)a)
返回如下:

該請求在5s多時得到響應。
Payload2:pass_area_spot_id+desc,(select*from(select+sleep(6)union/**/select+1)a)
返回如下:

該請求在8s多時得到響應。
在不考慮測試環(huán)境本身的網(wǎng)絡(luò)狀況,上述測試過程可以證明漏洞的存在。
Step 4:
Time-based還是不太方便,并且時間較長。所以更換注入手法,直觀輸出注入結(jié)果:
Payload:pass_area_spot_id+desc,if(ascii(substr(database(),? ,1))=?,1,(select%201%20from%20information_schema.tables))
通過布爾類型的盲注,利用上述payload進行fuzz(省略通過二分法判斷當前數(shù)據(jù)庫長度的步驟):

查找對應ascii所代表的字符,獲得當前數(shù)據(jù)庫名:xxxxxxxx,測試結(jié)束。
三、Review
在整個測試階段里,雖然注入成功,但是也引發(fā)了自己的兩個問題,這兩個問題也是自己在面試的過程中所碰到的。
Q1、為什么預編譯不太好防order by注入(就mysql數(shù)據(jù)庫來談)?
A1:
這個問題不管在面試的過程中還是個人的平時積累過程中都能夠折射出個人的學習深度,很遺憾,我并沒有在面試的過程中很好的回答這個問題,其實并不是不知道這個問題的答案,而是不太好組織語言去系統(tǒng)性的帶擴展的回答它,并且在之前的挖洞經(jīng)歷里沒有對應的場景支撐,所以還是比較遺憾吧,另一方面更說明自己的知識體系太弱,表面徘徊較多。
言歸正傳,為什么預編譯不太好防order by注入?這個問題等于是在問為什么在預編譯中order by后不能參數(shù)化?一般的sql執(zhí)行片段代碼(預編譯方式)是這樣的:
Connection conn = DBConnect.getConnection();
String sql = " SELECT xxx FROM xxx WHERE xxx = ? ";
ps = conn.prepareStatement(sql);
ps.setString(1, xxx); //或者ps.setInt(1, username);
rs = ps.executeQuery();
當我們輸入xxx=abc時,預編譯的代碼會自動給abc加上引號,變成'abc',sql語句也變成了:
String sql = " SELECT xxx FROM xxx WHERE xxx = 'abc' ";
這樣可以有效的防止用戶的輸入直接拼接在sql語句之后,譬如用戶輸入xxx=' or '1'='1時,實際上的sql語句卻為:
String sql = " SELECT xxx FROM xxx WHERE xxx = '' or '1'='1' ";
無法達到攻擊的效果。說回order by,order by的用法一般為order by [字段名] [desc/asc],如果對order by之后的輸入進行參數(shù)化,那么sql語句將會變成這樣:
String sql = " SELECT xxx FROM xxx WHERE xxx ='xxx' order by 'xxx' "
這樣會造成sql語法錯誤,因為此時'xxx'是字符串而不是字段名。
那么為什么預編譯的函數(shù)在參數(shù)化時非要把輸入加上引號呢?如果沒有引號不就可以防御order by注入了嗎?是的,但確實沒有不加引號的預編譯的方法哈哈。
引伸來說,這樣的sql注入不僅僅發(fā)生在order by處,任何需要字符串且不能夠加引號的地方都有可能發(fā)生類似的注入,因為不能參數(shù)化的位置,不管怎么拼接,最終都是和使用“+”號拼接字符串的功效一樣。
那么又引出了新的問題,既然預編譯防不了order by,那么該怎么樣防?答案是采用白名單的方式,因為order by之后跟的字段名肯定是有限的并且是數(shù)據(jù)庫中已經(jīng)存在的字段,只要對這些有限字段設(shè)置白名單,任何不在白名單內(nèi)的數(shù)據(jù)統(tǒng)一報錯,那么就可以解決啦。
Q2、面對時間較長的Time-based或者Boolean-Base,除了用工具自動化跑結(jié)果外,有沒有其他較好的方式?
A2
答案是利用Dnslog,當時面試的時候比較緊張,一直想著payload的改進和工具自動化,把這個給忘了,其實也跟平時使用這個技巧不多的原因有關(guān),所以認了。。不過在面試過后確實對Dnslog又強化了一波,的確很好用。直接上鏈接把->Dnslog-<,這個博主關(guān)于Dnslog在各個場景下的使用技巧總結(jié)的十分詳細了。
四、Re-review
參與面試的過程其實是對自己之前所積累的技術(shù)體系的一種測試,更重要的對我來說收獲最多是了解圈內(nèi)的大牛們都在關(guān)注著哪些他們感興趣的方向和技術(shù)細節(jié),在丟人的同時的確獲得了很多自己想要的信息哈哈。也發(fā)現(xiàn)了自己在hack的過程中深度的確不夠,容易滿足于眼前的成果,挖洞是一方面,學習知識也是。因為hack的過程永遠比挖到漏洞更重要。
五、To-Do
1、接下來的時間里繼續(xù)靶場的練習,回顧之前浮躁狀態(tài)下所忽略的技術(shù)細節(jié),直到hack清楚為止。
2、思考自己的知識框架和技術(shù)框架,乘早跳出滲透的圈子,接觸真正的企業(yè)體系安全建設(shè)。
3、培養(yǎng)好奇心,保持好奇心。