Tags: flask session cookie 

Rating:

############################################################

If we take a look at run.py, we see that the session is managed by Flask, and it seems like a secret key is used to encode the session.

We also notice, that the session exists of some sort of state, containing the "Room" object that was generated on connection, the Caught object, which contains the pokemon you have caught, and a balls object which contans the amount of pykeballs the user has left.

The flag is most probably located within the description of the pykemon named "FLAG".

############################################################

First, we checked why it is that we cannot just click flag like any other pokemon and get a chance of "catching" it.
Turns out, the "rarity" variable for "FLAG" is 0, and the formula for catching a pokemon with rarity X is impossible to "pass" for X<10.

```
elif p.rarity > 0:
chance = (randint(1,90) + p.rarity) / 100
if chance > 0:
```

So we need to get another way to catch this flag.

############################################################

The whole "room" is stored in the session, but it looks like we need a secret key to decode this.

A quick google on "Flask session decode" gives us following video:
https://www.youtube.com/watch?v=mhcnBTDLxCI

The second example shows us how to decode a compressed session, which is identifyable by the first character being a DOT ['.'] .

```
import zlib
import crypto

print(zlib.decode(base64.urlsafe_base64decode(***INSERT_SESSION_COOKIE_HERE***)));
```

Gives us the decoded cookie in JSON format, with values encoded in base64 format.

My cookie turned out to be:
```
b'{"balls":10,"caught":{"pykemon":[]},"room":{"pykemon":[{"description":{" b":"UENURntOMHRfNF9zaDFueV9NNGcxazRycH0="},"hp":65,"name":{" b":"RkxBRw=="},"nickname":{" b":"RkxBRw=="},"pid":{" b":"RkxBRzY1"},"rarity":0,"sprite":{" b":"aW1hZ2VzL2ZsYWcucG5n"}},{"description":{" b":"UHl0d28gaXMgYSBQeWthbW9uIGNyZWF0ZWQgYnkgZ2VuZXRpYyBtYW5pcHVsYXRpb24="},"hp":19,"name":{" b":"UHl0d28="},"nickname":{" b":"UHl0d28="},"pid":{" b":"UHl0d28xOQ=="},"rarity":10,"sprite":{" b":"aW1hZ2VzL3B5dHdvLnBuZw=="}},{"description":{" b":"UHlkdWNrIGlzIGEgeWVsbG93IFB5a2Ftb24gdGhhdCByZXNlbWJsZXMgYSBkdWNrIG9yIGJpcGVkYWwgcGxhdHlwdXM="},"hp":1,"name":{" b":"UHlkdWNr"},"nickname":{" b":"UHlkdWNr"},"pid":{" b":"UHlkdWNrMQ=="},"rarity":60,"sprite":{" b":"aW1hZ2VzL3B5ZHVjay5wbmc="}},{"description":{" b":"UHl0d28gaXMgYSBQeWthbW9uIGNyZWF0ZWQgYnkgZ2VuZXRpYyBtYW5pcHVsYXRpb24="},"hp":22,"name":{" b":"UHl0d28="},"nickname":{" b":"UHl0d28="},"pid":{" b":"UHl0d28yMg=="},"rarity":10,"sprite":{" b":"aW1hZ2VzL3B5dHdvLnBuZw=="}},{"description":{" b":"V2hlbiB0aGlzIFB5a2Ftb24gc2luZ3MsIGl0IG5ldmVyIHBhdXNlcyB0byBicmVhdGhlLg=="},"hp":98,"name":{" b":"UHlnZ2xpcHVmZg=="},"nickname":{" b":"UHlnZ2xpcHVmZg=="},"pid":{" b":"UHlnZ2xpcHVmZjk4"},"rarity":50,"sprite":{" b":"aW1hZ2VzL3B5Z2dsaXB1ZmYucG5n"}},{"description":{" b":"UHlyb2RhY3R5bCBpcyBhIFB5a2Ftb24gZnJvbSB0aGUgYWdlIG9mIGRpbm9zYXVycw=="},"hp":24,"name":{" b":"UHlyb2RhY3R5bA=="},"nickname":{" b":"UHlyb2RhY3R5bA=="},"pid":{" b":"UHlyb2RhY3R5bDI0"},"rarity":20,"sprite":{" b":"aW1hZ2VzL3B5cm9kYWN0eWwucG5n"}},{"description":{" b":"VGhpcyBQeWthbW9uIGhhcyBlbGVjdHJpY2l0eS1zdG9yaW5nIHBvdWNoZXMgb24gaXRzIGNoZWVrcy4gVGhlc2UgYXBwZWFyIHRvIGJlY29tZSBlbGVjdHJpY2FsbHkgY2hhcmdlZCBkdXJpbmcgdGhlIG5pZ2h0IHdoaWxlIFB5a2FjaHUgc2xlZXBzLg=="},"hp":64,"name":{" b":"UHlrYWNodQ=="},"nickname":{" b":"UHlrYWNodQ=="},"pid":{" b":"UHlrYWNodTY0"},"rarity":40,"sprite":{" b":"aW1hZ2VzL3B5a2FjaHUucG5n"}}],"pykemon_count":7,"rid":0}}'
```

To make things easier, i encoded "FLAG" to base64, which turned out to be RkxBRw==.
We can clearly see now, the description field of the object with nickname "RkxBRw==" is UENURntOMHRfNF9zaDFueV9NNGcxazRycH0=,
Which turns out to be Base64 for PCTF{N0t_4_sh1ny_M4g1k4rp}