FooBar CTF 2021: Wargames

Wargames

Category: reverse

262 points

My friend found a game server but it seems to be very old. The game seems to be interesting but it requires a launch code.

nc chall.nitdgplug.org 30419

File: wargames.zip

Solution

Challenge consists of text application launched by organizers and zip archive shared to participants. In archive there’s a __pycache__ directory with python compiled wargames.cpython-38.pyc. I must admin, I love that type of challs :-)

There’s not much in the application. We are able to start GLOBAL THERMONUCLEAR WAR game, for which example game I’m pasting below.

┌───────────────────────────────┐

│  GREETINGS, PROFESSOR FALKEN  │

└───────────────────────────────┘

HOPE YOU ARE FINE !


AVAILABLE COMMANDS:


HELP GAMES
LIST GAMES
PLAY <game>
EXIT
>PLAY GLOBAL THERMONUCLEAR WAR


AWAITING FIRST STRIKE COMMAND
------------------------------


PLEASE SPECIFY PRIMARY TARGET
BY CITY AND/OR COUNTRY NAME
>BERLIN


PREPARING NUCLEAR STRIKE FOR BERLIN
ENTER LAUNCH CODE:KABOOM
ACCESS DENIED

At this moment I knew that the challenge is about to retrieve the LAUNCH CODE.

I’ve used uncompyle6 to attempt decompilation. With some errors, but enough code has been recovered to start working on challenge.

uncompyle6 wargames.cpython-38.pyc 
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.9.1+ (default, Feb  5 2021, 13:46:56) 
# [GCC 10.2.1 20210110]
# Embedded file name: /home/d4nav/Downloads/wargames.py
# Compiled at: 2021-03-26 15:24:15
# Size of source mod 2**32: 7444 bytes
Instruction context:
-> 
 L. 190       268  JUMP_BACK           248  'to 248'
denied = '\n\n\n              ______               K         \n           ,\'"       "-._         C         \n         ,\'              "-._ _  A          \n         ;              __,-\'/  H           \n        ;|           ,-\' _,\'"\'._,\'          \n        |:            _,\'      |\\ \'.        \n        : \\       _,-\'         | \\  \'.      \n         \\ \\   ,-\'             |  \\   \\     \n          \\ \'.         .-.     |                  \\  \\         "      |        :             \n            \'. \'.              |        |              \n              \'. "-._          |        ;             \n              / |\'._ \'-._      /       /              \n             /  | \\ \'._   "-._____ _,\'                \n            /   |  \\_.-"-.___                      \n            \\   :            /                \n             \'._\\_       __.\'_                         \n        __,--\'\'_ \' "--\'\'\'\' \\_  \'-._\n  __,--\'     .\' /_  |   __. \'-.    \'-._                          \n /            \'.  \'-.-\'\'  __,-\'     _,-\'                         \n  \'.            \'.   _,-\'"      _,-\'                           \n    \'.            \'\'"       _,-\'                                \n      \'.                _,-\'                                \n        \'.          _,-\'                                        \n          \'.   __,\'"                                            \n            \'\'"             \n\n\n\n▄▀█\u2003█▀▀\u2003█▀▀\u2003█▀▀\u2003█▀\u2003█▀\u2003 \u2003█▀▄\u2003█▀▀\u2003█▄░█\u2003█\u2003█▀▀\u2003█▀▄\n█▀█\u2003█▄▄\u2003█▄▄\u2003██▄\u2003▄█\u2003▄█\u2003 \u2003█▄▀\u2003██▄\u2003█░▀█\u2003█\u2003██▄\u2003█▄▀\n\n'
okk = "\n\n  .\n           .d$ .\n           d$$  :\n          .$$$\n          :$$$   :\n          $$$$   :\n          $$$$   :\n         .$$$$\n         :$$$$    :\n         :$$$$    :\n         $$$$$    :\n         $$$$$    :\n         :    $$$$$\n         :    $$$$$\n         :    $$$$$\n        .:    $$$$$.\n       / :    $$$$:       /  :    $$$$:       '        $$$$`   '\n     |    :   $$$$    |\n     |   /:   $$$$\\   |\n     |  /     $$$` \\  |\n     |_/   :__$$P   \\_|\n\n▒█▀▀█ ▒█▀▀▀ ░█▀▀█ ▒█▀▀█ ▒█▀▀▀ \u3000 ▒█▀▀▀█ ▒█░▒█ ▀▀█▀▀ \n▒█▄▄█ ▒█▀▀▀ ▒█▄▄█ ▒█░░░ ▒█▀▀▀ \u3000 ▒█░░▒█ ▒█░▒█ ░▒█░░ \n▒█░░░ ▒█▄▄▄ ▒█░▒█ ▒█▄▄█ ▒█▄▄▄ \u3000 ▒█▄▄▄█ ░▀▄▄▀ ░▒█░░\n"
greeting = ' \n\n░██╗░░░░░░░██╗░█████╗░██████╗░░██████╗░░█████╗░███╗░░░███╗███████╗░██████╗\n░██║░░██╗░░██║██╔══██╗██╔══██╗██╔════╝░██╔══██╗████╗░████║██╔════╝██╔════╝\n░╚██╗████╗██╔╝███████║██████╔╝██║░░██╗░███████║██╔████╔██║█████╗░░╚█████╗░\n░░████╔═████║░██╔══██║██╔══██╗██║░░╚██╗██╔══██║██║╚██╔╝██║██╔══╝░░░╚═══██╗\n░░╚██╔╝░╚██╔╝░██║░░██║██║░░██║╚██████╔╝██║░░██║██║░╚═╝░██║███████╗██████╔╝\n░░░╚═╝░░░╚═╝░░╚═╝░░╚═╝╚═╝░░╚═╝░╚═════╝░╚═╝░░╚═╝╚═╝░░░░░╚═╝╚══════╝╚═════╝░     \n\n\n'
under = '\n\n▒█░▒█ ▒█▄░▒█ ▒█▀▀▄ ▒█▀▀▀ ▒█▀▀█ \u3000 ▒█▀▀▄ ▒█▀▀▀ ▒█░░▒█ ▒█▀▀▀ ▒█░░░ ▒█▀▀▀█ ▒█▀▀█ ▒█▀▄▀█ ▒█▀▀▀ ▒█▄░▒█ ▀▀█▀▀ \n▒█░▒█ ▒█▒█▒█ ▒█░▒█ ▒█▀▀▀ ▒█▄▄▀ \u3000 ▒█░▒█ ▒█▀▀▀ ░▒█▒█░ ▒█▀▀▀ ▒█░░░ ▒█░░▒█ ▒█▄▄█ ▒█▒█▒█ ▒█▀▀▀ ▒█▒█▒█ ░▒█░░ \n░▀▄▄▀ ▒█░░▀█ ▒█▄▄▀ ▒█▄▄▄ ▒█░▒█ \u3000 ▒█▄▄▀ ▒█▄▄▄ ░░▀▄▀░ ▒█▄▄▄ ▒█▄▄█ ▒█▄▄▄█ ▒█░░░ ▒█░░▒█ ▒█▄▄▄ ▒█░░▀█ ░▒█░░\n'
maintain = '\n𝔾𝔸𝕄𝔼 𝕀𝕊 𝕋𝔼𝕄ℙ𝕆ℝ𝔸ℝ𝕀𝕃𝕐 𝕌ℕ𝔸𝕍𝔸𝕀𝕃𝔸𝔹𝕃𝔼 𝔻𝕌𝔼 𝕋𝕆 𝕄𝔸𝕀ℕ𝕋𝔼ℕ𝔸ℕℂ𝔼\n'
menu = [
 'HELP GAMES', 'LIST GAMES', 'PLAY <game>']
game = ["FALKEN'S MAZE", 'TIC TAC TOE ', 'GLOBAL THERMONUCLEAR WAR']

def validateLaunchcode(launchcode):
    if len(launchcode[::-2]) != 12 or len(launchcode[15:]) != 9:
        print(denied)
        return False
    clen = len(launchcode)
    l1 = launchcode[:8]
    cc = []
    for i in range(0, len(l1), 2):
        q = []
        q.append(ord(l1[i]))
        q.append(ord(l1[(i + 1)]))
        cc.append(q)
    else:
        enc = []
        for i in range(len(cc)):
            val1 = cc[i][0] << 1
            val1 ^= 69
            val2 = cc[i][1] << 2
            val2 ^= 10
            enc.append(val1)
            enc.append(val2)
        else:
            correct = [
             159, 218, 153, 214, 45, 206, 153, 374]
            if enc != correct:
                print('ACCESS DENIED ok')
                return False
            l2 = launchcode[8:16]
            key = 'PEACEOUT'
            res = []
            [res.append(ord(key[i]) - ord(l2[i])) if i & 1 == 1 else res.append(ord(key[i]) + ord(l2[i])) for i in range(len(l2))]
            ok = [
             192, 18, 117, -32, 120, -16, 173, -2]
            if ok != res:
                print('ACCESS DENIED')
                return False
            l3 = launchcode[int(2 * clen / 3):]
            KEY = "There's no way to win"
            I = 7
            KARMA = [
             123, 47, 86, 28, 74, 50, 32, 114]
            MISSILE = []
            for x in l3:
                MISSILE.append((ord(x) + I ^ ord(KEY[I])) % 255)
                I = (I + 1) % len(KEY)
            else:
                if KARMA == MISSILE:
                    print(okk)
                    exit()


def start_challenge--- This code section failed: ---

 L. 161         0  LOAD_GLOBAL              print
                2  LOAD_STR                 'HELLO PROFESSOR FALKEN\n'
                4  CALL_FUNCTION_1       1  ''
                6  POP_TOP          
              8_0  COME_FROM           266  '266'
              8_1  COME_FROM           162  '162'

 L. 163         8  LOAD_GLOBAL              print
               10  LOAD_STR                 'AVAILABLE COMMANDS:'
               12  CALL_FUNCTION_1       1  ''
               14  POP_TOP          

 L. 164        16  LOAD_GLOBAL              print
               18  LOAD_GLOBAL              menu
               20  LOAD_STR                 'sep'
               22  LOAD_STR                 '\n'
               24  BUILD_MAP_1           1 
               26  CALL_FUNCTION_EX_KW     1  'keyword and positional arguments'
               28  POP_TOP          

 L. 165        30  LOAD_GLOBAL              input
               32  LOAD_STR                 '>'
               34  CALL_FUNCTION_1       1  ''
               36  LOAD_METHOD              rstrip
               38  CALL_METHOD_0         0  ''
               40  LOAD_METHOD              lstrip
               42  CALL_METHOD_0         0  ''
               44  STORE_FAST               'choice'

 L. 166        46  LOAD_FAST                'choice'
               48  LOAD_GLOBAL              menu
               50  LOAD_CONST               0
               52  BINARY_SUBSCR    
               54  COMPARE_OP               ==
               56  POP_JUMP_IF_FALSE    66  'to 66'

 L. 167        58  LOAD_GLOBAL              print
               60  LOAD_STR                 "'GAMES' REFER TO MODELS , SIMULATIONS AND GAMES \n WHICH HAVE TACTICAL AND STRATEGIC APPLICATION\n\n"
               62  CALL_FUNCTION_1       1  ''
               64  POP_TOP          
             66_0  COME_FROM            56  '56'

 L. 168        66  LOAD_FAST                'choice'
               68  LOAD_GLOBAL              menu
               70  LOAD_CONST               1
               72  BINARY_SUBSCR    
               74  COMPARE_OP               ==
               76  POP_JUMP_IF_FALSE   108  'to 108'

 L. 169        78  LOAD_GLOBAL              print
               80  LOAD_STR                 '\n'
               82  CALL_FUNCTION_1       1  ''
               84  POP_TOP          

 L. 170        86  LOAD_GLOBAL              print
               88  LOAD_GLOBAL              game
               90  LOAD_STR                 'sep'
               92  LOAD_STR                 '\n'
               94  BUILD_MAP_1           1 
               96  CALL_FUNCTION_EX_KW     1  'keyword and positional arguments'
               98  POP_TOP          

 L. 171       100  LOAD_GLOBAL              print
              102  LOAD_STR                 '\n'
              104  CALL_FUNCTION_1       1  ''
              106  POP_TOP          
            108_0  COME_FROM            76  '76'

 L. 172       108  LOAD_FAST                'choice'
              110  LOAD_STR                 'PLAY'
              112  COMPARE_OP               ==
              114  POP_JUMP_IF_FALSE   124  'to 124'

 L. 173       116  LOAD_GLOBAL              print
              118  LOAD_STR                 'WHICH GAME ?? CHECK THE LIST\n\n'
              120  CALL_FUNCTION_1       1  ''
              122  POP_TOP          
            124_0  COME_FROM           114  '114'

 L. 174       124  LOAD_FAST                'choice'
              126  LOAD_STR                 "PLAY FALKEN'S MAZE"
              128  COMPARE_OP               ==
              130  POP_JUMP_IF_FALSE   140  'to 140'

 L. 175       132  LOAD_GLOBAL              print
              134  LOAD_GLOBAL              under
              136  CALL_FUNCTION_1       1  ''
              138  POP_TOP          
            140_0  COME_FROM           130  '130'

 L. 176       140  LOAD_FAST                'choice'
              142  LOAD_STR                 'PLAY TIC TAC TOE'
              144  COMPARE_OP               ==
              146  POP_JUMP_IF_FALSE   156  'to 156'

 L. 177       148  LOAD_GLOBAL              print
              150  LOAD_GLOBAL              maintain
              152  CALL_FUNCTION_1       1  ''
              154  POP_TOP          
            156_0  COME_FROM           146  '146'

 L. 178       156  LOAD_FAST                'choice'
              158  LOAD_STR                 'PLAY GLOBAL THERMONUCLEAR WAR'
              160  COMPARE_OP               ==
              162  POP_JUMP_IF_FALSE     8  'to 8'

 L. 179       164  LOAD_GLOBAL              print
              166  LOAD_STR                 '\n'
              168  CALL_FUNCTION_1       1  ''
              170  POP_TOP          

 L. 180       172  LOAD_GLOBAL              print
              174  LOAD_STR                 'AWAITING FIRST STRIKE COMMAND'
              176  CALL_FUNCTION_1       1  ''
              178  POP_TOP          

 L. 181       180  LOAD_GLOBAL              print
              182  LOAD_STR                 '------------------------------'
              184  CALL_FUNCTION_1       1  ''
              186  POP_TOP          

 L. 182       188  LOAD_GLOBAL              print
              190  LOAD_STR                 '\n'
              192  CALL_FUNCTION_1       1  ''
              194  POP_TOP          

 L. 184       196  LOAD_GLOBAL              print
              198  LOAD_STR                 'PLEASE SPECIFY PRIMARY TARGET'
              200  CALL_FUNCTION_1       1  ''
              202  POP_TOP          

 L. 185       204  LOAD_GLOBAL              print
              206  LOAD_STR                 'BY CITY AND/OR COUNTRY NAME'
              208  CALL_FUNCTION_1       1  ''
              210  POP_TOP          

 L. 186       212  LOAD_GLOBAL              input
              214  LOAD_STR                 '>'
              216  CALL_FUNCTION_1       1  ''
              218  LOAD_METHOD              rstrip
              220  CALL_METHOD_0         0  ''
              222  LOAD_METHOD              lstrip
              224  CALL_METHOD_0         0  ''
              226  STORE_FAST               'target'

 L. 187       228  LOAD_GLOBAL              print
              230  LOAD_STR                 '\n'
              232  CALL_FUNCTION_1       1  ''
              234  POP_TOP          

 L. 188       236  LOAD_GLOBAL              print
              238  LOAD_STR                 'PREPARING NUCLEAR STRIKE FOR '
              240  LOAD_FAST                'target'
              242  BINARY_ADD       
              244  CALL_FUNCTION_1       1  ''
              246  POP_TOP          

 L. 189       248  LOAD_GLOBAL              validateLaunchcode
              250  LOAD_GLOBAL              input
              252  LOAD_STR                 'ENTER LAUNCH CODE:'
              254  CALL_FUNCTION_1       1  ''
              256  LOAD_METHOD              rstrip
              258  CALL_METHOD_0         0  ''
              260  LOAD_METHOD              lstrip
              262  CALL_METHOD_0         0  ''
              264  CALL_FUNCTION_1       1  ''
              266  POP_JUMP_IF_TRUE      8  'to 8'

 L. 190       268  JUMP_BACK           248  'to 248'
              270  JUMP_BACK             8  'to 8'

Parse error at or near `JUMP_BACK' instruction at offset 268


print(greeting)
start_challenge()

# file wargames.cpython-38.pyc
# Deparsing stopped due to parse error

Let’s analysis start from the line with if len(launchcode[::-2]) != 12 or len(launchcode[15:]) != 9:, which means that the only possible length of LAUNCH CODE is 24 characters. The LAUNCH CODE seems also be divided into 3 parts (l1, l2, l3), each with different logic for checking.

I wrote 3 ugly scripts (I’m not proud of them) to retrieve each part of the CODE.

correct = [159, 218, 153, 214, 45, 206, 153, 374]
guess = [0 for i in range(8)]
is_correct = [False for i in range(8)]
enc = []

while correct != enc:
    guess = [guess[i] + 1 if is_correct[i] is not True else guess[i] for i in range(8)]
    l1 = ''.join([chr(i) for i in guess])
    cc = []
    for i in range(0, len(l1), 2):
        q = []
        q.append(ord(l1[i]))
        q.append(ord(l1[(i + 1)]))
        cc.append(q)
    else:
        enc = []
        for i in range(len(cc)):
            val1 = cc[i][0] << 1
            val1 ^= 69
            val2 = cc[i][1] << 2
            val2 ^= 10
            enc.append(val1)
            enc.append(val2)
        is_correct = [True if enc[i] == correct[i] else False for i in range(8)]

print(l1)

Above gave me m4n741n_.

correct = [192, 18, 117, -32, 120, -16, 173, -2]
key = 'PEACEOUT'
guess = [ord(key[i]) - correct[i] if i & 1 == 1 else correct[i] - ord(key[i]) for i in range(len(correct))]
print(''.join([chr(i) for i in guess]))

Second part of code is p34c3_XV.

correct = [123, 47, 86, 28, 74, 50, 32, 114]
guess = [0 for i in range(8)]
is_correct = [False for i in range(8)]
MISSILE= []

while correct != MISSILE:
    guess = [guess[i] + 1 if is_correct[i] is not True else guess[i] for i in range(8)]
    l3 = ''.join([chr(i) for i in guess])
    KEY = "There's no way to win"
    I = 7
    MISSILE = []
    for x in l3:
        MISSILE.append((ord(x) + I ^ ord(KEY[I])) % 255)
        I = (I + 1) % len(KEY)
    is_correct = [True if MISSILE[i] == correct[i] else False for i in range(8)]

print(l3)

This gave me T9022GLD.

Let’s try the solution…

┌───────────────────────────────┐

│  GREETINGS, PROFESSOR FALKEN  │

└───────────────────────────────┘

HOPE YOU ARE FINE !


AVAILABLE COMMANDS:


HELP GAMES
LIST GAMES
PLAY <game>
EXIT
>PLAY GLOBAL THERMONUCLEAR WAR


AWAITING FIRST STRIKE COMMAND
------------------------------


PLEASE SPECIFY PRIMARY TARGET
BY CITY AND/OR COUNTRY NAME
>BERLIN


PREPARING NUCLEAR STRIKE FOR BERLIN
ENTER LAUNCH CODE:m4n741n_p34c3_XVT9022GLD
GLUG{15_7h15_r34l_0r_15_17_g4m3??}

Flag

GLUG{15_7h15_r34l_0r_15_17_g4m3??}

Privacy Policy
luc © 2021