前言
Javascript中的数组和数组对象一直都是编程人员优化的主要目标,一般来说,数组只会包含一些基本类型数据,比如说32位整数或字符等等。因此,每个引擎都会对这些对象进行某些优化,并提升不同元素类型的访问速度和密集型表示。
在JavaScriptCore中,JavaScript引擎是在WebKit中实现的,其中每一个存储在对象中的元素都代表着一个IndexingType值,一个8位整数代表一套Flag组合,具体的参数定义可以在IndexingType.h中找到。接下来,引擎会检测一个对象中indexing的类型,然后决定使用哪一条快速路径,其中最重要的一种indexing类型就是ArrayWithUndecided,它表示的是所有元素均为未定义(undefined),而且没有存储任何实际的值。在这种情况下,引擎为了提升性能,会让这些元素保持未初始化。
分析
下面,我们一起看一看旧版本中实现Array.prototype.concat的代码(ArrayPrototype.cpp):
EncodedJSValueJSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
{
...
unsigned resultSize =checkedResultSize.unsafeGet();
IndexingType firstType =firstArray->indexingType();
IndexingType secondType =secondArray->indexingType();
IndexingType type =firstArray->mergeIndexingTypeForCopying(secondType); // [[ 1 ]]
if (type == NonArray ||!firstArray->canFastCopy(vm, secondArray) || resultSize >=MIN_SPARSE_ARRAY_INDEX) {
...
}
JSGlobalObject* lexicalGlobalObject =exec->lexicalGlobalObject();
Structure* resultStructure =lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type);
if(UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
return JSValue::encode(jsNull());
ASSERT(!lexicalGlobalObject->isHavingABadTime());
ObjectInitializationScopeinitializationScope(vm);
JSArray* result =JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure,resultSize);
if (UNLIKELY(!result)) {
throwOutOfMemoryError(exec, scope);
return encodedJSValue();
}
if (type == ArrayWithDouble) {
[[ 2 ]]
double* buffer =result->butterfly()->contiguousDouble().data();
memcpy(buffer,firstButterfly->contiguousDouble().data(), sizeof(JSValue) *firstArraySize);
memcpy(buffer + firstArraySize,secondButterfly->contiguousDouble().data(), sizeof(JSValue) *secondArraySize);
} else if (type != ArrayWithUndecided) {
...
这个函数主要用来判断结果数组[[1]]的indexing类型,我们可以看到,如果indexing类型为ArrayWithDouble,它将会选择[[2]]作为快速路径。接下来,我们看一看:
mergeIndexingTypeForCopying的实现代码,这个函数主要负责在Array.prototype.concat被调用时,判断结果数组的indexing类型:
inlineIndexingType JSArray::mergeIndexingTypeForCopying(IndexingType other)
{
IndexingType type = indexingType();
if (!(type & IsArray && other& IsArray))
return NonArray;
if (hasAnyArrayStorage(type) ||hasAnyArrayStorage(other))
return NonArray;
if (type == ArrayWithUndecided)
return other; [[ 3 ]]
...
我们可以看到在这种情况下,有一个输入数组的indexing类型为ArrayWithUndecided,结果indexing类型将会是另一个数组的indexing类型。因此,如果我们我们用一个indexing类型为ArrayWithUndecided的数组和另一个indexing类型为ArrayWithDouble的数组去调用Array.prototype.concat方法的话,我们将会按照快速路径[[2]]运行,并将两个数组进行拼接。
这段代码并不能保证这两个“butterfly”(JavaScript引擎攻击技术里的一种概念,详情请参考【这篇文章】)在代码调用memcpy之前能够正确初始化。这也就意味着,如果我们能够找到一条允许我们创建一个未初始化数组并将其传递给Array.prototype.concat的代码路径,那我们就能够在堆内存中拥有一个包含了未初始化值的数组对象了,而且它的indexing类型还不是ArrayWithUndecided。从某种程度上来说,这个安全问题跟lokihardt在2017年报告的一个 旧漏洞有些相似,只不过利用方式不同。
在创建这种数组对象时,可以利用NewArrayWithSize DFG JIT的操作码来实现,在对FTLLowerDFGToB3.cpp中FTL所实现的allocateJSArray操作码进行分析之后,我们可以看到这个数组将会包含未初始化的值。引擎根本不需要对数组进行初始化,因为这个数组的indexing类型为ArrayWithUndecided。
ArrayValuesallocateJSArray(LValue publicLength, LValue vectorLength, LValue structure,LValue indexingType, bool shouldInitializeElements = true, boolshouldLargeArraySizeCreateArrayStorage = true)
{
[ ... ]
initializeArrayElements(
indexingType,
shouldInitializeElements ?m_out.int32Zero : publicLength, vectorLength,
butterfly);
...
voidinitializeArrayElements(LValue indexingType, LValue begin, LValue end, LValuebutterfly)
{
if (begin == end)
return;
if (indexingType->hasInt32()) {
IndexingType rawIndexingType =static_cast<IndexingType>(indexingType->asInt32());
if (hasUndecided(rawIndexingType))
return; // [[ 4 ]]
语句new Array(n)在被FTL JIT编译时将会触发[[4]],然后返回一个indexing类型为ArrayWithUndecided的数组,其中就包含未初始化的元素。
漏洞利用
清楚了之前所介绍的漏洞原理之后,想必触发这个漏洞也并非难事:我们可以不断重复调用一个使用new Array()方法来创建数组的函数,然后调用concat方法将这个数组和一个只包含double类型数据的数组进行拼接。在调用够足够次数之后,FTL编译器将会对其进行编译。
这份【漏洞利用代码】可以利用这个漏洞来泄漏一个目标对象的内存地址,实现机制是通过我们所创建的对象进行内存喷射,在触发这个漏洞之后,我们就能够从代码所返回的数组中找到目标对象的地址了。
总结
这个漏洞目前已经在iOS 12和macOS Mojave的最新版本(Safari)中修复了,该漏洞的CVE编号为CVE-2018-4358。
- 上一篇:欲求不满之 Redis Lua脚本的执行原理
- 下一篇:交易所漏洞之薅羊毛分析
猜你还喜欢
- 07-25渗透测试培训(第五期)
- 08-08高通曝Quadrooter高危漏洞, 影响全球9亿安卓用户
- 06-23看我如何从漏洞公告入手黑掉一台打印机
- 11-22iPhone锁屏却锁不住个人信息,iOS安全性真的很高吗?
- 05-07AppStore充值漏洞,王者荣耀等热门游戏可免费充值
- 11-10代码安全保障技术趋势前瞻
- 01-2815岁男孩假扮成CIA官员黑掉高度敏感的信息
- 08-03Google宣布对其域名启用HSTS协议
- 11-16美国大型成人网站遭黑,4.12亿用户数据曝光
- 11-14撸翻希拉里的胖子黑客成长史
- 随机文章
-
- FuzzScanner:信息搜集开源小工具
- 对某网的一次渗透测试纪实
- Metasploit之文件解析远程代码执行(NAT穿透+IIS发布)
- 通过Thinkphp框架漏洞所发现的安全问题
- 端口扫描系统实践心得
- B站后台疑似“被开源”数小时,官方回应内容两次秒删
- 看我如何揪出远控背后的幕后黑手
- Mad-Metasploit:一款多功能Metasploit自定义模块、插件&资源脚本套件
- 冲突的公链!来自P2P协议的异形攻击漏洞
- Dirmap:一款高级Web目录文件扫描工具
- 渗透测试中信息收集的那些事
- 另一种Tomcat渗透Getshell技巧
- “找回密码”邮件骚操作之帐户接管
- 对过WAF的一些认知
- 挖洞经验 | 通过购物车参数篡改实现低价免费购买商品
- 防代码泄漏的监控系统架构与实践
- 针对Memcached缓存服务器的渗透测试方法介绍
- 聊聊安全测试中如何快速搞定Webshell
- 分享两个利用sudo token实现提权的技巧
- Proxy_Pool自动化代理搜集+评估+存储+展示工具
- 热门文章
-
- 八年专业安全团队承接渗透入侵维护服务
- Emlog黑客站模板“Milw0rm”发布
- Stuxnet纪录片-零日 Zero.Days (2016)【中文字幕】
- SQLMAP的注入命令以及使用方法
- 白帽故事汇:网络安全战士从来不是「男生」的专利
- 编辑器漏洞手册
- web安全之如何全面发现系统后台
- 常见Web源码泄露总结
- 渗透测试培训(第五期)
- 深入理解JAVA反序列化漏洞
- cmseasy前台无需登录直接获取敏感数据的SQL注入(有POC证明)
- 网站后台登陆万能密码
- 黑麒麟2016渗透培训系列教程
- 破解emlog收费模板“Begin”
- 那些强悍的PHP一句话后门
- Android平台渗透测试套件zANTI v2.5发布(含详细说明)
- 渗透工具BackTrack与KaliLinux全套视频教程
- Python列为黑客应该学的四种编程语言之一 初学者该怎么学
- CVE-2017-11882漏洞复现和利用
- 恶意程序报告在线查询工具
文章存档
- 2021年3月(4)
- 2020年12月(4)
- 2020年11月(5)
- 2020年10月(8)
- 2020年9月(8)
- 2020年8月(20)
- 2020年7月(47)
- 2020年6月(70)
- 2020年5月(41)
- 2020年4月(21)
- 2020年3月(120)
- 2020年2月(26)
- 2019年12月(12)
- 2019年11月(13)
- 2019年10月(17)
- 2019年9月(15)
- 2019年8月(13)
- 2019年7月(15)
- 2019年6月(15)
- 2019年5月(19)
- 2019年4月(23)
- 2019年3月(19)
- 2019年2月(11)
- 2019年1月(29)
- 2018年12月(24)
- 2018年11月(56)
- 2018年10月(79)
- 2018年9月(20)
- 2018年8月(17)
- 2018年7月(16)
- 2018年6月(7)
- 2018年5月(10)
- 2018年3月(6)
- 2018年2月(2)
- 2018年1月(11)
- 2017年11月(18)
- 2017年10月(6)
- 2017年9月(8)
- 2017年8月(7)
- 2017年7月(7)
- 2017年6月(15)
- 2017年5月(30)
- 2017年4月(7)
- 2017年3月(1)
- 2017年2月(4)
- 2017年1月(1)
- 2016年12月(3)
- 2016年11月(7)
- 2016年10月(6)
- 2016年9月(6)
- 2016年8月(102)
- 2016年7月(24)
- 2013年7月(1)
- 文章标签
-