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