反制 Webdriver - 从 Bot 到 RCE 进发
时间:2021年4月16日
什么是Webdriver?
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import selenium
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException
import os
chromedriver = "./chromedriver_win32.exe"
browser = webdriver.Chrome(executable_path=chromedriver)
url = "https://lorexxar.cn"
browser.get(url)
# browser.quit()
Chrome Webdriver攻击与利用
在了解了Webdriver基础之后,我们一起来探讨一些整个流程中到底有什么样得安全隐患。
任意文件读?
Page.navigate
访问相应的url,包括file协议
Runtime.evaluate
来执行任意js
http://127.0.0.1:<CDP Port>/json/list
读取相应的webSocketDebuggerUrl 呢?至少我们没办法使用任何非0day来轻易的绕过同源策略的限制,那么我们就需要继续探索~
通过REST API来RCE
GET /sessions
从这个端点我们可以获取到所有目前活跃webdriver 进程的session,并且获取相应的session id.
GET /session/{sessionid}/source
如果我们获取到Session id,那么我们就可以获取到对应session的各种数据,比如页面内容。
POST /session 通过POST数据我们可以发起一个新的会话,并且其中允许我们通过POST参数来配置新会话。
Host header or origin header is specified and is not whitelisted or localhost.
std::string origin_header = info.GetHeaderValue("origin");
,也就是说,是当发送请求头中带Origin时,才会导致这个校验,众所周知,只有当使用js发送POST请求时,才会自动带上这个头,换言之,这里的校验并不会影响我们发送GET请求。
allow_remote
为假,就一定回进入判断,也就一定会经过
net::IsLocalhost
的校验,而这里的
allow_remote
默认为假,只有当开启allow-ips的时候才会为真。所以结论和原文相同。
无论任何类型的请求HOST都需要经过net::IsLocalhost校验
如果带有Origin头,那么Origin头数据也需要经过net::IsLocalhost校验
GET请求不会检查HOST
POST请求:
如果带有Origin头,那么Origin头数据需要经过net::IsLocalhost校验。
如果不带有Origin头,那么没有额外的校验。(如何用js完成没有Origin的post请求呢?)
如果HOST为ip:port格式,那么ip需要在whitelist中。
--allowed-ips
参数时,我们可以通过绑定域名来发起GET请求对应的API。否则我们就必须让HOST通过检查,但可惜的是,仅有ip和localhost能通过
net::IsLocalhost
校验。我们可以简单验证这一点。
配合DNS Rebinding来读取GET返回
var i = 0;
var sessionid;
function waitdata(){
fetch("http://r.d73ha3.ceye.io:22827/sessions", {
method: "GET",
mode: "no-cors"
}).then(res => res.json()).then(res => function () {
if(res.value){
sessionid = res.value[0].id;
}
}());
stopwait();
}
function stopwait(){
if(sessionid!=undefined){
console.log(sessionid);
clearInterval(t1);
}
}
t1 = setInterval('i +=1;console.log("wait dns rebinding...test "+i);waitdata()',1000);
attack chain!
写在最后
在前文中提到过,不同的浏览器会采用专属自己的浏览器协议,但其中差异比较大的是firefox和对应的Geckodriver,在Geckodriver上,firefox设计了一套与chrome逻辑差异比较大的调试协议,在原文中,作者使用了一个TCP连接拆分错误来完成相应的利用,并且在Firefox 87.0当中被修复。而safaridriver实现了更严格的host检查,导致DNS rebinding漏洞并不能生效。而包括chrome、MS Edge 和 Opera在内的浏览器仍然受到这个漏洞威胁。
--allowed-ips
这个配置却非常的少见,在普遍通过Selenium来操作webdriver的场景中,一般的用户都只会配置Chrome的参数选项,而不是webdriver的参数,而且在官网中也明确提出
--allowed-ips
会导致可能的安全问题。
References
[1]
@cursered: https://twitter.com/cursered[2]
《You Talking To Me?》: https://starlabs.sg/blog/2021/04/you-talking-to-me/[3]
webdriver协议: https://www.w3.org/TR/webdriver/
往 期 热 门
(点击图片跳转)
