Hack.lu 2020 CTF write up: Confessions
Tags: security, ctf
Published: 2020-11-10 15:02:35

# Description

Someone confessed their dirtiest secret on this new website: https://confessions.flu.xxx
Can you find out what it is?

# Write up

This challenge was quite fun and let me thinking for a while.

We are presented with a website that encode confessions. The title is in clear but the message is encoded with SHA256.

![Webpage of the challenge](https://dev-to-uploads.s3.amazonaws.com/i/3xz9gqbrt1eh13utahow.png)

First thing first: we need to inspect the website. We have a `confessions.js` and it seems that the website works with a backend API in GraphQL which is great because we know a little of that!

![Same webpage with the developer tools console open](https://dev-to-uploads.s3.amazonaws.com/i/bk0s1abov76ue78k30ek.png)

Then taking a look at the `confessions.js` file, we can get a glimpse of how the website works and what kind of queries and mutations can be ran:

![Javascript code](https://dev-to-uploads.s3.amazonaws.com/i/x3kdytmze8sft5lqfx9e.png)

Thanks to that, we are going to reverse step by step this API and see if the developers haven't forgotten some fields and resolvers there.

So let's boot up Burp and try to gather information for this API. Sometimes, if the API is meant to be used by client, the developers can allow a graphic interface: /graphiql. But this is not the case here.

When running a normal query we can compare what is shown on the website and through the API, it's the same:

![Query through the API](https://dev-to-uploads.s3.amazonaws.com/i/43sn5f5o09a7g33tzlk3.png)

So now, we need to know more about the API. Graphql APIs are defined by a schema that we can gather with this query:

```graphql
"query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } }"
```

This will enumerate all the fields, objects and queries of the API. Through that we will try to understand if we can get some more information than displayed on the website.

We can see a new object: Access that can I queried with the function accessLog.

![AccessLog 1](https://dev-to-uploads.s3.amazonaws.com/i/zpinc60za6u3mr0gusmo.png)
![Close up on this interesting query that seems only available through the API](https://dev-to-uploads.s3.amazonaws.com/i/qscdsbq4v1s65paaygra.png)
![The accessLog query returns a Access object with those fields](https://dev-to-uploads.s3.amazonaws.com/i/zjvv5ocosa7migrlqioi.png)

After querying accessLog:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/422uhm8q3wof09jayyvi.png)

Those logs are interesting, it is possible that we have the flag right there. As the website shows you the hash of your "confession", maybe by querying the hash we can retrieve the confession. This didn't work from the website because the ID to query the "confession" was a UUID and not the hash itself. Nonetheless, a query through the API resulted with that:

![](https://dev-to-uploads.s3.amazonaws.com/i/3z2ngcorow9s8xeiqnjj.png)


At this point it was already a couple hours in the challenge and we didn't realised that we were literally looking at the flag. We mistaken the title "Flag" with our own queries.

But not, it was the actual flag!

In fact, when using the website, the queries are launched at every key strokes, this mean that the accesslog is actually the flag being typed. So if we manage to reverse the hash or compare it somehow, we can retrieve the flag.

Getting the first letters online was quite simple, but we were stuck with: `flag`.

So knowing that the hashes are incremental, we can built up a mask attack on the supposed length of the flag as at the end we see 4 times empty SHA256 hashes from the log file and then queries that matched out timestamps of API use. Clearly a strong indicator of insecure data access.

We launched a massive mask attack and went to sleep but in the morning, we got nothing ; the computer was still running.

So we decided to try manually to gather the letters one by one as they are constructed. And tada, we finally got the flag!

![Final crack of the flag](https://dev-to-uploads.s3.amazonaws.com/i/7ghnak4e361g3thpw1tu.png)

Conclusions:

* reverse a GraphQL API is quite easy!
* masks attacks are costly but possible