# Reversconstrictor

**Catégorie:** Rétro-Ingénierie - **Difficulté:** Facile

{% file src="/files/220tLVAOaoQQjPlbGff1" %}

**Description:**

<figure><img src="/files/CaxoJE5jDIJGfayoA4Mk" alt=""><figcaption></figcaption></figure>

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 :&#x20;

<figure><img src="/files/e4U5c5C1Emx5D53SaSvq" alt=""><figcaption></figcaption></figure>

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...).

Pour aller plus loin, nous allons utiliser un outil nommé [pyinstxtractor](https://github.com/extremecoders-re/pyinstxtractor/blob/master/pyinstxtractor.py) afin de récupérer les sources contenues dans le binaire :&#x20;

<figure><img src="/files/PCMEqYVF2nlLW7kd96NU" alt=""><figcaption></figcaption></figure>

Et maintenant nous avons de nouveaux fichiers, dont un .pyc qui nous intéresse beaucoup :&#x20;

<figure><img src="/files/evAI98TRoHr37DEhZOAc" alt=""><figcaption></figcaption></figure>

À 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 :&#x20;

<figure><img src="/files/YLPA00mM5FEqWSSGgwIh" alt=""><figcaption></figcaption></figure>

Nous obtenons ce code python en clair, c'est merveilleux :&#x20;

```python
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 :&#x20;

<figure><img src="/files/lRJIRNxT7UyAOK1A2Fjs" alt=""><figcaption></figcaption></figure>

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 [service en ligne](https://pylingual.io/) qui l'a fait sans souci.

Voici le code complet :&#x20;

```python
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) :&#x20;

```python
# ---------------------------------------------------------------------------
# 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)

```

<figure><img src="/files/fwP8gPJIhvXU6BMi8LTz" alt=""><figcaption></figcaption></figure>

<details>

<summary>🚩FLAG</summary>

`404CTF{D0_y0U_L0v3_Pyth02?1_l0v3_pYt60n!}`

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://writeups.ayweth20.com/2025/404ctf-2025/retro-ingenierie/reversconstrictor.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
