![challenge description](https://i.imgur.com/scC1klM.png)

Going to the homepage link in the description, we can see a page with a
login/register form.

![home](https://i.imgur.com/okH0ixP.png)

After registering, we can see a textbox with a string and we can see that a
cookie is set with a JWT ([Json Web Token](https://jwt.io/)).

![saved_cookie](https://i.imgur.com/wFdBvJ0.png)

We can get the content of the token by going on `https://jwt.io/#debugger-io`.
Here we can see the that it contains the User-ID of the account that's logged
in

![decoded_cookie](https://i.imgur.com/ozf8pYO.png)

Now, looking at the source of the challenge, we can see that the objective is
to visit the endpoint `/vault` while being "unrestricted"

```js  
app.get("/vault", (req, res) => {  
if (!res.locals.user) {  
res.status(401).send("Log in first");  
return;  
}  
const user = users.get(res.locals.user.uid);  
res.type("text/plain").send(user.restricted ? user.vault : flag);  
});  
```  
Going back on the site while being logged in, the content of `user.vault` is
none other than the content of the text area in the home:

![set_vault](https://i.imgur.com/dYtqCUn.png)

![vault](https://i.imgur.com/cDyAVna.png)

As we have seen before, after login or registration, the cookie will be set
with the UID of the user and signed with a random key.

Since we already know the format of the secret key (`0.[0-9]+`), I've started
to try to break the jwt token by using [jwtcrack](https://github.com/brendan-
rius/c-jwt-cracker), to try to forge a valid token.  
This tool uses a brute-force attack (so it tries every possible combination of
the specified charset) to retrieve the secret used to sign the token.

It can be lauched with:  
`docker run -it --rm jwtcrack [token] [charset] [maxlen] [algorithm]`

So, in our case:  
`docker run -it --rm jwtcrack
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIwLjM0Nzc5Mjc3MTY2MDg4MjIiLCJpYXQiOjE2NTE1NzcwNDh9.asR5mHIczZ-s1tZHCoCNoLKtPsPFu8S46adyRwOYa-U
0.123456789 20 sha256`

After a day without anything (and thanks to a nudge from VCT on Discord), I've
turned back to the source code.

In express, every app has an `app.use` function that's used as middleware and
is called on every request.

```js  
app.use((req, res, next) => {  
try {  
res.locals.user = jwt.verify(req.cookies.token, jwtKey, {  
algorithms: ["HS256"],  
});  
} catch (err) {  
if (req.cookies.token) {  
res.clearCookie("token");  
}  
}  
next();  
});  
```

We can notice that the only condition set the user variable, is to use a valid
jwt as a cookie.

Also, the login function (as opposed to the register function) never verifies
that the fields of the request are actually defined.  
```js  
app.post("/login", (req, res) => {  
const user = users.get(users.lookup(req.body.username));  
if (user && user.password === req.body.password) {  
res.cookie(  
"token",  
jwt.sign({ uid: user.uid }, jwtKey, { algorithm: "HS256" })  
);  
res.redirect("/");  
} else {  
res.redirect("/?e=" + encodeURIComponent("Invalid username/password"));  
}  
});  
```

We can use this to our advantage: in theory by making a login request without
having a body, it will generate a valid token

Let's try our hypothesis: we can use a tool called "Burp" to intercept our
login request and modify its content.

![alogin](https://i.imgur.com/X4GYI6x.png)

![login](https://i.imgur.com/TEiwsZO.png)

Here we can send the request to the Repeater page, where we can edit and
replay the request without problems.

![9repeater](https://i.imgur.com/tG8wElY.png)

![login_no_creds](https://i.imgur.com/Sibdgnn.png)

Now we have a valid token, but that's not related to any user!

![decoded jwt](https://i.imgur.com/PTffsP5.png)

We can see why this worked by carefully reading the functions called in the
login method:  
```js  
const user = users.get(users.lookup(req.body.username));

//~~~~

get(uid) {  
return this.users[uid] ?? {};  
}  
lookup(username) {  
return this.usernames[username];  
}  
```  
In javascript, some objects when used in a boolean statement are always
evaluated to `true` and others to `false`. These objects are called
[Truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) and
[Falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) respectively.

Here we can see two examples of these kinds of objects:

In the `lookup` function, if we pass an undefined value, it will return
`undefined`.

As for the `get(uid)`, il will try to evaluate `this.users[undefined]`, but
since `undefined` is a [Falsy](https://developer.mozilla.org/en-
US/docs/Glossary/Falsy), it will return an empty object.

So, now we have  
```js  
if (user && user.password === req.body.password)  
```  
where `user` is an empty object so a
[Truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy).

The same goes for the password check: if we don't send a password in the
request and since the empty object won't have a password attribute, both will
be evaluated to `undefined` so `undefined === undefined` which is true and
will be created a correct cookie.

Now, when accessing the `vault` endpoint, `user.restricted` will be undefined
too, and since `undefined` is a [Falsy](https://developer.mozilla.org/en-
US/docs/Glossary/Falsy), instead of the vault content, we'll get the flag!

So everything we need to do is to set the cookie with the value obtained by
making the empty `/login` request, and then we can read the flag successfully.

![set_cookie](https://i.imgur.com/iPdnqd2.png)

![flag](https://i.imgur.com/I6hfpD4.png)

\---

Now, this isn't the supposed way to solve the CTF: there's a "[Use after
free](https://cwe.mitre.org/data/definitions/416.html)" vulnerability present
(as indicated in the flag text) that enables you to use the cookie of a
deleted user

In this case, the exploit works mostly the same, it's also based on the
possibility to use a valid signed token in the request, but instead of using
an empty one, is of a deleted user.

This causes the script to retrieve an undefined `user` object and therefore
the checks will fail in a similar way

Original writeup
(https://gist.github.com/SalScotto/a4580f132148804dce058af18b6e9a2f).