Challenge
Category: OSINT & REV
An elf were found to be doing some shady things behind Santa and has since gone rogue and missing. Hopefully the elf is not doing something harmful to the upcoming christmas eve. I heard that he went by the name the-polar-develf1337 on the interwebs.
Solution
I used sherlock to find the username on github.
sherlock the-polar-develf1337
He had a github repo with code to a discord bot. I cloned the repo to to find out how it worked.
There was no flags, but the git commit history showed that there was a flag.cpython-310.pyc
file that was removed in the latest commit.
This is a python compiled file, so i used a tool to decompyle the python bytecode to python code, but since it was python 3.10.x i had a bad time finding a good tool for the job so the result was kind of messed up, but still readable…
# Source Generated with Decompyle++
# File: flag.cpython-310.pyc (Python 3.10)
import os
import discord
import hashlib
from discord.ext import commands
def xor(s1, s2):
return ''.join((lambda .0: for a, b in .0:
chr(a ^ b))(zip(s1, s2)))
Unsupported Node type: 27
Flag = <NODE:27>((lambda :
def __init__(self, bot):
self.bot = botself._secret = bytes.fromhex('1b252067113f23283c743e0c18413a1943551b1d13113a06550b1c0d130f01031306481a08401e560e')self._last_message = os.urandom(25)
async def flag(self = None, ctx = None, arg = None, *, member):
if not member:
passmember = ctx.authorif member.display_name == 'The Polar Express':
key = xor(arg.encode(), self._last_message).encode()if hashlib.md5(key).hexdigest() == '4b462bce910ff869f9a8ec2604fc9707':
Unsupported Node type: 28
<NODE:28>else:
Unsupported Node type: 28
<NODE:28>else:
Unsupported Node type: 28
<NODE:28>self._last_message = arg.encode()flag = None(flag)), 'Flag', commands.Cog, 'flag', **('name',))
async def setup(bot):
await bot.add_cog(Flag(bot))
Looks like there was some xor magic going on so I naivly tried to xor some random stuff together, but that did not work..
I wanted to look at what the flag
method did, but it was to unreadable in the decompiled code, so i used the dis
module to disassemble the bytecode.
import dis
import flag
flag = flag.Flag("bot")
dis.dis(flag.__cog_commands__[0].callback.__code__)
The most interesting code can be found 60-94, since we can clearly see what is happening.
flag function
0 GEN_START 1
20 2 LOAD_FAST 3 (member)
4 JUMP_IF_TRUE_OR_POP 5 (to 10)
6 LOAD_FAST 1 (ctx)
8 LOAD_ATTR 0 (author)
>> 10 STORE_FAST 3 (member)
21 12 LOAD_FAST 3 (member)
14 LOAD_ATTR 1 (display_name)
16 LOAD_CONST 1 ('The Polar Express')
18 COMPARE_OP 2 (==)
20 POP_JUMP_IF_FALSE 57 (to 114)
22 22 LOAD_GLOBAL 2 (xor)
24 LOAD_FAST 2 (arg)
26 LOAD_METHOD 3 (encode)
28 CALL_METHOD 0
30 LOAD_FAST 0 (self)
32 LOAD_ATTR 4 (_last_message)
34 CALL_FUNCTION 2
36 LOAD_METHOD 3 (encode)
38 CALL_METHOD 0
40 STORE_FAST 4 (key)
23 42 LOAD_GLOBAL 5 (hashlib)
44 LOAD_METHOD 6 (md5)
46 LOAD_FAST 4 (key)
48 CALL_METHOD 1
50 LOAD_METHOD 7 (hexdigest)
52 CALL_METHOD 0
54 LOAD_CONST 2 ('4b462bce910ff869f9a8ec2604fc9707')
56 COMPARE_OP 2 (==)
58 POP_JUMP_IF_FALSE 48 (to 96)
24 60 LOAD_FAST 1 (ctx)
62 LOAD_METHOD 8 (send)
64 LOAD_CONST 3 ('Commencing express delivery! The flag is: ')
66 LOAD_GLOBAL 2 (xor)
68 LOAD_FAST 4 (key)
70 LOAD_CONST 4 (2)
72 BINARY_MULTIPLY
74 LOAD_FAST 0 (self)
76 LOAD_ATTR 9 (_secret)
78 CALL_FUNCTION 2
80 FORMAT_VALUE 0
82 BUILD_STRING 2
84 CALL_METHOD 1
86 GET_AWAITABLE
88 LOAD_CONST 0 (None)
90 YIELD_FROM
92 POP_TOP
94 JUMP_FORWARD 17 (to 130)
26 >> 96 LOAD_FAST 1 (ctx)
98 LOAD_METHOD 8 (send)
100 LOAD_CONST 5 ('Incorrect key. The Polar Express is not pleased.')
102 CALL_METHOD 1
104 GET_AWAITABLE
106 LOAD_CONST 0 (None)
108 YIELD_FROM
110 POP_TOP
112 JUMP_FORWARD 8 (to 130)
28 >> 114 LOAD_FAST 1 (ctx)
116 LOAD_METHOD 8 (send)
118 LOAD_CONST 6 ('The Polar Express has not arrived yet.')
120 CALL_METHOD 1
122 GET_AWAITABLE
124 LOAD_CONST 0 (None)
126 YIELD_FROM
128 POP_TOP
29 >> 130 LOAD_FAST 2 (arg)
132 LOAD_METHOD 3 (encode)
134 CALL_METHOD 0
136 LOAD_FAST 0 (self)
138 STORE_ATTR 4 (_last_message)
140 LOAD_CONST 0 (None)
142 RETURN_VALUE
Since we know that the flag starts with OMEGAPOINT{
we can xor that with the secret
to get the first charcahters of the key
def xor(s1, s2):
return ''.join(chr(a ^ b) for a, b in zip(s1, s2))
flag = b"OMEGAPOINT{"
secret = bytes.fromhex("1b252067113f23283c743e0c18413a1943551b1d13113a06550b1c0d130f01031306481a08401e560e")
xor_key = xor(flag, secret)
print(xor_key)
This outputs: The Polar Exp
Nice!
Using this we can programaticly add more characters to the key, and then the flag to get a readable flag in the end.
def xor(s1, s2):
return ''.join(chr(a ^ b) for a, b in zip(s1, s2))
flag = b"OMEGAPOINT{"
secret = bytes.fromhex("1b252067113f23283c743e0c18413a1943551b1d13113a06550b1c0d130f01031306481a08401e560e")
xor_key = xor(flag, secret)
print(xor_key)
xor_key = b"The Polar Exp"
flag = xor(xor_key, secret)
print(flag)
xor_key = xor(flag.encode(), secret)
print(xor_key)
xor_key = b"The Polar Express is "
flag = xor(xor_key, secret)
print(flag)
xor_key = xor(flag.encode(), secret)
print(xor_key)
flag = b"OMEGAPOINT{th3_j0urn3y"
xor_key = xor(flag, secret)
print(xor_key)
flag = b"OMEGAPOINT{th3_j0urn3y_"
xor_key = xor(flag, secret)
print(xor_key)
xor_key = b"The Polar Express is "
flag = xor(xor_key, secret)
flag = b"OMEGAPOINT{th3_j0urn3y_"
xor_key = xor(flag, secret)
print("HERE",xor_key)
xor_key = b"The Polar Express is here"
flag = xor(xor_key, secret)
print(flag)
flag = "OMEGAPOINT{th3_j0urn3y_t0"
xor_key = xor(flag.encode(), secret)
print(xor_key)
xor_key = b"The Polar Express is hereThe Polar Express is here"
flag = xor(xor_key, secret)
print(flag)
flag: OMEGAPOINT{th3_j0urn3y_t0_th3_north_p0l3}