介绍
在Chromium v8中x64平台的指令优化中发现了UAF漏洞。成功利用此漏洞可以允许攻击者在浏览器上下文中执行任意代码。
该漏洞是由于v8在优化结束之后,指令选择阶段,选择了错误的指令,导致的内存破坏漏洞,成功利用此漏洞,可以达到代码执行的效果。
该漏洞发生在
https://chromium.googlesource.com/v8/v8/+/71a9fcc950f1b8efb27543961745ab0262cda7c4^
之前(含本次提交),如果想重现此漏洞,可以同步代码到此次提交之上。
**注:Google已发布紧急补丁更新,请及时下载安装。**
代码主要的功能及原理:
1.首先创建一个typed array数组,大小为0x10000
2.然后判断a[0]的值是否为非零,然后结果赋值给b
3.然后进行垃圾回收
4.如果b为true,打印字符串boom
运行上面的代码输出的结果:
图1:内存泄漏错误
可以看到,访问违规的地址就是a的data_ptr: 0x7f71576a0000
图2: 访问违规地址数组a
代码首先,调用了runtime函数gc,然后[r8],0做对比:
图3: 调用gc时的汇编指令
漏洞产生原因
在前两次运行的时候,v8知道对数组a的使用只是在步骤2处,后续并没有访问数组a,所以在优化编译阶段,gc把v8的数组存放的内存给回收了,数组a的内存被释放了,但是由于v8在x64平台上错误的生成了指令,导致在4处访问到了已经被回收的页面,造成了内存访问异常,也就是在0x7f6e29984162地址处,又一次的访问已经被释放的数组a的内存,从而导致crash。
优化过程:
if(b)=>if(a[0]!=0)=>Word32Equal(a[0],0) =>[cmp [r8],0 ; jnz xxxx]
因此,在执行第4步时,它正在访问已回收的同一位置,导致地址0x7f6e29984162的内存访问异常,其中再次访问已释放的数组a的内存,导致内存损坏和崩溃。
图4 :漏洞出发流程图
补丁对比
修补代码删除了CanCoverForCompareZero函数,恢复了CanCover的使用CanCoverForCompareZero的作用是用来代替在函数VisitWordCompareZero中canCover。如果canCover返回了false,但是这个节点是一个比较的节点,就不需要其他的任何寄存器,可以被user节点给cover住。
一般情况下这个没有问题,但是问题恰恰出现在当访问内存的时候,此时生成的代码会直接去比较内存,而不是先获取比较结果,在去进行比较,但是当在第二次访问内存的时候,调用gc,就会造成UAF。
这时候会调用这个函数,用来访问a的数组buffer,如下图所示:
在这里生成转化为具体的指令
因为易受攻击的代码采用CanCoverForCompareZero为true的路径,从而生成不同的指令序列(易受攻击版本VS固定版本)。
指令序列的详细分析
在分析了Turbofan的优化阶段后,我们知道这个问题发生在最后两个阶段:
在计划阶段,节点信息完全一致,但之后变得不同,我们找到了相关的指令序列。
事实上,相应的js代码是const b=a[0]!=0;
修补前:
修补之后:
乍一看,它似乎完全相同,但由于漏洞,这里生成的代码不同(请注意,114的左侧,修复前是一个点,修复后是一个圆圈和一个点,这意味着修复前的114行不是直接生成的指令,而是从33个输入中提取,以生成优化的指令序列——指令折叠。
在易受攻击代码中,当调用VisitWordCompareZero时,CanCoverForCompareZero将返回true,因为Word32Equal节点是一个比较类型位置。由于后续访问是cmp指令,v8不需要在注册表中存储比较结果,但假设后续访问(即if(b)语句)可以再次直接访问数组a的内存,并最终生成包含更少指令序列的优化代码。
这就是注册表分配之前发生的事情:
修复前:
修复前生成的相关指令:
修复后:
修复后生成的相关说明:
生成25行指令序列:
生成26行指令序列:
比较修复前后的2组指令,条件指令的差异与setnzl VS
setzl相反,因为修复前的代码在函数VisitWordCompareZero中执行cont->OverwriteAndNegateIfEqual(kEqual)。
以下显示了修复漏洞后的TurboFan程序集指令(注意生成的代码指令序列比修复之前更长):
图5: 漏洞修补后的TurboFan程序集说明
在图5中,我们可以看到比较结果首先放在堆栈[rbp-0x28]上,然后将[rbp-0x28]与0进行比较,在这种情况下,它将不会访问gc中的内存,因此不会出现UAF问题。
修补代码对比
-// Used instead of CanCover in VisitWordCompareZero: even if CanCover(user,
-// node) returns false, if |node| is a comparison, then it does not require any
-// registers, and can thus be covered by |user|.
-bool CanCoverForCompareZero(InstructionSelector* selector, Node* user,
- Node* node) {
- if (selector->CanCover(user, node)) {
- return true;
- }
- // Checking if |node| is a comparison. If so, it doesn't required any
- // registers, and, as such, it can always be covered by |user|.
- switch (node->opcode()) {
-#define CHECK_CMP_OP(op) \
- case IrOpcode::k##op: \
- return true;
- MACHINE_COMPARE_BINOP_LIST(CHECK_CMP_OP)
-#undef CHECK_CMP_OP
- default:
- break;
- }
- return false;
-}
-
} // namespace
// Shared routine for word comparison against zero.
@@ -2516,7 +2494,7 @@
cont->Negate();
}
- if (CanCoverForCompareZero(this, user, value)) {
+ if (CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kWord32Equal:
cont->OverwriteAndNegateIfEqual(kEqual);
@@ -2536,7 +2514,7 @@
case IrOpcode::kWord64Equal: {
cont->OverwriteAndNegateIfEqual(kEqual);
Int64BinopMatcher m(value);
- if (m.right().Is(0) && CanCover(user, value)) {
+ if (m.right().Is(0)) {
// Try to combine the branch with a comparison.
Node* const eq_user = m.node();
Node* const eq_value = m.left().node();
@@ -2646,6 +2624,7 @@
break;
}
}
+
// Branch could not be combined with a compare, emit compare against 0.
VisitCompareZero(this, user, value, kX64Cmp32, cont);
}
(https://chromium.googlesource.com/v8/v8/+/71a9fcc950f1b8efb27543961745ab0262cda7c4^!/#F0)
对比修补的代码,我们看到,恢复了canCover函数,删除了CanCoverForCompareZero,这样就不会将对比的节点这种情况来进去到这个switch
case里,也就不会产生错误的指令生成。
利用思路
本漏洞通过堆喷的方式,覆盖a的数组内存,然后转化为类型混淆漏洞,之后再利用类型混淆,来实现读写的能力,最后达到RCE的效果,感兴趣的话可以自行尝试构造。
最后
网络安全学习路线(就业版)
再次声明,此学习路线主打就业方向,如果只是感兴趣,想成为什么黑客的朋友可以划走了!
网络安全≠黑客
很多人上来就说想做想入行网络安全,但是连方向都没搞清楚就开始学习,最终也只是会无疾而终!黑客是一个大的概念,里面包含了许多方向,不同的方向需要学习的内容也不一样。
网络安全再进一步细分,还可以划分为:网络渗透、逆向分析、漏洞攻击、内核安全、移动安全、破解PWN等众多子方向。今天的这篇,主要是针对网络渗透方向,其他方向仅供参考,学习路线并不完全一样,有机会的话我再单独梳理。
今天,就为大家整理一份自己自学网络安全企业级的最主流的职业规划路线学习流程:
学前感言
- 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了.
- 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发.
- 3.有时多google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答.
- 4.遇到实在搞不懂的,可以先放放,以后再来解决
第一步:明确的学习路线
你肯定需要一份完整的知识架构体系图。
如图片过大被平台压缩导致模糊,可以在扫码下载高清无水印版

第二步:阶段性的学习目标&规划
企业级:初级网络安全工程师
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)
2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)
4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据 转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固
6、Web渗透(1周)
①HTML、CSS和JavaScript简介 ②OWASP Top10 ③Web漏洞扫描工具 ④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)
以上学习路线附全套教程由我个人支付上万元在培训机构付费购买,如有朋友需要扫码下方二维码免费获取,无偿分享!

一些我收集的网络安全自学入门书籍
一些我自己买的、其他平台白嫖不到的视频教程:
如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k。
进阶:脚本编程(初级/中级/高级)
在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力。
零基础入门,建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习; 搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP, IDE强烈推荐Sublime; ·Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完; ·用Python编写漏洞的exp,然后写一个简单的网络爬虫; ·PHP基本语法学习并书写一个简单的博客系统; 熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选); ·了解Bootstrap的布局或者CSS。
高级网络安全工程师
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,贴一个大概的路线。感兴趣的童鞋可以研究一下。
学习资料
最后还是给大家分享一下我整理的这份【282G】的网络安全工程师从入门到精通的学习资料包,需要的小伙伴可以加我vx免费领取!后续会展示资料包的部分内容哦!
![img](https://img-blog.csdnimg.cn/img_convert/37ea220c018321d9c75965e418d55385.jpeg
4.面试题
当你学完整个教程最终目的还是为了就业,那么这一套各个大厂的内部面试题汇总你一定没理由错过,对吗?
5.其他
最后还有小白最需要的安装包和源码等更多资源,这里就不一一展示了,需要的小伙伴可以看文中内容免费领取全套合集大礼包
结语
最后我想说,人生艰难,职业发展也不容易。这些除了依靠个人努力,更需要天时地利人和等外在条件。这不是个人做一点选择就可以搞定的,劝退、转行容易,但是想要因此一帆风顺、平步青云难。
人生不只是选择题,不是会选就能赢,更何况很多人的选择还是盲目做出的。我理解很多人因为行情不好而焦虑,因为焦虑而打退堂鼓。但相比于因为受到鼓动草草做出一个前途未卜的决定,倒不如冷静下来思考一下,自己究竟想要什么,当下能够做点什么。有没有什么事情是当下就可以着手去做,并努力做好的?
仰望天空容易,砥砺前行难。愿我们都能沉着冷静,多做难事,踏实笃行。