标签 Selenium 下的文章

前端动态变化对抗Selenium类自动化工具思路探索

0x01 前言

这不是一篇安全技术文章,如果你关注业务安全,羊毛党对抗,爬虫对抗,可以慢慢观看。

在业务安全领域最大的困扰是来自各种各样的自动化工具的薅羊毛行为,羊毛党所使用的自动化武器五花八门,其中模拟更像真人的,使用比较多的是基于Selenium库实现的操作各种真实浏览器模拟的操作。Selenium库提供的webdriver,支持主流的浏览器如:chrome,firefox, ie,opera,phantomjs,safari 也支持浏览器的headless模式,更多的介绍可以看文章:https://www.cnblogs.com/zhaof/p/6953241.html

0x02 自动化工具的demo

羊毛党在薅羊毛前就会准备好自动化工具,如抢票活动的薅羊毛,他们需要自动化工具能够完成打开浏览器,打开登录网页,填充账号密码信息,点击完成登录,打开活动页面,点击抢票等一系列操作。

下面给出的一个demo是使用Selenium +Chrome 浏览器模拟的对测试网站demo. testfire.net进行自动化登录(或撞库,爆破密码)的代码。

201812051544014778183194.png

上面的代码中首先使用Selenium中的webdriver 打开本地的Chrome 浏览器,然后利用其提供的API 接口,直接打开登录地址。要完成填充账号,密码的信息,需要先找到输入位置。Selenium 提供了多种查找元素的接口,可以通过id ,name ,xpath ,css selector ,甚至通过文本来定位。当找到操作元素的位置后就可以对其进行相应的点击,输入,拖动等动作了。

0x03 GET一些知识背景

元素的定位应该是自动化测试的核心,要想操作一个对象,首先应该识别这个对象。 一个对象就是一个人一样,他会有各种的特征(属性),如比我们可以通过一个人的身份证号,姓名,或者他住在哪个街道、楼层、门牌找到这个人。

那么一个对象也有类似的属性,我们可以通过这个属性找到这对象。 webdriver 提供了一系列的对象定位方法,常用的有以下几种:

  • id
  • name
  • name
  • class name
  • link text
  • partial link text
  • tag name
  • xpath
  • css selector

我们拿百度搜索的页面来做例子,分别使用不同的定位方法下python的调用参数如下:

百度搜索输入框的input标签如下:

<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">

哪么使用Selenium的自动化工具实现定位元素的方式有如下:

1.通过id定位

u = dr.find_element_by_id('kw')

2.通过name定位

u = dr.find_element_by_name('wd')

3.通过class name定位

s = dr.find_element_by_class_name('s_ipt')

4.通过xpath定位

s = dr.find_element_by_xpath('//*[@id="kw"]')

5.通过css selector定位

s = dr.browser.find_element_by_css_selector('#kw')

以上5种是使用Selenium编写自动化工具定位元素时使用的最多的方式,(其他的就不做列举了)可以看出自动化工具查找元素时对于这个输入标签的id,name, class等值的依赖是十分的强的。

我们再看看浏览器上javascript提供的查找元素的接口

201812051544016163471346.png

同样是使用了id, name, class的值进行的元素查找.

0x04 思考对抗思路

这种类型的利用驱动程序调用浏览器模拟人为操作的攻击方式通过传统的js防爬,UA 黑名单等方式是比较难防护的,因为这种攻击是真实浏览器发起的操作。回想到上面,这类攻击实现是通过webdriver 驱动浏览器进行的自动化操作,它的原理是通过注入自己的js 脚本到浏览器的每一个页面,通过js 完成页面的自动化点击,输入操作。哪么第一个思路就是去检测webdriver注入的js,这是目前一些防自动化攻击的厂商产品思路。防护产品A给每一个页面注入检测js ,通过检测webdriver 注入js 时带来的函数名,变量名等,实现自动化工具识别。这个检测思路也确实有效,但是这就像是杀软对抗病毒一样,通过特征库进行查杀。一定特征函数名,特征变量名等发生了变化如攻击者重写了webdriver 的驱动程序,修改了特征,哪防护产品就毫无办法了。这也是杀软一直以来面临的困扰。。。

变化一下思路,我们知道通过js 操作页面,同样需要先查找到元素。通过document. find ElenentBy**,最常见的是通过ID ,name 来查找元素。如果让 webdriver 的js注入过程是成功的,如果动态变化了标签的id, name, class name值,那么注入的JS脚本的find 定位元素过程是失败的,同样也能起到防护自动化工具的作用。

新的思路1:标签属性动态变化,干扰js 查找元素过程

对于防护产品而言,第一阶段保留原来的检测webdriver 注入js 的防护方式。随机切换到第二阶段即放过webdriver 的js 检测,动态混淆关键标签的ID ,NAME ,class name的值

什么时候动态变化?我们可以在upstream 第一次返回内容就开始,往后我们hook 一些关键事件,当页面触发这类事件时主动变化。

新的思路2:随机插入不可见相同标签

在分析Selenium中发现它在查找元素定位时不可见标签的会对其有干扰,在Selenium的git hub有提到过,高版本的Selenium 支持查找disabled 的标签。哪么我们就可以构造ID ,name 等属性一样的但是hidden隐藏的,disabled 不可操作的标签,可以成功干扰它的定位元素过程,并且界面UI不会察觉。这种思路作用于,自动化脚本不是通过ID,NAME来做元素定位,而是通过xpath使用 css selector来做定位的情况。

举个栗子:

201812051544019146607737.png

自动化工具通过xpath语法可以定位上面的搜索位置,上面的ID, name动态方式就无法干扰了。

上面图搜索框上面的hidden的是插入的虚假标签,

未插入前:羊毛程序通过下面的结构避开id,name完成定位

#main > header > div.header-content.clearfix > div.header-search-module > div.header-search-block > input

插入隐藏标签后:羊毛程序需要修改xpath结构,才能找到正确的输入位置。

#main > header > div.header-content.clearfix > div.header-search-module > div.header-search-block > input:nth-child(2)

这里插入的标签是hidden,disabled的,他UI不可见,不会被form表单提交到Server端的。完美的干扰了羊毛程序的查找。

新的思路3:text插入不可见字符/其他字符

上面的思路几乎干扰了webdriver最常用的元素定位方式,剩下的是通过link文本的方式进行定位。Selenium支持根据标签的text值或部分值进行搜索定位元素。我们在原始的text里插入不影响页面UI明显变化的特殊字符,就可以干扰webdriver的这种定位元素的方式。

新的思路4:化被动为主动的对抗思路

这里的反攻思路主要是利用浏览器的缺陷,利用自动化工具的bug,甚至漏洞来反击。这个比较有意思,我们通过GitHub上查看Selenium,Chromedriver, geckodriver的issues,收集广大网友们在自动化测试(薅羊毛)中发现的bug,我们故意构造这样的环境,迫使羊毛程序自己崩溃。用这种方式来干扰阻挡羊毛行动。

0x05 demo一下

长篇大论说了那么多,你一定心里发毛了“show me the code...”。基于上面的多种思路现在开始demo一下,我用mitmproxy模拟反向代理工具,编写python脚本来对原站返回内容做修改,插入特定的JS代码。插入的JS代码实现上面的思路和hook一些Window的事件比如onkeypress, onclick, onchange事件,当触发这些事件时页面上发生一些动态变化,这些动态变化UI上没有明显变化,但是影响了Selenium注入的JS脚本的自动化行为过程。

Demo1­:动态变化id,name值

mitmproxy脚本编写如下,给特定的域名,登录页面注入我的们JS脚本,并放在了body最后,应该能尽量减少对原页面的影响。

201812051544017569103738.png

注入的JS脚本太长限于篇幅不贴出来了,主要实现:

通过document.getElementsByTagName遍历input标签,记录所有标签的id,name,class name值,定义change 函数,用于随机生成新的id,name等值,hook一些事件,当触发事件后调用change函数实现一次动态修改。

最后当然还需要hook 表单提交的onsubmit函数,先还原真实的id,name,class name值,然后走正常提交。

注入JS后的效果:

当页面每一次触发事件,关键标签input的id,name进行一次变化。

201812051544018018166191.png

正常人类在无感知下完成业务的正常工作。

201812051544018322300778.png

使用自动化工具进行测试如下:

首先是没有经过mitmproxy的修改注入JS的,自动化登录成功如下图。

201812051544018394910059.png

然后访问经过代理后的注入JS的地址,自动化登录失败。

201812051544018454832802.png

根据console的提示,可以看出自动化工具在工作中因触发了一些事件,id, name动态变化了,程序无法定位完成对应的数据输入。

Demo2:插入不可见的相同标签

注入JS后的效果,插入了hidden,disabled,id,name等属性相同的标签:

201812051544019562214217.png

当然不能影响正常业务啦,如下:

201812051544019625790680.png

webdriver测试如下:下面是成功干扰了Firefox的程序,它说找到的元素不可用的。。

201812051544019677488025.png

Demo3:text插入不可见字符/其他字符

Selenium 类工具支持一种叫link 定位的操作,有时候不是一个输入框也不是一个按钮,而是一个文字链接,可以通过 link进行定位。

这种情况下,自动化程序不用id, name和xpath结构,直接通过文本查找匹配进行定位。我们为了干扰,能做的就是动态修改这些关键位置的文本,让其程序无法用于定位。

比如下面的测试代码,程序自动打开网页,通过“Sign In”找到登录链接,然后打开,通过“Contact Us”找到联系商家,然后通过“online form” 找到订单页面等。。这一些列自动化过程仅通过页面的文本来查找定位。前面的思路都无法对其干扰,我们如果将页面的“Sign In” 修改为“Sign . In”这样,做一些UI上尽可能小的变化动作。同样可能成功干扰自动化程序。

201812051544020237881426.png

对Webdriver成功干扰效果如下:

201812051544020448896634.png

这里仅能做到一个思路demo的证明,真正要做好还是不容易,因为对我来说还不知道怎么找到,浏览器上UI不显示,但是text是不同的修改办法。

跪求前端大佬指教。。

Demo4:化被动为主动的对抗思路

这里思路还没有完全构思好,用JS反攻客户端的好点子还没有。这里抛2个bug,怎么很好的用bug和JS反攻,期待和大佬的交流。bug地址:https://github.com/SeleniumHQ/selenium/issues/5840https://github.com/mozilla/geckodriver/issues/1228

0x06 总结

本文的思路早也有人提出过,写完本文后才知道早在16年携程就有前辈写文《关于反爬虫,看这一篇就够了》地址:https://blog.csdn.net/u013886628/article/details/51820221,提到过了。提及的内容截图如下:

201812051544021106525820.png

可见携程的反爬虫历史很悠长哦。晚辈献丑了,路漫漫其修远兮,吾将上下而求索。

[撞库测试] Selenium+验证码打码时的特殊情况-【遇到滚动条】

题外话

测试的目标网站如果登录接口有验证码+浏览器环境检测的时候,有时候脚本小子就望而却步了,比如我。因为正面对抗JS的环境检测和验证码是有难度的,这个时候我们可以借助Selenium + 打码平台来搞一搞。这里只做笔记记录,不做具体细节描述,如有兴趣可以私下交流。

测试目标

我们的假定目标如下,某贷网站的登录入口:

201805101525941070537294.png

关键点

  1. 需要自动填充账号、密码
  2. 需要将验证码进行截图,然后接入打码平台SDK
  3. 这里暂时不管短信验证码。

一般情况

使用Selenium进行自动化登录的基本操作是会的,结合打码平台的SDK的操作也是基本的,有时候会遇到验证码是特殊url,页面关联性很强的时候,想要打码,必须使用通过截图打码来完成登录。

关于selenium+验证码截图网上搜一搜有很多,比如你会搜到下面的:

201805101525942436360685.png

上面的这种在windosw上确实OK的

不一般的情况

上面的情况适合一部分Window用户,Mac电脑上就不一样了,多次的实践结果证明Mac上的对验证码截图部分代码应该是下面这样写的:

201805101525942650541222.png

(曾经去B站大佬面前演示过B站可被撞库)

特殊情况

今天遇到了不一样的情况了,这个情况就是开篇截图里的情况。当登录页面有滚动条的时候,上面的2种做法都行不通了。回顾截图的代码,思路是,先整体当前网页全屏截图,然后通过Selenium查找到验证码图片元素,拿到该图片元素的长-宽,以及在页面的location相对位置,然后通过计算得到截图的坐标,通过4个坐标点,进行截图得到了我们想要的验证码图片。

这里的新情况是,页面出现了滚动条,如下图:

201805101525943188398931.png

在有滚动条的时候,验证码图片的相对位置计算方式就不一样了,滚动条向下滚动了,验证码图相对于网页的左上角是更近了一些距离,这个距离就是滚动条的滚动距离。

所以4个点坐标的正确计算方式记录一下应该如下:

201805101525943599804798.png

上面的思路是:

  1. 获取当前滚动条滚动距离。
  2. 创建一个标签,记录该值,然后selenium找到这个标签,拿到这个值。
  3. 验证码元素的相对位置y值需要剪掉滚动的距离。

这样就能拿到验证码图片了

201805101525944044345501.png

剩下的工作就是SDK打码,输入验证码,进行测试了,这就不多说了。。

总结

确认过眼神,就是这么整。

谈谈Selenium Server的安全问题 - 未完

0x01 开篇

不知道大家在平日工作中有没有遇到过一些端口,使用浏览器打开是下面这样子的:

201803041520131466539800.png

上图中我找了几个在不同端口下的例子。

0x02 Selenium-开源的自动化测试利器

本篇主要的主角-Selenium究竟是什么呢?有过QA经验或安全自动化测试经验的朋友应该知道,以下文字来自百度百科:Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。

官网地址:https://www.seleniumhq.org/

Github地址:https://github.com/SeleniumHQ/selenium/wiki/Grid2

Selenium支持本地和远程浏览器的自动化测试,在远程调用浏览器时需要在远程服务器上启动一个SeleniumServer,它会负责远程浏览器的启用和你的自动化脚本的执行。

官方给出的启用该SeleniumServer的命令:

java -jar selenium-server-standalone-<version>.jar -role node  -hub http://localhost:4444/grid/register

那么脚本远程调用可以如下:

//第一个参数:表示服务器的地址。第二个参数:表示预期的执行对象,其他的浏览器都可以以此类推
WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub/"), DesiredCapabilities.chrome());

路径/wd/hub/我是怎么知道的?

  1. 点开console,自动跳转知道的。
  2. 百度selenium remote 知道的
  3. 查看源码发现是默认的- 源码

通过阅读源码能发现默认的端口为4444,如下图所示:

201803051520263000170174.png

下面是一个Python写的远程调用的Demo:

201803041520134142491036.png

0x03 Selenium-server分析

为了方便我们分析,我在Mac下启用一个Sever, 服务端就是一个独立端JAR文件,下载地址:点击下载

可以直接这样启用:

201803041520137267231202.png

浏览器访问,打开console看看,如下图:

201803041520137304734235.png

201803041520139782900165.gif

Selenium Server 给每一个远程调用浏览器进行自动化任务分配一个Session会话,该控制台可以新创建会话,可以在页面上给每一个会话下发自动化脚本到每一个会话对应到浏览器上执行。

观察加载Console页面时,加载了一个叫client.js的文件,从这个文件中可以找到一些有用的调用接口,比如我的地址:http://127.0.0.1:4444/wd/hub/ ,当前的SessionId为,179220de83fee4d6090502b003b692a0 简单整理可以GET访问的URL地址如下:

GET 方式 - BaseUrl = http://127.0.0.1:4444/wd/hub
URL ==> 对应函数
/sessions ==> getSessions 获取当前所有打开浏览器的Session信息
/status ==> getStatus 获取当前Server状态 
/session/179220de83fee4d6090502b003b692a0/url ==> getCurrentUrl  获取当前浏览器打开的URL
/session/179220de83fee4d6090502b003b692a0/alert_text ==> getAlertText  获取弹窗内容
/session/179220de83fee4d6090502b003b692a0/source ==> getPageSource  获取网页源码
/session/179220de83fee4d6090502b003b692a0/screenshot ==> screenshot   网页快照,返回Base64图片
/session/179220de83fee4d6090502b003b692a0/cookie ==> getCookies  获取当前页面Cookie

上面的只是举例说明,完整的接口定义可以去GitHub上看WIKI说明,地址:https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol

现在我们知道了Selenium Server给我们提供了很多API接口以供我们使用来完成我们对远程浏览器对控制操作,下面看看我们就一起研究下能怎么玩?

0x04 构想和实施你的玩法

问题:

在敌人后方,你拿到了一个完全可控的浏览器后,你能做些什么?

针对这种,下面是暂时想到的玩法。

实施对敌方远程浏览器自动化作业的监控(偷视)

利用上面学习的知识,我们通过接口:

http://127.0.0.1:4444/wd/hub/sessions

发现存在着有效的Session正在作业如图:

201803041520146295646570.png

那么我们可以通过自己编写脚本,调用API接口,实时获取该浏览器的信息。如:

比如下面展示的,某一个浏览器正在进行重置密码的-自动化操作,我们可以通过API接口实时拿到该浏览器当前的URL地址。(很关键拿到这个URL就可以为所欲为了。。。)

201803041520146861917279.png

201803041520147053363582.png

当然这个返回的是页面截图的Base64,转化一下如下:

201803041520147160885551.png

如果只是简单的URL监控,思路利用BurpSuite就可以实现,这里我实验了一下,如图:

201803041520147482205223.png

利用这类思路,我们可以自己编写脚本利用其提供的API接口,对网络空间里的这类Selenium Server的行为进行24h的实时监控、记录其xx行为。

以此为跳板,对内网实施攻击

利用Selenium Server对特点,创建或者可控一个正在作业的远程浏览器,向内网发起攻击。

说到利用跳板发起内网攻击,可能最开始想到的是被大家玩出花的SSRF了。相比一般的SSRF漏洞,这里我们手里拿到的攻击筹码是远超过一般的SSRF漏洞的。敌方后防一个完全可控的浏览器,能够下发任意的JS代码,控制浏览器访问任意的URL这简直不能再爽!--- 还在实践ing

file协议任意文件读取

我们知道浏览器支持使用file协议读取本地系统的文件,哪么我们可以利用控制的远程浏览器利用file协议读取系统敏感文件,甚至是重要的shadow等口令文件

在测试实践中,我找了几台靶机尝试读取系统/etc/passwd文件,发现甚至有的可以读取shadow文件。准备测试脚本很简单,几行代码比如:

def test_readfile(driver_url = '', filename=''):
    driver = webdriver.Remote(command_executor='http://' + driver_url + '/wd/hub',
                              desired_capabilities=DesiredCapabilities.CHROME)
    driver.get("file://%s" % filename)
    print driver.page_source

    driver.quit()

然后结果就是这样的:

201803041520169754704399.png

甚至读取到了shadow

201803041520172210136585.png

当然最简单的方式是直接在Console里直接操作,步骤:Create a New Session -> chrome or firefox -> Load Script ->添入 file:///etc/passwd 然后OK -> 然后 Take a ScreenShot你就能看到了。下面是测试的截图

201803041520169348222300.png

看到这里,是不是震惊了?赶紧回去问问你们的研发,你们的QA,有没有用到它,小心菊花不保!!

远程挖矿

近两年来很流行的浏览器挖矿,从去年6月后吧,利用浏览器进行虚拟货币挖矿的事件越来越多来,网站代码中暗藏JS挖矿机脚本

挖矿脚本上GitHub一搜索就能找到很多,Github搜索地址 这里就不做过多测试了,不玩这个!

0x05 网络空间调查

使用Shodan, Censys, Zoomeye, FOFA 等一些知名的网络搜索引擎进行关键字搜索,看看网络空间里有多少Selenium Server以及分布情况。

  • Zoomeye

搜索地址:搜索链接

搜索结果如下图:

201803051520229306291405.png

搜索发现在Zoomeye引擎中记录,在整个网络空间中存在有大约有 1.5万左右的Selenium Server运行着。

  • FOFA引擎

搜索地址:搜索链接

使用FOFA引擎,匿名用户 normal模式进行搜索,获得了1.3k左右的记录

搜索结果:

201803051520230301290760.png

  • Shodan引擎

搜索地址:搜索链接

在SHODAN引擎中关键字搜索,仅44条记录。

搜索结果:

201803051520230575847986.png

SHODAN引擎主要采集基础组件端口信息,采集网页信息较少,所以用网页关键字:SeleniumHQ 搜索的结果较少,但是我们如果搜索Selenium Server 所使用的组件 Jetty,就会发现搜索结果比较多了,如下图所示。这些结果中一定存在一定数量的Selenium Server运行着的。

搜索地址:搜索链接

搜索结果:

201803051520230961203412.png

0x06 进一步研究方向

能想到的可行的研究方向:

  • 网络空间的基于Selenium的自动化攻击监控

从网络空间搜索引擎里采集Selenium Server服务端IP,实时采集并记录其远程浏览器的自动化行为。

  • 主动探测网络空间里部署Selenium Server服务器分布

使用探测工具主动探测存在于网络空间的Selenium Server分布情况,记录和观测这些探测得到的IP,这些IP都有自动化攻击,薅羊毛的潜在可能。

个人博客:http://www.coffeehb.cn