Rating: 5.0

# SECCON 2020 Online CTF - Capsule & Beginner's Capsule - Author's Writeup

## Author

@hakatashi

## Challenge Summary

The task is quite obvious from the appearence of the given website.

![](https://i.imgur.com/6WZBHgQ.png)

You can arbitrarily change the code below the given header and the task is to get the value stored in ESNext's [Private Class Field](https://v8.dev/features/class-fields#private-class-fields). *Decapsulation.*

```js
const fs = require('fs');
const {enableSeccompFilter} = require('./lib.js');

class Flag {
#flag;
constructor(flag) {
this.#flag = flag;
}
}

const flag = new Flag(fs.readFileSync('flag.txt').toString());
fs.unlinkSync('flag.txt');

enableSeccompFilter();

// input goes here
```

`enableSeccompFilter` function is just for preventing dumb solution to read out `/proc/self/mem` so you don't have to care about it.

## Capsule: Intended Solution

You can access [V8's inspector API](https://chromedevtools.github.io/devtools-protocol/) from [Node.js](https://nodejs.org/api/inspector.html) and get hidden property like this.

Note that you must put `flag` variable to the global scope to inspect.

```js
global.flag = flag;
const inspector = require('inspector');
const session = new inspector.Session();
session.connect();
session.post('Runtime.evaluate', {expression: 'flag'}, (e, d) => {
session.post('Runtime.getProperties', {objectId: d.result.objectId}, (e, d) => {
console.log(d.privateProperties[0].value.value);
});
});
```

(I didn't know it, but this is mentioned [here](https://github.com/nodejs/node/issues/27404#issuecomment-569924796))

## Capsule: Unintended Solution

You can inject fake `require` function using [function hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting) and read `flag.txt` before it is removed.

```js
function require() {
const fs = process.mainModule.require('fs');
console.log(fs.readFileSync('flag.txt').toString());
}
```

So basic... :man-facepalming:

## Capsule: Unintended Solution

We expected Node.js cannot directly read memory without `/proc/self/mem`, but actually it is possible using [v8.getHeapSnapshot](https://nodejs.org/api/v8.html#v8_v8_getheapsnapshot).

```js
const v8 = require('v8');
const memory = v8.getHeapSnapshot().read();
const index = memory.indexOf('SEC' + 'CON');
const len = memory.slice(index).indexOf('}');
const flagBuffer = memory.slice(index, index + len + 1);
console.log(flagBuffer.toString());
```

Original writeup (https://hackmd.io/@hakatashi/ryLh2okDD).