← Back to Writeups
CUSTOMEasyReversing

Matryoshka

XESXOR9/2/20263 min read
#reversing#custom#easy#inverse_function_construction#pyc_decompilation#reverse_cipher_chain

Matryoshka

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: inverse_function_construction, pyc_decompilation, reverse_cipher_chain

Summary

Walkthrough of the CUSTOM Reversing Matryoshka. Task: reverse a chain of transformations applied by a Python bytecode program. Solution: decompile the .pyc file with uncompyle6, then apply inverse operations in reverse order (from_binary, atbash, r

Description

Что-то очень на крипту похоже... (Something looks very much like crypto...)

Files provided:

  • matryoshka.zip containing:
    • __pycache__/easy_reverse.cpython-38.pyc - Python 3.8 bytecode
    • __pycache__/encrypt.txt - Encrypted data in binary format

Analysis

Step 1: Extract and Examine Files

The archive contains a Python 3.8 compiled bytecode file and encrypted data:

01010011 01011011 00111101 00111001 01001110 00111101 01001010 00111101 01001110 01110001 01100010 00111111 01110001 01000000 00110111 01010000 01010111 01001110 00100111 01010101 01110110 01101110 00101011 00110011 00101101 01101011 00101100

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Decompile the .pyc File

Using uncompyle6 to decompile the bytecode:

bash
uncompyle6 easy_reverse.cpython-38.pyc

Decompiled source:

python
import string

def rot13(text):
    result = []
    for i in text:
        char = i - 13
        if char < 32:
            char += 95
        result.append(chr(char))
    return "".join(result)

def atbash(text):
    alphabet = string.ascii_uppercase + string.ascii_lowercase
    reversed_alphabet = alphabet[::-1]
    table = str.maketrans(
        alphabet + string.digits + string.punctuation,
        reversed_alphabet + string.digits[::-1] + string.punctuation[::-1]
    )
    return text.translate(table)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

def to_hex(text):
    result = []
    for i in text:
        result.append(hex(ord(i)))
    return result

def xor(text):
    result = []
    for i in text:
        result.append(int(i, 16) ^ 8)
    return result

def reverse_text(text):
    return text[::-1]

def to_binary(text):
    result = []
    for i in text:
        result.append(bin(ord(i))[2:].zfill(8))
    return " ".join(result)

text = input()
print(to_binary(atbash(reverse_text(rot13(xor(to_hex(text)))))))

Step 3: Understand the Encryption Pipeline

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The encryption chain is:

input -> to_hex -> xor(8) -> rot13 -> reverse -> atbash -> to_binary -> output

To decrypt, we need to reverse each operation in reverse order:

encrypted -> from_binary -> atbash -> reverse -> rot13_inverse -> xor(8) -> from_hex -> flag

Key observations:

  • atbash is self-inverse (applying twice returns original)
  • reverse is self-inverse
  • xor(8) is self-inverse (XOR is its own inverse)
  • rot13 needs an inverse function (add 13 instead of subtract)
  • binary/hex are encoding operations that need decoding ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

python
#!/usr/bin/env python3
import string

encrypted = "01010011 01011011 00111101 00111001 01001110 00111101 01001010 00111101 01001110 01110001 01100010 00111111 01110001 01000000 00110111 01010000 01010111 01001110 00100111 01010101 01110110 01101110 00101011 00110011 00101101 01101011 00101100"

def from_binary(text):
    return "".join(chr(int(b, 2)) for b in text.split())

def atbash(text):
    alphabet = string.ascii_uppercase + string.ascii_lowercase
    reversed_alphabet = alphabet[::-1]
    table = str.maketrans(
        alphabet + string.digits + string.punctuation,
        reversed_alphabet + string.digits[::-1] + string.punctuation[::-1]
    )
    return text.translate(table)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

def reverse_text(text):
    return text[::-1]

def rot13_reverse(text):
    result = []
    for c in text:
        char = ord(c) + 13
        if char > 126:
            char -= 95
        result.append(char)
    return result

def xor_reverse(values):
    return [hex(v ^ 8) for v in values]

def from_hex(hex_values):
    return "".join(chr(int(h, 16)) for h in hex_values)

# Decrypt step by step
step1 = from_binary(encrypted)    # S[=9N=J=Nqb?q@7PWN'Uvn+3-k,
step2 = atbash(step1)             # h*.0m.q.mJY,J+2kdm^fEM@6>P?
step3 = reverse_text(step2)       # ?P>6@MEf^mdk2+J,YJm.q.m0.*h
step4 = rot13_reverse(step3)      # [76, 93, 75, ...]
step5 = xor_reverse(step4)        # ['0x44', '0x55', '0x43', ...]
flag = from_hex(step5)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

print(flag)

Lessons Learned

  1. Python bytecode decompilation - .pyc files can be decompiled with tools like uncompyle6, pycdc, or decompyle3
  2. Cipher chain reversal - When reversing a chain of transformations, apply inverse operations in reverse order
  3. Self-inverse functions - Some operations like XOR, atbash, and reverse are their own inverses
  4. Custom ROT13 - This implementation uses ASCII range 32-126 (95 characters), not standard ROT13

Tools Reference

  • uncompyle6 - Python 2.7-3.8 decompiler: pip install uncompyle6
  • pycdc - Alternative decompiler for newer Python versions
  • decompyle3 - For Python 3.7+ ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering inverse_function_construction, pyc_decompilation, reverse_cipher_chain was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-01-09 • CUSTOM • Matryoshka • Difficulty: Easy