Reversconstrictor
Last updated
Was this helpful?
Last updated
Was this helpful?
CatÊgorie: RÊtro-IngÊnierie - DifficultÊ: Facile
Description:
Solution:
Nous avons à disposition un binaire dont nous ne connaissons pas le contenu. En regardant le titre du challenge, nous avons une petite idÊe de ce qu'il peut cacher mais nous allons faire du repÊrage :
Bon cela nous apporte quelques infos, mais nous allons surtout nous focus sur la partie "python" qui est l'objectif pour lequel le titre rÊfÊrence (boa constrictor / python...).
Et maintenant nous avons de nouveaux fichiers, dont un .pyc qui nous intÊresse beaucoup :
à partir de maintenant, il s'agit d'un simple reverse de .pyc, comme dans chaque CTF. Nous allons donc procÊder Êtape par Êtape en commençant par dÊcompiler le .pyc :
Nous obtenons ce code python en clair, c'est merveilleux :
import tkinter as tk
import importlib.util as importlib
import os
import sys
def import_module():
module_path = os.path.join(sys._MEIPASS, 'modules/encrypt_key.cpython-39.pyc')
spec = importlib.util.spec_from_file_location('nom_module', module_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
module = import_module()
def xor(a, b):
return bytes((lambda .0: for x, y in .0:
x ^ y)(zip(a, b)))
def validate_password(password):
if xor(module.encode_password(password.encode('ascii')), module.encrypt_key(0x6D39D56F8A40A6BBE43A82A53B2C762EA780C21A32C6B3EF765D3A54F3432432F3E6D39D56F8A40A6BBE43A82A53B2C762EA780C21A32C6B3EF765D3A54F3432432F3E)) == b'\xe9J\x1aB\xe2\xc5\xf3S\'\xd6>\n$\x94\x1a\x07\'F\xc6\xa1\x07\xb7\xcc\xec\xe1\x84\xec\xac\xe4\xd64\x8f\xc3\x12\x04\x16$n\x15\xec\xe1\xaee5\xc7\xecOX"\x98EO\x1f2\xb4\x15\xc4\xed\xf4\xcd$\xd3\xd3u\xc2\xf8\xc6\xae\x06\x08\xcd\xff\xe0(\xe9\xb0\xe7\xde6\x90\xcc\xfd\x02}%\x1a\x1a\xc9#\x10\xc2\x86\x06\x08\xcd\xfe&\xb8K\x0f)\x9a\xb6\xb9\x02\x17\xa0\xd8\xe4]\x98\xf5*\x154<\x06\x875\xbd\x05@\xe6\x88\xe3&6%\xcc\x18\x06\\%\xa4\x1a7!\xfe\xc3\xae\x06\x08\xcd\xff\xe2\x18\xe2x\xe0\x927x\r\xfa\xa6\xbd\xe67\x97\xf7\xe5)f\x94\xc8\xbdv\r\xef\x12\x1bZ\xe8e\xf3S\'\xd6>\n"8\x1be\x9c\xdf\xe8\x9b\x06\xb7\x0b3V\x1f\xedN\x87\xbbI!C>8z%\xc0\xeaM\xb5\xd1p\xd1\x0f|A\xd7B\x03\xc54\xd5T\xb9\xfd\x88;\xbf\x10\x81L\x90L\x0b\xff\xed\xe1\xe5dQ\xc4\x17\xd5\xafUl\xec':
label.config('Mot de passe correct !', **('text',))
else:
label.config('Mot de passe incorrect !', **('text',))
root = tk.Tk()
root.title('RÃĒve en Python')
root.geometry('300x200')
label = tk.Label(root, 'Entrez le mot de passe :', **('text',))
label.pack(10, **('pady',))
entry_password = tk.Entry(root, 20, **('width',))
entry_password.pack(5, **('pady',))
validate_button = tk.Button(root, 'Valider', (lambda : validate_password(entry_password.get())), **('text', 'command'))
validate_button.pack(5, **('pady',))
root.mainloop()
à ce que nous pouvons voir à la ligne 7, il fait appel à un autre fichier : modules/encrypt_key.cpython-39.pyc
Nous allons donc procÊder de la mÃĒme manière pour rÊcupÊrer son code :
Voici le code complet :
def encrypt_key(key):
for _ in range(100):
key <<= 1
key ^= 28822426245224264980979321341345830828356797782055329828562090481380116178932605047916536097759912367507574811007140506795012665313071649493970094581868771201400995139374360577815608383329090103210656811239441222567030843263175788045939510447941542030750571932256144094323129497438168350642218592361235189388565721332503735143045072480576796605508643464856724395849208355789353984327745285879034330837484467961665782705864897713128496005106869640456653659559683849265346017315593794571762231394086134713064707220190583043994656101899455622598958597972721263137718293457676368185324168958757297967087684
key >>= 1
key &= 109051226663159329753852712655361641732299866997884252194334358336942491585415479875295813282055589726130083576518139892456534344478282750316593419244506314188673419372046405908627553201165095804542555714314672295089748281211620003277814236498206175511101163526217782040005646393337007890354251307329101818031816904956050245603083420859836404091717206011901780489732016582515541865280619889684277395118340072030441380240101696073919110415984233261149866579378832210321108153068228794726382510988474475602148132965289631295957683363624794221017463029077341994983080187012597951461555452400067297865364263
key -= 30192244264443276570339417266820821425000308120852003855173657187210512774975257998930431381307567916542287355804911140923844613245045835307895499468937998079600051550088167870781968330783540846793847843565906160661919016260958589152016311435717470936180510788226717267994601294202007594965057260235235670775632982187962998589201876115127904069810375748935472032501147620317590198256980636488859805187975959355302150868894406858968080766062052395340075809777559133433991365763567236747759028423915038949095093046477455353960143902941004723715753803085977215629377804159634990715729987578541999707000127
key ^= 28822426245224264980979321341345830828356797782055329828562090481380116178932605047916536097759912367507574811007140506795012665313071649493970094581868771201400995139374360577815608383329090103210656811239441222567030843263175788045939510447941542030750571932256144094323129497438168350642218592361235189388565721332503735143045072480576796605508643464856724395849208355789353984327745285879034330837484467961665782705864897713128496005106869640456653659559683849265346017315593794571762231394086134713064707220190583043994656101899455622598958597972721263137718293457676368185324168958757297967087684
key <<= 1
key += 4324354
key >>= 1
key = abs(key)
encrypted_bytes = key.to_bytes((key.bit_length() + 7) // 8, byteorder='big')
return encrypted_bytes
def encode_password(password):
a = b''
x_list = [110, -34, -230]
for i in range(len(password)):
b = password[i] // 11 + 11
c = password[i] % 11
d = b + c
e = b * c
r = []
for i in range(3):
x = x_list[i]
y = x ** 2 - d * x + e
assert y > 0 and y < 65536 and (y not in r)
r.append(y)
else:
for i in range(3):
a += bytes.fromhex(f'{r[i] // 256:02x}')
a += bytes.fromhex(f'{r[i] % 256:02x}')
else:
return a
Nous avons donc toutes les pièces en main pour dÊchiffrer tout cela. Pour faire ça automatiquement, nous allons crÊer un script Python (pour rester dans l'ambiance) :
# ---------------------------------------------------------------------------
# 1) Constantes du challenge
# ---------------------------------------------------------------------------
TARGET = bytes.fromhex(
"e94a1a42e2c5f35327d63e0a24941a072746c6a107b7ccece184ecace4d6348f"
"c3120416246e15ece1ae6535c7ec4f582298454f1f32b415c4edf4cd24d3d375"
"c2f8c6ae0608cdffe028e9b0e7de3690ccfd027d251a1ac92310c2860608cdfe"
"26b84b0f299ab6b90217a0d8e45d98f52a15343c068735bd0540e688e3263625"
"cc18065c25a41a3721fec3ae0608cdffe218e278e09237780dfaa6bde63797f7"
"e5296694c8bd760def121b5ae865f35327d63e0a22381b659cdfe89b06b70b33"
"561fed4e87bb4921433e387a25c0ea4db5d170d10f7c41d74203c534d554b9fd"
"883bbf10814c904c0bffede1e56451c417d5af556cec"
)
CST = int(
"6D39D56F8A40A6BBE43A82A53B2C762EA780C21A32C6B3EF765D3A54F3432432F3E"
"6D39D56F8A40A6BBE43A82A53B2C762EA780C21A32C6B3EF765D3A54F3432432F3E",
16
)
# ---------------------------------------------------------------------------
# 2) encrypt_key â repris mot-pour-mot du .pyc
# ---------------------------------------------------------------------------
def encrypt_key(key: int) -> bytes:
for _ in range(100):
key <<= 1
key ^= 0x40440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044440440444044404440440440440440444044440440444044
key >>= 1
key &= 0xF3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF33271ADF3F3271ADF3271ADF3F3271ADFF3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF33271ADF3F3271ADF3271ADF3F3271ADFF3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF33271ADF3F3271ADF3271ADF3F3271ADFF3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF3F3271ADF3271ADF33271ADF3F3271ADF3271ADF3F3271ADF1ADFF3271ADF1ADFADF3F3271ADF1ADFF3271ADF1ADFADF3F327
key -= 0x4351EAC5DB5A0D3F31513511EAC5DB5A0D3F3521EAC5DB5A0D3F3151EAC5DB5A0D3F2143EAC5DB5AEAC5DB5A0D3F3151EAC5DB5A0D3F31514351EAC5DB5A0D3F31513511EAC5DB5A0D3F3521EAC5DB5A0D3F3151EAC5DB5A0D3F2143EAC5DB5AEAC5DB5A0D3F3151EAC5DB5A0D3F31514351EAC5DB5A0D3F31513511EAC5DB5A0D3F3521EAC5DB5A0D3F3151EAC5DB5A0D3F2143EAC5DB5AEAC5DB5A0D3F3151EAC5DB5A0D3F31514351EAC5DB5A0D3F31513511EAC5DB5A0D3F3521EAC5DB5A0D3F3151EAC5DB5A0D3F2143EAC5DB5AEAC5DB5A0D3F3151EAC5DB5A0D3F315131510D3F31513151DB5A0D3F315131510D3F31513151DB5A0D3F
key ^= 0x40440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044044044044044404404404404404440440440440440444044440440444044404440440440440440444044440440444044
key <<= 1
key += 4324354
key >>= 1
key = abs(key)
return key.to_bytes((key.bit_length() + 7) // 8, "big")
# ---------------------------------------------------------------------------
# 3) encode_byte (une lettre -> 6 octets) et table inverse
# ---------------------------------------------------------------------------
X = [110, -34, -230]
def encode_byte(p: int) -> bytes:
b, c = p // 11 + 11, p % 11
d, e = b + c, b * c
r = [x * x - d * x + e for x in X] # trois entiers 16 bits
out = bytearray()
for y in r:
out += y.to_bytes(2, "big")
return bytes(out)
# table â6 octets â caractèreâ
DEC = {encode_byte(p): p for p in range(256)}
# ---------------------------------------------------------------------------
# 4) DÊcodage : TARGET â encrypt_key(CST) â mot de passe
# ---------------------------------------------------------------------------
enc_key = encrypt_key(CST)
encoded = bytes(t ^ k for t, k in zip(TARGET, enc_key))
password = "".join(
chr(DEC[encoded[i : i + 6]]) # 6 octets = 1 caractère
for i in range(0, len(encoded), 6)
)
print(password)
Pour aller plus loin, nous allons utiliser un outil nommÊ afin de rÊcupÊrer les sources contenues dans le binaire :
Alors ici ça n'a pas fonctionnÊ comme prÊcÊdemment, et c'est assez Êtrange... Mais pour rÊparer ça, j'ai utilisÊ un qui l'a fait sans souci.