Py[tc]on
Pour ce challenge, on nous donne un fichier challenge :
└─[$] <> file challenge
challenge: Byte-compiled Python module for CPython 3.11, timestamp-based, .py timestamp: Thu Nov 28 19:48:37 2024 UTC, .py size: 1978 bytes
Grâce à ça, on peut déterminer le fait qu’on soit sur des instructions Python compilées.
Après une petite recherche sur internet, j’ai pu trouver l’outil :
pycdc qui permet de décompiler les fichiers .pyc en .py.
On obtient donc :
└─[$] <> ./pycdc.x86_64 ./challenge
# Source Generated with Decompyle++
# File: challenge (Python 3.11)
import base64
import zlib
import marshal
import random
upup = None
def compute_rand(string):
global upup
(a, b, c) = (42, 3.14, -7)
result = (a ** 2 + b ** 0.5) * c % 13 + (a & b.__trunc__())
result = ((b / 3.14) ** (c + 10)).__round__(5)
Unsupported opcode: RETURN_GENERATOR (109)
final = (lambda .0: pass# WARNING: Decompyle incomplete
)(range(1, 10)()) + result
upup = zlib.decompress(base64.b64decode(string))
return random.randint(1, 100)
def get_flag(upup):
data = 'AMSI{aGFoYWhhX25vdF9kb25lX3lldF9tYW4=}'.encode('utf-8')
Unsupported opcode: RETURN_GENERATOR (109)
encoded = (lambda .0: pass# WARNING: Decompyle incomplete
)(enumerate(data)())
Unsupported opcode: RETURN_GENERATOR (109)
decoded = (lambda .0: pass# WARNING: Decompyle incomplete
)(enumerate(encoded)())
upup = 7
return base64.b64decode(data.strip(b'AMSI).strip(b'))
def loader():
global upup
print('Starting the challenge...')
compute_rand('eJxtkc9LG0EUx2cTI9hdUvx56EFGb5GSFVEwKgVpC3qwggl4XMbZZ7JkM7PMvD2sGujRY28eSq8i9J/ZlBxkwZP0L/C2J2f9rTi892Xee583M4+5Ji/WiPFJ4zdHRs6Ib/UJWs9lv3Ruor9Pmb51YvnlbqnYqyksvyBHTshv8ucNbxGfNMkrrvIeURv9kX/a3GluH0cJ9wLtiUX0sLPc8raWld/P578LBEWxA7QLCUVJYxFK3r3LHIasvUazD7sRiCZo1oN85qtUCjjO0dYDQAO9RvPpfSVFuzijTlsqoazNAjFXK2WVQEQxZpVIBQJNbGvgCtArWrOy4XUxM6X5ituRPXC7LAzcb5LHPRCoXfNqKVwEjW4bBCiG4B0kRrj0oR4l2UfGMWahdxgLjoEU+eITB4KrJELw7+ENMxcL9Zf6m44xc7/eM/KTXM4sndpX1cl0anVQbQyrjdPypV399fmfPZvas1fOeDqxerFuxNjAaQydRvpo/4tibeAsDJ2F9NFuio+4BVBtm2s=')
jk = upup
exec(marshal.loads(jk))
upup = None
def confuse_decompiler():
Unsupported opcode: JUMP_BACKWARD (226)
junk_code = [
(lambda : print("Analyzing this won't help.")),
(lambda : random.choice([
'Flag not here!',
'Still nothing here...'])),
(lambda : get_flag(upup))]
# WARNING: Decompyle incomplete
def main():
print('Welcome to the obfuscated challenge!')
confuse_decompiler()
loader()
if __name__ == '__main__':
main()
return None
On peut voir deux fonctions intéressantes :
loaderqui semble faire quelque chose avec une longue stringcompute_randqui est requise parloader.
Il ne reste plus qu’à refactorer le code :
import base64
import zlib
import marshal
import random
upup = None
def compute_rand(string):
global upup
(a, b, c) = (42, 3.14, -7)
result = (a ** 2 + b ** 0.5) * c % 13 + (a & b.__trunc__())
result = ((b / 3.14) ** (c + 10)).__round__(5)
upup = zlib.decompress(base64.b64decode(string))
return random.randint(1, 100)
def loader():
global upup
print('Starting the challenge...')
compute_rand('eJxtkc9LG0EUx2cTI9hdUvx56EFGb5GSFVEwKgVpC3qwggl4XMbZZ7JkM7PMvD2sGujRY28eSq8i9J/ZlBxkwZP0L/C2J2f9rTi892Xee583M4+5Ji/WiPFJ4zdHRs6Ib/UJWs9lv3Ruor9Pmb51YvnlbqnYqyksvyBHTshv8ucNbxGfNMkrrvIeURv9kX/a3GluH0cJ9wLtiUX0sLPc8raWld/P578LBEWxA7QLCUVJYxFK3r3LHIasvUazD7sRiCZo1oN85qtUCjjO0dYDQAO9RvPpfSVFuzijTlsqoazNAjFXK2WVQEQxZpVIBQJNbGvgCtArWrOy4XUxM6X5ituRPXC7LAzcb5LHPRCoXfNqKVwEjW4bBCiG4B0kRrj0oR4l2UfGMWahdxgLjoEU+eITB4KrJELw7+ENMxcL9Zf6m44xc7/eM/KTXM4sndpX1cl0anVQbQyrjdPypV399fmfPZvas1fOeDqxerFuxNjAaQydRvpo/4tibeAsDJ2F9NFuio+4BVBtm2s=')
jk = upup
print(jk)
exec(marshal.loads(jk))
upup = None
loader()
Et hop, dans la console, on a le flag!
└─[$] <git:(feat/start_create_amsi_ctf_writeup*)> python test.py
Starting the challenge...
b'\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x13\x00\x00\x00\xf3z\x00\x00\x00\x97\x00d\x01}\x00t\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00}\x01|\x01d\x03k\x02\x00\x00\x00\x00r\x14t\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x04|\x00\x9b\x00\x9d\x02\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x00S\x00t\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x05\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x00S\x00)\x06N\xfa\x1aAMSI{pyc_is_n0t_th4T_H4rd}\xfa"Enter the key to unlock the flag: \xda\nOpenSesame\xfa\x16Correct! The flag is: \xfa\x15Wrong key. Try again!)\x02\xda\x05input\xda\x05print)\x02\xda\x0bsecret_flag\xda\x03keys\x02\x00\x00\x00 \xfa5/home/kali/Documents/pycon/test/generate_byte_code.py\xda\x0factual_function\xfa0generate_encrypted_code.<locals>.actual_function\t\x00\x00\x00sR\x00\x00\x00\x80\x00\xd8\x162\x88\x0b\xdd\x0e\x13\xd0\x148\xd1\x0e9\xd4\x0e9\x88\x03\xd8\x0b\x0e\x90,\xd2\x0b\x1e\xd0\x0b\x1e\xdd\x0c\x11\xd0\x128\xa8;\xd0\x128\xd0\x128\xd1\x0c9\xd4\x0c9\xd0\x0c9\xd0\x0c9\xd0\x0c9\xe5\x0c\x11\xd0\x12)\xd1\x0c*\xd4\x0c*\xd0\x0c*\xd0\x0c*\xd0\x0c*\xf3\x00\x00\x00\x00'
Enter the key to unlock the flag:
AMSI{pyc_is_n0t_th4T_H4rd}