新人第一次分析v8利用,v8的资料在墙内墙外都挺多的,但质量良莠不齐,过程中也是走了不少的弯路,踩了不少的坑,不过最后还是成功复现了一次完整的利用,特此记录一下。
成功弹出计算器
我们仅仅需要关注diff文件的22-42行即可,其它部分的删改只是为了能让v8正确地编译通过。
这个diff文件给Array对象增加了一个oob方法,提供了数组越界读写的功能,不提供参数时为越界读(arr.oob())
提供一个参数时为越界写(arr.oob(val))。
1、辅助输出的js函数,实现浮点数和整数相互转换的功能
2、v8内置的辅助函数
3、不同数据类型的表示方法
做实验:首先创建了一个长度为2的浮点数数组,然后用DebugPrint输出该数组的调试信息。
我们需要关注Array对象的地址和Array对象的map域和element域。
element域可以理解为一个指针,指向的是这个Array具体保存的数据。
map域比较复杂但是很重要,可以简单理解成v8用它来表示这是一个什么样的对象,以及采用什么样的方法来对这个对象进行各种操作。
切换到调试器模式看内存:
map的偏移为0,elements的偏移为0x10
elements指向的区域就在这个Array对象的正前方:
其中0x3ff199999999999a 0x400199999999999a就是1.1和2.2在内存中的64位表示。
再往前的0x00000838ec0414f9 0x0000000200000000则分别代表另一个对象的map域和这个数组的长度(smi类型)
所以题目提供的array.oob()方法恰好能对该array的map域进行越界读写
首先要实现取对象地址功能和在任意地址伪造对象的功能。
刚才已经提到过v8通过对象的map域来决定对象的数据类型以及操作方法。
所以下面我们先定义了一个长度为1的对象数组,其唯一的一个元素是一个对象。
再定义了一个长度为4的浮点数数组。
利用oob方法越界读取出这两个数组的map对象的地址值。
获取任意对象地址:
我们先把需要获取地址的对象放入对象数组中,然后将这个对象数组的map篡改为浮点数组的map域。此时再读取这个对象,由于map域被改成了浮点类型的map域,v8会将这个对象的地址当成一个浮点数返回给我们。
任意地址伪造对象:
与上面功能类似,这里是把浮点数组的index0处的数据改为任意地址,然后把浮点数组的map域篡改成对象数组的map域,然后在获取这个值,v8引擎则会将该地址作为一个对象返回给我们。
接下来实现的是任意地址读取的功能:
先定义一个长度为4的浮点数数组,其index0处放置的是另一个浮点数数组的map域。
然后用上面的fakeobj功能在该map域伪造另一个浮点数数组,那么伪造的这个浮点数数组的element域就对应着原来数组的arb_rw_arr[2],把这个伪造的element域指向要读取地址-0x10,再利用fakeobj完成读取功能即可
任意地址写功能实现起来比较麻烦,不能直接利用fakeobj来任意写(会报错),还需要通过ArrayBuffer和DataView来实现具体的功能,具体的原因和过程我并没深究:
有了任意地址读写功能后的最后一个问题是如何实现任意代码执行。
这里需要提到的是v8内嵌的wasm字节码在内存中是以RWX的权限保存的。
大致的利用思路:
1、分配一个WebAssembly对象。
2、本地调试找到字节码存放的具体地址
3、利用任意写功能把shellcode写到该地址处
4、调用该wasm对象,完成任意代码执行。
1、Exploiting v8: *CTF 2019 oob-v8
2、V8 Exploitation : Star CTF 2019 OOB-v8 | by 0verflowme | Medium
git clone https:
/
/
chromium.googlesource.com
/
chromium
/
tools
/
depot_tools.git
echo
"export PATH=/home/pwn/tools/depot_tools:$PATH"
>> ~
/
.bashrc
fetch v8
cd v8
git checkout
6dc88c191f5ecc5389dc26efa3ca0907faef3598
gclient sync
git
apply
..
/
oob.diff
.
/
tools
/
dev
/
v8gen.py x64.release
ninja
-
C .
/
out.gn
/
x64.release
.
/
tools
/
dev
/
v8gen.py x64.debug
ninja
-
C .
/
out.gn
/
x64.debug
git clone https:
/
/
chromium.googlesource.com
/
chromium
/
tools
/
depot_tools.git
echo
"export PATH=/home/pwn/tools/depot_tools:$PATH"
>> ~
/
.bashrc
fetch v8
cd v8
git checkout
6dc88c191f5ecc5389dc26efa3ca0907faef3598
gclient sync
git
apply
..
/
oob.diff
.
/
tools
/
dev
/
v8gen.py x64.release
ninja
-
C .
/
out.gn
/
x64.release
.
/
tools
/
dev
/
v8gen.py x64.debug
ninja
-
C .
/
out.gn
/
x64.debug
~
/
Desktop
/
v8
/
v8
/
v8
/
out.gn
/
x64.release$ .
/
d8
/
home
/
srp8ve7ou2
/
Desktop
/
v8
/
starctf2019
/
wasm_pwn.js
~
/
Desktop
/
v8
/
v8
/
v8
/
out.gn
/
x64.release$ .
/
d8
/
home
/
srp8ve7ou2
/
Desktop
/
v8
/
starctf2019
/
wasm_pwn.js
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
/
TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
+
CPP(ArrayOob) \
\
/
*
ArrayBuffer
*
/
\
/
*
ES
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:
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
/
TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
+
CPP(ArrayOob) \
\
/
*
ArrayBuffer
*
/
\
/
*
ES
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:
var buf
=
new ArrayBuffer(
8
);
/
/
8
byte array
buffer
var f64_buf
=
new Float64Array(buf);
var u64_buf
=
new Uint32Array(buf);
function ftoi(val) {
/
/
typeof(val)
=
float
f64_buf[
0
]
=
val;
return
BigInt(u64_buf[
0
])
+
(BigInt(u64_buf[
1
]) <<
32n
);
/
/
Watch
for
little endianness
}
function itof(val) {
/
/
typeof(val)
=
BigInt
u64_buf[
0
]
=
Number(val &
0xffffffffn
);
u64_buf[
1
]
=
Number(val >>
32n
);
return
f64_buf[
0
];
}
var buf
=
new ArrayBuffer(
8
);
/
/
8
byte array
buffer
var f64_buf
=
new Float64Array(buf);
var u64_buf
=
new Uint32Array(buf);
function ftoi(val) {
/
/
typeof(val)
=
float
f64_buf[
0
]
=
val;
return
BigInt(u64_buf[
0
])
+
(BigInt(u64_buf[
1
]) <<
32n
);
/
/
Watch
for
little endianness
}
function itof(val) {
/
/
typeof(val)
=
BigInt
u64_buf[
0
]
=
Number(val &
0xffffffffn
);
u64_buf[
1
]
=
Number(val >>
32n
);
return
f64_buf[
0
];
}
1
、
%
DebugPrint(obj);
2
、
%
SystemBreak();
1
、
%
DebugPrint(obj);
2
、
%
SystemBreak();
double浮点数类型:为
64
位浮点数的正常表示
smi整数类型:低
32
位为
0
,高
32
位为该整数
指针类型:最低位永远为
1
double浮点数类型:为
64
位浮点数的正常表示
smi整数类型:低
32
位为
0
,高
32
位为该整数
指针类型:最低位永远为
1
srp8ve7ou2@vm:~
/
Desktop
/
v8
/
v8
/
v8
/
out.gn
/
x64.debug$ gdb d8
GNU gdb (Ubuntu
9.2
-
0ubuntu1
~
20.04
)
9.2
Copyright (C)
2020
Free Software Foundation, Inc.
License GPLv3
+
: GNU GPL version
3
or
later <http:
/
/
gnu.org
/
licenses
/
gpl.html>
This
is
free software: you are free to change
and
redistribute it.
There
is
NO WARRANTY, to the extent permitted by law.
Type
"show copying"
and
"show warranty"
for
details.
This GDB was configured as
"x86_64-linux-gnu"
.
Type
"show configuration"
for
configuration details.
For bug reporting instructions, please see:
<http:
/
/
www.gnu.org
/
software
/
gdb
/
bugs
/
>.
Find the GDB manual
and
other documentation resources online at:
<http:
/
/
www.gnu.org
/
software
/
gdb
/
documentation
/
>.
For
help
,
type
"help"
.
Type
"apropos word"
to search
for
commands related to
"word"
...
GEF
for
linux ready,
type
`gef
' to start, `gef config'
to configure
93
commands loaded
for
GDB
9.2
using Python engine
3.8
[
*
]
3
commands could
not
be loaded, run `gef missing` to know why.
Reading symbols
from
d8...
gef➤ run
-
-
allow
-
natives
-
syntax
Starting program:
/
home
/
srp8ve7ou2
/
Desktop
/
v8
/
v8
/
v8
/
out.gn
/
x64.debug
/
d8
-
-
allow
-
natives
-
syntax
[Thread debugging using libthread_db enabled]
Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1"
.
[New Thread
0x7ffff3a87700
(LWP
15179
)]
V8 version
7.5
.
0
(candidate)
d8> var a
=
[
1.1
,
2.2
]
undefined
d8>
%
DebugPrint(a)
DebugPrint:
0x2013f4c4dd79
: [JSArray]
-
map
:
0x1f8bf2202ed9
<
Map
(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
-
prototype:
0x20ef3d611111
<JSArray[
0
]>
-
elements:
0x2013f4c4dd59
<FixedDoubleArray[
2
]> [PACKED_DOUBLE_ELEMENTS]
-
length:
2
-
properties:
0x0838ec040c71
<FixedArray[
0
]> {
}
-
elements:
0x2013f4c4dd59
<FixedDoubleArray[
2
]> {
0
:
1.1
1
:
2.2
}
0x1f8bf2202ed9
: [
Map
]
-
type
: JS_ARRAY_TYPE
-
instance size:
32
-
inobject properties:
0
-
elements kind: PACKED_DOUBLE_ELEMENTS
-
unused
property
fields:
0
-
enum length: invalid
-
back pointer:
0x1f8bf2202e89
<
Map
(HOLEY_SMI_ELEMENTS)>
-
prototype_validity cell:
0x2c439fb80609
<Cell value
=
1
>
-
instance descriptors
-
layout descriptor: (nil)
-
transitions
0x0838ec044ba1
<Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS)
-
>
0x1f8bf2202f29
<
Map
(HOLEY_DOUBLE_ELEMENTS)>
-
prototype:
0x20ef3d611111
<JSArray[
0
]>
-
constructor:
0x20ef3d610ec1
<JSFunction Array (sfi
=
0x2c439fb8aca1
)>
-
dependent code:
0x0838ec0402c1
<Other heap
object
(WEAK_FIXED_ARRAY_TYPE)>
-
construction counter:
0
[
1.1
,
2.2
]
d8>
srp8ve7ou2@vm:~
/
Desktop
/
v8
/
v8
/
v8
/
out.gn
/
x64.debug$ gdb d8
GNU gdb (Ubuntu
9.2
-
0ubuntu1
~
20.04
)
9.2
Copyright (C)
2020
Free Software Foundation, Inc.
License GPLv3
+
: GNU GPL version
3
or
later <http:
/
/
gnu.org
/
licenses
/
gpl.html>
This
is
free software: you are free to change
and
redistribute it.
There
is
NO WARRANTY, to the extent permitted by law.
Type
"show copying"
and
"show warranty"
for
details.
This GDB was configured as
"x86_64-linux-gnu"
.
Type
"show configuration"
for
configuration details.
For bug reporting instructions, please see:
<http:
/
/
www.gnu.org
/
software
/
gdb
/
bugs
/
>.
Find the GDB manual
and
other documentation resources online at:
<http:
/
/
www.gnu.org
/
software
/
gdb
/
documentation
/
>.
For
help
,
type
"help"
.
Type
"apropos word"
to search
for
commands related to
"word"
...
GEF
for
linux ready,
type
`gef
' to start, `gef config'
to configure
93
commands loaded
for
GDB
9.2
using Python engine
3.8
[
*
]
3
commands could
not
be loaded, run `gef missing` to know why.
Reading symbols
from
d8...
gef➤ run
-
-
allow
-
natives
-
syntax
Starting program:
/
home
/
srp8ve7ou2
/
Desktop
/
v8
/
v8
/
v8
/
out.gn
/
x64.debug
/
d8
-
-
allow
-
natives
-
syntax
[Thread debugging using libthread_db enabled]
Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1"
.
[New Thread
0x7ffff3a87700
(LWP
15179
)]
V8 version
7.5
.
0
(candidate)
d8> var a
=
[
1.1
,
2.2
]
undefined
d8>
%
DebugPrint(a)
DebugPrint:
0x2013f4c4dd79
: [JSArray]
-
map
:
0x1f8bf2202ed9
<
Map
(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
-
prototype:
0x20ef3d611111
<JSArray[
0
]>
-
elements:
0x2013f4c4dd59
<FixedDoubleArray[
2
]> [PACKED_DOUBLE_ELEMENTS]
-
length:
2
-
properties:
0x0838ec040c71
<FixedArray[
0
]> {
}
-
elements:
0x2013f4c4dd59
<FixedDoubleArray[
2
]> {
0
:
1.1
1
:
2.2
}
0x1f8bf2202ed9
: [
Map
]
-
type
: JS_ARRAY_TYPE
-
instance size:
32
-
inobject properties:
0
-
elements kind: PACKED_DOUBLE_ELEMENTS
-
unused
property
fields:
0
-
enum length: invalid
-
back pointer:
0x1f8bf2202e89
<
Map
(HOLEY_SMI_ELEMENTS)>
-
prototype_validity cell:
0x2c439fb80609
<Cell value
=
1
>
-
instance descriptors
-
layout descriptor: (nil)
-
transitions
0x0838ec044ba1
<Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS)
-
>
0x1f8bf2202f29
<
Map
(HOLEY_DOUBLE_ELEMENTS)>
-
prototype:
0x20ef3d611111
<JSArray[
0
]>
-
constructor:
0x20ef3d610ec1
<JSFunction Array (sfi
=
0x2c439fb8aca1
)>
-
dependent code:
0x0838ec0402c1
<Other heap
object
(WEAK_FIXED_ARRAY_TYPE)>
-
construction counter:
0
[
1.1
,
2.2
]
d8>
gef➤ x
/
4gx
0x2013f4c4dd79
-
1
0x2013f4c4dd78
:
0x00001f8bf2202ed9
0x00000838ec040c71
0x2013f4c4dd88
:
0x00002013f4c4dd59
0x0000000200000000
gef➤ x
/
4gx
0x2013f4c4dd79
-
1
0x2013f4c4dd78
:
0x00001f8bf2202ed9
0x00000838ec040c71
0x2013f4c4dd88
:
0x00002013f4c4dd59
0x0000000200000000
gef➤ x
/
10gx
0x2013f4c4dd59
-
1
0x2013f4c4dd58
:
0x00000838ec0414f9
0x0000000200000000
0x2013f4c4dd68
:
0x3ff199999999999a
0x400199999999999a
0x2013f4c4dd78
:
0x00001f8bf2202ed9
0x00000838ec040c71
0x2013f4c4dd88
:
0x00002013f4c4dd59
0x0000000200000000
0x2013f4c4dd98
:
0x00000838ec040941
0x00000adce0993e5a
gef➤ x
/
10gx
0x2013f4c4dd59
-
1
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-8-16 17:02
被afa9040e编辑
,原因: