zoukankan      html  css  js  c++  java
  • 关于爬虫本地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()

    代码很简单就是打开一个网页,然后截图。这个网页的内容就是一个弹框:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
    <script>alert("hacked by Helen");</script>
    </body>
    </html>

    而运行结果并不符合预期:

    截图失败了,因为有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,技术问题千万别百度。

    来源:华为云社区  作者:HuangJacky

  • 相关阅读:
    SOA精华的内容和实用的知识
    众多SEO专家集体盛赞
    黑客大曝光:VoIP安全机密与解决方案
    博文视点大讲堂41期SEO难点之网站内部链接结构
    TransactSQL管理与开发实例精粹
    千万不要错过云计算兴起的时代
    《海量数据库解决方案》之位图索引的结构和特征
    Oracle开发艺术
    Android应用程序的开发
    BizTalk Accelerator for HL7医疗行业消息路由处理机制
  • 原文地址:https://www.cnblogs.com/2020-zhy-jzoj/p/13165788.html
Copyright © 2011-2022 走看看