類似我一貫的做法,這次Real World CTF我出了一道實戰性的題目,目標仍然是getshell。
我們以滲透測試的步驟來審視這道題目。
0x01資訊蒐集
與我以往的題目不同的是,這次雖然我自己寫了一部分程式碼,但是這部分程式碼的目的是串聯起幾個服務,整個流程與程式碼漏洞無關,所以沒有給出原始程式碼。
目標是一個簡單的web,描述是“Use crawlbox to get all links from a page”,輸入一個URL,稍等片刻可以獲取這個頁面裏所有的連結,後端應該是一個爬蟲:
我們抓取一下這個“爬蟲”的請求:
可見其User-Agent是Scrapy/ChromeHeadless(+https://scrapy.org)。從其中獲取到了兩個資訊:
Scrapy/ChromeHeadless (+https://scrapy.org)
- Scrapy
- Chrome Headless
scrapy是python下的一款非常流行的爬蟲框架,猜測用戶輸入的URL被交給scrapy執行,而scrapy中使用了Chrome對URL進行訪問並獲取結果。
關注到這一點,我們可以瀏覽一下scrapy這個工具的首頁:
其中提到部署爬蟲可以部署到scrapyd,在檔案中也提到了這一點:https://docs.scrapy.org/en/latest/topics/deploy.html。
思考,作為一個Web服務,如果要調用scrapy爬蟲爬取資訊,無非有兩種方法:
- 直接命令列調用scrapy
- 通過scrapyd提供的API進行調用
那麼,如何分辨目標使用了哪種方法調用scrapy呢?
方法也很容易想到:我們可以嘗試探測本地或內網中是否有開啟scrapyd服務的埠。打開scrapyd的檔案(https://scrapyd.readthedocs.io/en/latest/overview.html#webui),可知scrapyd默認開放在6800埠。
最簡單的方法,我們直接用目標提供的爬蟲功能進行探測(需要用xip.io簡單繞過一下SSRF的檢測):
xip.io
顯然,本地6800埠是開啟的,可以確定後端是scrapyd。
如果你完全沒注意到User-Agent中的scrapy,題幹中的“I wrote a secure crawler on top of a browser,which you can use to crawl all the links on a website”也提到了瀏覽器,你也可以完全將這裡理解為一個XSS盲打漏洞。
於是,我們可以利用XSS中的一些技巧,如:獲取內網地址、對內網服務進行掃描、獲取User-Agent、Cookie、LocalStorage等資訊,進而也能獲取到User-Agent中的scrapy,或者發現6800埠這樣的敏感服務。
如果你閱讀檔案或者掃描到了6023埠,這也是曾經一個可以攻擊scrapy的埠。scrapy在啟動掃描期間會開放6023埠作為Telnet Console,通過這個埠可以直接執行任意Python程式碼。在1.5.2後,scrapy官方修復了這個問題,詳見https://docs.scrapy.org/en/latest/news.html#scrapy-1-5-2-2019-01-22。
0x02如何攻擊scrapyd
一頓資訊蒐集後,目標整個工作流程就清晰了:用戶輸入的URL被交給部署在scrapyd上的爬蟲進行爬取,爬蟲調用了Chrome渲染頁面,並將結果中的連結返回給用戶。
那麼,這裡重點就是scrapyd了。我們需要閱讀scrapyd的檔案,方便理解這個項目的工作流程。通過檔案可知,scrapy是一個爬蟲框架,而scrapyd是一個雲服務,用戶可以將自己用scrapy框架開發的爬蟲上傳到雲端,然後通過Web API調用這個爬蟲爬取資訊。
scrapyd主要提供以下一些API:
- /daemonstatus.json獲取雲服務的狀態
- /addversion.json上傳一個新的爬蟲項目,或者給一個已有的項目更新代碼
- /schedule.json執行一個爬取任務
- /cancel.json停止並取消一個任務
- /listprojects.json列出雲端的所有項目
- /listversions.json列出某個項目的所有程式碼版本
- /listspiders.json列出一個項目下所有spider,spider這個概念是scrapy框架中的,一個scrapy開發的爬蟲可以有多個spider
- /listjobs.json列出所有任務,包括正在進行的、已完成的、等待執行的三個狀態
- /delversion.json删除某個項目下的某個程式碼版本
- /delversion.json删除某個項目
簡單來說,scrapyd雲服務下可以有多個項目,每個項目下可以有多個程式碼版本,每個程式碼版本就是一個完整的scrapy項目,一個scrapy項目下可以有多個spider,最終執行的任務的載體是一個spider。
那麼,也就是說,攻擊者可以創建一個項目,並部署他自己的scrapy程式碼版本,將惡意程式碼部署到雲端,進而對scrapyd雲端進行攻擊。
根據這個思路,我們先在本地進行測試。
安裝並啟動scrapyd:
啟動後訪問http://127.0.0.1:6800即可看到主頁:
http://127.0.0.1:6800
此時雲端沒有項目:
然後,我們本地再安裝scrapy框架,並創建一個scrapy項目:
生成了項目後,我們在evil/__init__.py中加入惡意程式碼:
evil/__init__.py
然後,我們用scrapyd-client這個工具,將項目打包成egg包。當然也可以自己用setuptools手工打包。
此時,惡意的egg包已經生成,然後我們將其部署到雲端:
成功部署:
此時,touch success已成功執行:
touch success
0x03利用CSRF漏洞攻擊瀏覽器爬蟲
針對6800埠的攻擊在本地可以複現了,但是目標網站的6800是開啟在內網的,我們無法直接訪問。
可以借助目標前端的那個SSRF嗎?不行,因為這只是一個GET型的URL請求,無法發送POST包部署程式碼。
不過,因為這個URL是被瀏覽器執行的,而scrapyd的所有API介面實際上都是可以進行CSRF攻擊的,所以我們可以利用頁面中的JavaScript發送POST數据包給6800埠,進而調用那些非GET型的API。
構造一個向http://127.0.0.1:6800/addversion.json發送POST上傳請求的頁面:
http://127.0.0.1:6800/addversion.json
值得注意的是,因為我們要上傳一個二進位檔案,所以我將evil.egg進行的base64編碼:cat evil.egg | base64,然後將其轉換成JavaScript中的Blob對象,添加到FormData中。
cat evil.egg | base64
將這個頁面提交給爬蟲進行爬取,成功完成整個利用過程。
0x04總結
首先吐槽一下scrapy這個框架,真是盛名之下其實難副,雖然說到爬蟲必然會說到這個框架,但實際上不管是從其生態、檔案、程式碼等角度看待這個項目,都是無法和Python下另一個偉大的項目Django相提並論的。實際使用下來感覺其架構不合理,檔案也模糊不清,周邊生態如scrapyd、scrapyd-client更是陳舊不堪,問題很多,處於弃療狀態。
總的來說scrapy作為一個生態框架是不及格的,但聊勝於無吧,相比於手工編寫爬蟲來說還是給開發者提供了很多幫助。
另外,在MVVM架構日益流行的當下,爬蟲也變得更加靈活,特別是借助Chrome Headless或splash的爬蟲能够動態執行JavaScript這個特性,能讓爬蟲爬到的資訊更加完善,但也讓攻擊者有更多攻擊途徑。
通常來說scrapy和splash是一對標配,雖然我這次用的是Chrome,事實上沒啥太大差別。對於此類動態爬蟲,攻擊者可以對瀏覽器或js引擎本身進行攻擊,或者如我這樣利用JavaScript攻擊內網裏一些基礎服務。
另外,經常會有人在運行爬蟲的時候會設定--no-sandbox、--disable-web-security等危險選項,這也給攻擊者提供了很多便利,我建議利用普通用戶許可權啟動瀏覽器爬蟲,以避免使用這些不安全的選項。
--no-sandbox
--disable-web-security
作為一個“駭客”,在開動自己掃描器的同時,也要注意這些問題了哦,不要踏進別人的蜜罐還被人反日了。