# TSG CTF 2021 Beginner's Web 2021 Writeup
## Challenge Summary
You are given some converter website.
It is doing some weird job. First, it constructs routes object based on action parameter and store it to the session.
```javascript=const setRoutes = async (session, salt) => {    const index = await fs.readFile('index.html');
    session.routes = {        flag: () => '*** CENSORED ***',        index: () => index.toString(),        scrypt: (input) => crypto.scryptSync(input, salt, 64).toString('hex'),        base64: (input) => Buffer.from(input).toString('base64'),        set_salt: async (salt) => {            session.routes = await setRoutes(session, salt);            session.salt = salt;            return 'ok';        },        [salt]: () => salt,    };
    return session.routes;};```
Then, it will render the page based on the routes object.
```javascript=app.get('/', async (request, reply) => {    // omitted
    const {action, data} = request.query || {};
    let route;    switch (action) {        case 'Scrypt': route = 'scrypt'; break;        case 'Base64': route = 'base64'; break;        case 'SetSalt': route = 'set_salt'; break;        case 'GetSalt': route = session.salt; break;        default: route = 'index'; break;    }
    reply.type('text/html')    return session.routes[route](data);});```
## Solution
![](https://i.imgur.com/8q7R2If.gif)
The flag is in the `flag` route, so you will want to set `session.salt = 'flag'`, but by doing so, `flag` route will be overwritten by `[salt]` end point and you will lose access to it.
So, what we want to achieve is the following session state.
```javascriptsession = {    routes: {        flag: ...,        index: ...,        ...        [salt]: ..., // salt is anything different from 'flag'    },    salt: 'flag',}```
### 1st request
First, you have to send `GET /?action=SetSalt&data=flag` to set `salt = 'flag'`. The session will be the following structure.
```javascriptsession = {    routes: {        flag: ..., // this route is overwritten and not accessible        index: ...,        ...        flag: ...,    },    salt: 'flag',}```
### 2nd request
Second is the most important part. Now we want to recover `session.routes` so that we can access the `flag` route, but we don't want to update `salt`.
The key is `set_salt` function. Normally, it updates routes and salt together.
```javascript=set_salt: async (salt) => {    session.routes = await setRoutes(session, salt);    session.salt = salt;    return 'ok';},```
What if line 2 is executed but line 3 is NEVER executed? It is possible.
In line 2, we are `await`-ing the execution of `setRoutes` function. Do you know `async`/`await` in ECMAScript is just a syncax sugar of `Promise`? So, we can transform this function to the following equivalent code.
```javascript=set_salt: (salt) => {    return setRoutes(session, salt).then((result) => {        session.routes = result;        session.salt = salt;        return 'ok';    });},```
The key is that the code is calling the chained method `then()` from the returned value of `setRoutes` function. What is the return value of this function?
```javascript=const setRoutes = async (session, salt) => {    const index = await fs.readFile('index.html');
    session.routes = {        // omitted        [salt]: () => salt,    };
    return session.routes;};```
It is returning the result of `session.routes`. This is unnecessary since the assignment to `session.route` is already done. 
Okay, we can control this value by `salt` parameter. What if we set `salt = 'then'`? It will return the following object.
```javascript{    // omitted    then: () => salt,}```
As you can infer from the above code, this `then` method will be called with callback function as an argument. If the function is called, the returned value is considered to be `resolve`-ed and the process continues. But, this `then()` method is just ignoring the argument and the function is never called.
So, by setting `salt = 'then'`, the assignment to `session.routes` happens inside `setRoutes` function, but `setRoute` function is not resolved and the assignment to `session.salt` never happens.
So, send `GET /?action=SetSalt&data=then` to server and this will result in the following session state.
```javascriptsession = {    routes: {        flag: ...,        index: ...,        ...        then: ...,    },    salt: 'flag',}```
This is what we want to achieve!
### 3rd request
Now, just request the salt and it will print the flag!
`GET /?action=GetSalt`
```javascript=return session.routes[route](data); // route = session.salt here```
### Appendix
In technical term, this is called [Thenable Object](https://masteringjs.io/tutorials/fundamentals/thenable).
## Only 1 solve during contest? Why the fuck is this beginner????
Several reasons.
* This challenge does not require experience in CTF. It consists of the combination of some logical errors in the code and knowledge of JavaScript itself.* A member of TSG, who is actually a beginner of CTF, could solve this challenge in just 3 hours.* ~~I thought more people solve this challenge honestly~~