Rating:

![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/)).

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

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) {
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 {
algorithms: ["HS256"],
});
} catch (err) {
}
}
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
"token",
jwt.sign({ uid: user.uid }, jwtKey, { algorithm: "HS256" })
);
res.redirect("/");
} else {
}
});


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.

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)

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

//~~~~

get(uid) {
return this.users[uid] ?? {};
}
}

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

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.

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