關于爬蟲本地JS Hook的研究
0x00 背景介紹
最早的爬蟲,只需要能夠從服務端獲取到HTML代碼,進行分析即可,隨著Web2.0的普及,越來越多的網站都必須JavaScript解析之后才能正常顯示。因此這也對爬蟲提出了新的要求,當前前人們已經在爬蟲中集成Webkit等框架來滿足這樣的需求。
本文將從實際漏洞掃描器項目中,爬蟲遇到的一個問題作為切入點,簡單的介紹一下爬蟲過程中一些JavaScript上Mock或者Hook的技巧。
0x01 需求
這里需求主要有兩個:
場景一:彈框阻礙流程
在網頁中存在alert,prompt等彈出框,如果沒有取消會造成webkit某些API運行異常。當前針對alert的情況,通常的框架都提供一些額外的解決方案,比如PhantomJS的onAlert()函數,Selenium的switch_to.alert().accept()。但是我們還是想和場景二一起使用Hook的方法來解決。
場景二:記錄指定函數被調用情況
存儲型XSS的驗證過程通常分為Payload的注入和執行情況驗證。作為一款優秀的掃描器(沒錯,說的就是華為云漏洞掃描服務),注入的Payload一定不能對目標系統有危害,因此我們通常會選用一些溫柔的函數,比如console.log,而非alert或者隨機不存在的函數。但是當第二次爬蟲在爬取過程中,如何統計Payload觸發的情況,就會成為一個難題。
0x02 JavaScript Hook
函數的Hook,其實就是在函數被調用前,對函數進行替換。
var?old_alert?=?window.alert; window.alert?=?function(message){ ????console.log("receive:?"?+?message); ????old_alert(message); }
上面的例子是對alert函數增加一個日志打印的功能。
Hook很簡單,現在唯一的問題就是要在函數執行之前就進行替換,很多函數是在網頁加載中(head部分)或者網頁加載完成后立即就執行了,沒有空隙給我們替換函數。
0x03 注入實戰
PhantomJS
它是基于QT和Webkit的無頭(Headless)瀏覽框架,因為其不依賴Xvfb,資源占用比較小,有段時間非常受大家歡迎。其Project的Owner已經宣布不維護了,現在版本定格在2.1.1。PhantomJS良好的接口,使其能夠非常方便的支持JS代碼注入。
假如某個網頁(http://fake.hack.com/location.html) 會獲取地理位置,只有指定位置的用戶才會進行下一步處理。
var?webPage?=?require('webpage'); var?page?=?webPage.create(); //頁面初始化之前插入一段JS page.onInitialized?=?function(){ ????//模擬地理定位. ????page.injectJs("fake-location.js"); };
fake-location.js的代碼也非常簡單,內容如下:
window.navigator.geolocation?=?{ ????getCurrentPosition:?function?(success,?failure)?{ ????????success({ ????????????coords:?{????????????????//模擬華中科技大學產學研基地 ????????????????latitude:?22.52902, ????????????????longitude:?113.94376 ????????????},?timestamp:?Date.now() ????????}); ????}, ????watchPosition:?function(success,?failure){ ????????success({ ????????????coords:?{????????????????//模擬華中科技大學產學研基地 ????????????????latitude:?22.52902, ????????????????longitude:?113.94376 ????????????},?timestamp:?Date.now() ????????}); ????} };
從上面的例子可以看到我們Hook了getCurrentPosition函數,它只會返回指定的經緯度。當然這一切都依賴于PhantomJS提供了injectJs這個方法以及onInitialized這個事件。
Selenium
PhantomJS始終是小眾的選擇,Selenium才是主流,尤其是Chrome推出了Headless模式之后,大大提高了Selenium的效率。
由于時間原因,本文只研究了ChromeWebDriver的情況。首先看一段代碼:
from?selenium?import?webdriver from?selenium.webdriver.chrome.options?import?Options #?定義Chrome和ChromeWebDriver的路徑 DRIVER_PATH?=?"/Users/huangjacky/program/tools/chromedriver" CHROME_PATH?=?"/Applications/Google?Chrome.app/Contents/MacOS/Google?Chrome" URL?=?"http://127.0.0.1:8080/test.html" ? def?main(): ????#?創建一個WebDriver實例 ????options?=?Options() ????options.add_argument("--headless") ????options.binary_location?=?CHROME_PATH ????driver?=?webdriver.Chrome(executable_path=DRIVER_PATH,?chrome_options=options) ????try: ????????driver.get(URL) ????????driver.get_screenshot_as_file("test.png") ????except?Exception?as?e: ????????print(e)???? ????finally: ????????driver.close() ????????driver.quit()
代碼很簡單就是打開一個網頁,然后截圖。這個網頁的內容就是一個彈框:
???? ????
而運行結果并不符合預期:
截圖失敗了,因為有alert彈出框。
通過查看WebDriver的API,我們發現一個函數execute_script, 因此我們修改代碼如下:
driver.execute_script('window.alert=null;') driver.get(URL) driver.get_screenshot_as_file("test.png")
結果肯定是不成功的,不然就不會有這篇文章了。
大概是因為driver.get之后才會有window對象,之前執行的js代碼都無效。
Google了大半天,無果。看來從WebDriver上面入手是不行了,我們將目光回到Chrome瀏覽器自身來,它提供Chrome Devtools Protocol來方便我們這些開發者進行定制,通過官方文檔?的查看,我發現了Page.addScriptToEvaluateOnNewDocument這個Method是可以滿足我們需求的。
Chrome Devtools Protocol的一些細節,請讀者自行Google或者等我下一篇文章吧。
新的代碼如下:
resource?=?"/session/%s/chromium/send_command_and_get_result"?%?driver.session_id url?=?driver.command_executor._url?+?resource body?=?json.dumps({ ????'cmd':?'Page.addScriptToEvaluateOnNewDocument', ????'params':?{"source":?"window.alert=function(msg){console.log(msg)}"} }) response?=?driver.command_executor._request('POST',?url,?body) if?response['status']: ????raise?Exception(response.get('value')) print(response.get('value')) driver.get(URL) driver.get_screenshot_as_file("test.png")
代碼運行成功,截圖OK啦。
這里需要注意幾點:
1. url的獲取以及session,這些WebDriver里面都有方法
2. 請求的通道,這個主要復用WebDriver.command_executor
0x04 結論
遇到問題,我們首先研究框架支持的情況,當框架不支持的時候,我們可以從事情本質出發,也就是框架的底座,Chrome瀏覽器自身。最重要的就是多Google,技術問題千萬別百度。
python Selenium
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。