ThaySan
  • 👋Bienvenue
    • Avant-propos
  • 🚩CTF & Writeups
    • 2025 | EC2
      • Let's Crax
    • 2025 | HackDay
      • 🔑Cryptographie
        • Drunk Christmas
        • Find Eve 1
        • Well hidden message - Standard Code Encryption
      • 🔎Forensic
        • Copperwire Extraction
        • Distracted user
        • I believe you can't fly
      • 🧠Misc
        • Hello Steve
      • 💾Programmation
        • Lonely bot
        • Lonely bot #2
        • Lonely bot #3
        • Useless Animals Sorting
        • Who cares about SSI anyway ?
      • ⚙️Reverse
        • Just dig
        • Rusty_rev
        • The Cogs of Blackmail
      • 🎭Steganographie
        • It says a lot when there is no music
        • Mona LSB
        • Well hidden message - Insignificant blue
      • 🌐Web
        • Super Website Verificator 3000
        • The analytical engine leak
        • The Watchful Gears: Uncover the Secrets Within
        • Your region's finest
    • 2024 | Efrei - CyberNight
      • 🔑Cryptographie
        • Clavier spécial
        • Le futur
        • Securechain 30.0
        • Cascade
        • Shared Flag
        • Weird Algorithm
      • 🧊Minecraft
        • Introduction
        • Non aux Bouquins Trafiqués
        • Redstone Gold Block
      • 💾Programmation
        • Captcha 1.0
        • Intro à la prog
        • Captcha 2.0
      • ⚙️Reverse
        • Reverse the Reverse
        • Find me if you can
        • HuGO Decrypt
        • Kitten eXORcism
        • M30W Vault Tech
        • The Ugandan Labyrinth
      • 🎭Stéganographie
        • Le message
        • bon Le ordre
        • COD FORFLAG
        • Mauvaise note
        • Bruit
        • Un (un ?) drôle de QR code
        • Randonnée Guillerette à Bordeaux
      • 💻Système
        • Marche-pied
        • Escabot
        • régulation des Données et des Normes de Sécurité
      • 🌐Web
        • cybernight.zip
        • Mon champion préféré
        • Co(mpressed)okies
        • Gitty Mistake
        • JWT Kiddo
        • Parseur Farceur
      • L'enquête
        • L'enquête 2/6
        • L'Enquête 1/6
        • Bienvenue, enquêteur
    • 2024 | Star-Hack
      • 🔑Cryptographie
        • César, mais pas César
        • Double ennui
        • Langage secret
        • Quadratique Mystérieuse
        • ReSultAt
        • Sup3r S3cr3t
        • Warmup
      • 🔎Forensic
        • Cache-cache
        • Fichier ZIP protégé par mot de passe
        • HEADER
        • Investigation 1
        • Investigation 2
      • 🧠Misc
        • B4l4d3 Urb41n3
        • Bruhh, c'est quoi ça ?
        • Cut13
        • Pika Pika
      • 😈Pwn
        • Pwn0x01
        • Pwn0x02
        • Pwn0x03
      • ⚙️Reverse
        • Assembly
        • Rev0x00
        • Rev0x01
        • Rev0x02
      • 🌐Web
        • Clone Udemy
        • Flask
        • Guess_The_Passcode
        • PHP
        • Tickets
        • Usine de Cookies
    • 2024 | ECW
      • 🔑Cryptographie
        • Course hipPIN
    • 2024 | CTFREI Intro
      • 🔑Cryptographie
        • AES Intro
        • Aléatoire
        • Game of Luck
        • RSA Intro
        • RSA2
        • RSA3
      • 🔎Forensic
        • Mais qui est le photographe ?
        • Mais où est passé mon flag ?
        • Mémoire 1/4
        • Mémoire 2/4
        • Mémoire 3/4
        • Mémoire 4/4
        • My computer is talking
      • 📚OSINT
        • Avion ✈
        • Geoint
        • Google!
        • Googlint
        • Le pivot
        • Le temps commence maintenant
        • Sacré dossier
        • Socint
      • 💾Programmation
        • Try Me
        • Answer Me
        • Eval Me
        • Time Based
      • 😈Pwn
        • BOF Intro
        • Shop
        • BOF 2
        • BOF win()
      • ⚙️Reverse
        • CrackMe1
        • CrackMe2
        • CrackMe3
        • Hidden...
        • Something changed?
        • ZZZ
      • 🎭Stéganographie
        • Cybernight être comme
        • Joli paysage
        • Petit poisson
        • StegHide 'n' Seek
        • Un canard pas comme les autres
      • 💻Système
        • Bash Jail
        • Bash Jail Revenge
        • BrokenBin
        • GTFO Of Here
        • Pyjail
        • Pyjail Revenge
        • Strange input, right?
      • 🌐Web
        • SQLi
        • POST This Money
        • Give me my Flask PIN
        • Access
        • Render
        • RenderV2
        • Touchy
    • 2024 | DefCamp
      • 🔑Cryptographie
        • conv
        • oracle-srl
        • ctr
      • 🔎Forensic
        • i-got-a-virus
        • Alternating
        • call-me-pliz
      • 🧠Misc
        • pyterm
      • 📱Mobile
        • mobisec
      • 📚OSINT
      • 😈Pwn
      • ⚙️Reverse
      • 🕵️Traque
      • 🌐Web
        • noogle
        • production-bay
    • 2024 | 404CTF
      • 🔑Cryptographie
        • Bébé nageur
        • Le petit bain
        • Poor Random Number Generator [1/2]
        • Plongeon Rapide Super Artistique
        • J'éponge donc j'essuie
        • Poor Random Number Generator [2/2]
        • La Seine
        • J'ai glissé chef !
        • SEA - La face cachée de l'Iceberg
      • 🔎Forensic
        • Le tir aux logs
        • Darts Bank
        • Vaut mieux sécuriser que guérir
        • De bons croissants au beurre
        • Poids Raw vs. Cours Jette [1/3]
      • 🔌Hardware
        • Serial killer
        • Le soulevé de GND
        • Comment est votre modulation ? [1/2]
        • Sea side channel [1/4] - Introduction
        • Comment est votre modulation ? [2/2]
        • Sea side channel [2/4] - Reconnaissance
        • Sea side channel [3/4] - Mais où sont les triggers ?
      • 🤖IA
        • Du poison [1/2]
        • Du poison [2/2]
        • Des portes dérobées
      • 🧠Misc
        • Discord
        • De la friture sur la ligne
        • Bienvenue
        • Revers(ibl)e Engineering [0/2]
      • 📚OSINT
        • Légende
        • Not on my watch
        • Secret Training [1/2]
      • 😈Pwn
        • Pseudoverflow
        • Jean Pile
        • Mordu du 100m
        • Antismash
      • 🐈‍⬛Quantique
        • Des trains superposés
        • De l'écoute, pas très discrète
        • De la multiplicité des problèmes
      • ⚙️Reverse
        • ⭐Échauffement
        • ⭐Intronisation du CHAUSSURE
        • ⭐Bugdroid Fight [1/2]
        • ⭐Revers(ibl)e Engineering [1/2]
        • ⭐Bugdroid Fight [2/2]
        • ⭐Nanocombattants
        • ⭐Revers(ibl)e Engineering [2/2]
        • Le Tableau Noir
      • 🎭Stéganographie
        • ⭐L'absence
        • ⭐Regarder en stéréo
        • ⭐La Barre Fixe
        • ⭐Le grand écart
        • ⭐La chute
      • 🌐Web
        • ⭐Vous êtes en RETARD
        • ⭐Le match du siècle [1/2]
        • ⭐Exploit mag
        • ⭐Le match du siècle [2/2]
        • ⭐LE GORFOU 42
        • ⭐La boutique officielle
    • 2024 | CTFREI - Bordeaux
      • 🔑Cryptographie
        • zzz
      • 📚OSINT
        • Alexis Dumas
        • Back to the bureau
        • Dr Octopus
        • Folie et ambition
        • GeoGuessr
        • Hugo Nelots : prélude
        • La fin ?
        • La fuite Dumas
        • Un réseau suspect
      • 💾Programmation
        • Eval me 1
        • Eval me 2
        • Time Based
      • 💻Système
        • Broken Binary 1
        • Broken Binary 2
        • GTFO of here
        • Pyjail 1
        • Pyjail 2
        • Pyjail 3
        • Pyjail 4
      • 🌐Web
        • Au commencement était le verb
        • Becadmin
        • PHP Juggler
    • 2024 | HTB - Cyber Apocalypse Challenges
      • 🔗Blockchain
        • Lucky Faucet
        • Recovery
        • Russian Roulette
      • 🔑Cryptographie
        • Blunt
        • Dynastic
        • Iced TEA
        • Makeshift
        • Primary Knowledge
      • 🔎Forensic
        • An unusual sighting
        • Data Siege
        • Fake Boost
        • Game Invitation
        • It Has Begun
        • Phreaky
        • Pursue The Tracks
        • Urgent
      • 🔌Hardware
        • BunnyPass
        • Flash-ing Logs
        • Maze
        • Rids
        • The PROM
      • 🧠Misc
        • Character
        • Cubicle Riddle
        • Path of Survival
        • Stop Drop and Roll
        • Unbreakable
        • Were Pickle Phreaks
        • Were Pickle Phreaks Revenge
      • 😈Pwn
        • Delulu
        • Pet Companion
        • Tutorial
        • Writing on the Wall
      • ⚙️Reverse
        • BoxCutter
        • Crushing
        • FollowThePath
        • LootStash
        • MazeOfPower
        • Metagaming
        • PackedAway
        • QuickScan
      • 🌐Web
        • Flag Command
        • KORP Terminal
        • Labyrinth Linguist
        • LockTalk
        • Testimonial
        • TimeKORP
    • 2024 | UNbreakable
      • 🔑Cryptographie
        • start-enc
        • traffic-e
      • 🔎Forensic
        • easy-hide
        • password-manager-is-a-must
      • 🧠Misc
        • rfc-meta
      • 📱Mobile
        • flagen
        • improper-configuration
      • 📡Network
        • wifi-basic
        • wifiland
      • 📚OSINT
        • persistent-reccon
        • safe-password
      • 😈Pwn
        • intro-to-assembly
      • ⚙️Reverse
        • fake-add
      • 🎭Stéganographie
        • secrets-of-winter
      • 🌐Web
        • pygment
        • sided-curl
        • you-can-trust-me
    • 2023 | EFREI - CyberNight
      • 📚OSINT
        • Invest Now !
      • 😈Pwn
        • NSA Call Converter
      • ⚙️Reverse
        • CryptoVirus
        • WebChaussettes
      • 🌐Web
        • DoctoLeak
    • 2023 | Flag4All
      • 🔑Cryptographie
        • Aes IV
        • Crypt my loop
        • Kentucky fried chicken
        • RSA primes
        • Xor
    • 2022 | EFREI - CyberNight
      • 🔑Cryptographie
        • Coupé-décalé
        • ExFILEtration
        • Il s'est baissé ou pas
        • J'ai pas roté
        • Les allemands !
        • RSA Strong Prime generator
      • 🔎Forensic
        • Bomberman 1/2
        • Bomberman 2/2
        • Magic
        • Peu importe le chemin
        • Sniff sniff
        • Souvenir
        • Xray
      • 🔌Hardware
        • Class4
        • Find me 2/3
        • Identify 1/3
        • Yo listen 3/3
      • 🧠Misc
        • Et je tombe tombe tombe
        • Des yeux partout
        • RiGOLe
        • Roomba tricheur
        • Survey
        • Tinder
      • 💾Programmation
        • Repeat
        • Startup
        • Timing
      • ⚙️Reverse
        • Auth 1
        • Auth2
        • Auth3
        • Cryptoroomba
        • Tenet
      • 🎭Stéganographie
        • 50 shades of stephane legar
        • Chess master
        • Deviens champion sers toi de tout ce que tu as appris
        • Drifting in the bits
        • Pyramide
        • Spirale
      • 🌐Web
        • Ah bah c'est du propre
        • Cooking roomba
        • Leaderboard
        • vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
Propulsé par GitBook
Sur cette page
  • Challenge
  • Préparation
  • Analyse des binaires reçus
  • Automatisation de GDB
  • Résolution

Cet article vous a-t-il été utile ?

  1. CTF & Writeups
  2. 2024 | 404CTF
  3. Reverse

Revers(ibl)e Engineering [1/2]

404CTF{e9d749db81e9f8caf745a5547da13579}

Catégorie: Reverse Difficulté: medium Flag: -

Challenge

Description


Après une année éprouvante marquée par les compétitions, vous décidez de rentrer dans votre village natal. C'est avec beaucoup d'émotion que vous apercevez le dojo de votre enfance et décidez de vous y rendre. Votre ancienne sensei vous y attend, le sourire aux lèvres.

"La clairvoyance est l'arme la plus redoutable du combattant. Anticiper chaque mouvement avant qu'il ne soit lancé, voilà la véritable maîtrise du combat. Relève mon défi et prouve ta valeur."


Récupérer une archive zip avec netcat contenant un crackme et un token, renvoyer le token avec la solution du crackme à un deuxième serveur, recevoir un flag... Facile. Petit détail : vous avez vingt secondes pour faire tout ça, et le binaire change à chaque essai.

Connexion : nc challenges.404ctf.fr 31998 > chall.zip nc challenges.404ctf.fr 31999

Ce challenge tourne sur un docker et n'est pas disponible

Préparation

On va commencer par construire, en Python, une classe pour faciliter la réception et l'étude des binaires. On fera évoluer ce code au fil de notre progression

import socket
import os
from io import BytesIO
from zipfile import ZipFile


HOST = 'challenges.404ctf.fr'
PORT_BINARY = 31998
PORT_SOLVE = 31999

class Binary:
    FOLDER = 'out'
    
    @staticmethod
    def fetch() -> 'Binary':
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((HOST, PORT_BINARY))
        archive = b''
        while True:
            data = client.recv(4096)
            if len(data) == 0:
                break
            archive += data
        archive = ZipFile(BytesIO(archive))
        crackme = archive.read('crackme.bin')
        token = archive.read('token.txt').decode()
        return Binary(crackme, token)

    @staticmethod
    def open(filename):
        token = filename.split('_')[1][:-4]
        with open(os.path.join(Binary.FOLDER, filename), 'rb') as f:
            crackme = f.read()
        return Binary(crackme, token)

    def __init__(self, crackme: bytes, token: str) -> None:
        self.crackme = crackme
        self.token = token
        self.filename = f'crackme_{self.token}.bin'
        self.filepath = os.path.join(self.FOLDER, self.filename)
    
    def save(self):
        if not os.path.exists(self.FOLDER):
            os.mkdir(self.FOLDER)
        with open(self.filepath, 'wb') as f:
            f.write(self.crackme)
        os.system(f'chmod +x {self.filepath}')
    
    def solve(self) -> str:
        # A compléter
        pass


def main() -> None:
    binary = Binary.fetch()
    print(f'Token: {binary.token}')
    


if __name__ == '__main__':
    main()

Ce script permet de récupérer l'archive depuis le serveur, extraire le crackme et le token associé, puis de les enregistrer localement. On peut également ouvrir un crackme déjà enregistré.

La fonction solve sera implémentée plus tard et retournera le mot de passe qui résout le challenge


Analyse des binaires reçus

On peut s'amuser à enregistrer 2-3 crackme et les décompiler pour comparer leurs différences.

int main(int argc,char **argv) {
  int exit_code;
  int is_different;
  size_t size_password;
  void *encrypted_password;
  undefined8 key_part_1;
  undefined8 key_part_2;
  int size;
  
  if (argc < 2) {
    puts("J'ai besoin d'un argument!");
    exit_code = 1;
  }
  else {
    size_password = strlen(argv[1]);
    size = (int)size_password;
    if (size == 16) {
      key_part_1 = 0xf2861cf7978787b5;
      key_part_2 = 0x1c940c8696a668b4;
      encrypted_password = encrypt((long)argv[1]);
      is_different = memcmp(encrypted_password,&key_part_1,16);
      if (is_different == 0) {
        puts("GG!");
        exit_code = 0;
      }
      else {
        puts("Dommage... Essaie encore!");
        exit_code = 1;
      }
    }
    else {
      puts("L'argument doit comporter 16 caractères.");
      exit_code = 1;
    }
  }
  return exit_code;
}

La structure est toujours la même :

  • On doit rentrer un mot de passe en argument.

  • Le mot de passe doit faire 16 caractères.

Mais 2 choses changent constamment :

  • les opérations faites dans la fonction encrypt qui est utilisée sur notre mot de passe

  • la valeur de key à laquelle le résultat de notre mot de passe chiffré est comparé

On ne va pas s'amuser à reverse la fonction encrypt mais on peut voir sa sortie en exécutant un des binaires avec GDB (ou autres débugueurs) et en plaçant un breakpoint juste avant la comparaison. Le breakpoint à placer est sur memcmp@plt (je précise "plt" car nous voulons break avant de rentrer dans la résolution de l'adresse de la vraie fonction memcmp et ainsi pouvoir regarder la valeur des paramètres situés dans les registres RAX et RCX).

$ gdb ./crackme_329455874d977e59dcb72eb58779ec5c.bin

gef➤ b memcmp@plt
Breakpoint 1 at 0x1050

gef➤ r 0123456789abcdef
[#0] Id 1, Name: "crackme_3294558", stopped 0x555555555050 in memcmp@plt (), reason: BREAKPOINT

gef➤  x/2xg $rax
0x7fffffffdd10: 0xf2861cf7978787b5      0x1c940c8696a668b4

gef➤  x/2xg $rcx
0x5555555592a0: 0xb5f492d3f1d4f2d7      0xa4c28385a0866a6b

Dans RAX, on voit bien les valeurs croisées pendant la décompilation. Et dans RCX, sans oubliler que c'est du Little Endian, on a :

Mot de passe : 0123456789abcdef
RCX : d7f2d4f1d392f4b56b6a86a08583c2a4

En relançant avec un mot de passe différent, on obtient :

Mot de passe : 0000111122223333
RCX : d7d7d7d7f2f2f2f2d4d4d4d4f1f1f1f1

Ce que l'on peut en déduire : la fonction encrypt chiffre chaque caractère de manière indépendante.

On peut donc essayer de faire un mapping en testant toute une liste de caractères et regarder en quoi ils sont transformés. Une fois ce mapping fait, il suffira alors de regarder la valeur de key pour regarder les correspondances attendues.


Automatisation de GDB

Il y a surement un façon plus simple avec Pwntools d'automatiser GDB, mais je n'ai pas réussi à le faire fonctionner de mon côté

Ici, on va utiliser un script python que l'on passera avec l'option --command à GDB. Ce script va pouvoir utiliser l'API de GDB pour exécuter des commandes (comme ajouter un breakpoint et lire les registres).

import json

# Les caractères possibles pour le mot de passe
CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
KEY_SIZE = 16

# Récupère la valeur 16 bytes d'une adresse dans un registre
def get_register(register: str) -> bytes:
	return b''.join([bytes.fromhex(n.strip())[::-1] for n in gdb.execute(f'x/2xg ${register}', False, True).split('\t0x')[1:]])

# Lancer le binaires avec un mot de passe
def run_password(password: str) -> tuple[bytes, dict]:
	gdb.execute(f'r {password}')
	key = get_register('rax')
	encrypted = get_register('rcx')
	# Retourne le tuple (clé en mémoire, mapping partiel)
	return key, {encrypted[i]: password[i] for i in range(KEY_SIZE)}

# Utilise le mapping pour retrouver le bon mot de passe
def solve_key(key: bytes, mapping: dict) -> str:
	return ''.join([mapping[n] for n in key])

# Break avant la comparaison
gdb.execute('b memcmp@plt')

# Map contenant le couple {chiffré: caractère}
mapping = {}

# Coupe notre charset en bloc de 16 caractères
for i in range(0, len(CHARSET), KEY_SIZE):
	# Complète au cas où il n'y en a pas assez avec des 'a'
	password = CHARSET[i:i+KEY_SIZE].ljust(KEY_SIZE, 'a')

	key, partial_mapping = run_password(password)
	mapping.update(partial_mapping)

# Détermine le mot de passe
password = solve_key(key, mapping)

# Le print dans la console
print(password)

# Quit GDB
gdb.execute(f'quit')

On peut tester en lançant le binaire avec gdb et le script en option

$ gdb ./out/crackme_329455874d977e59dcb72eb58779ec5c.bin --command gdbscript.py
[ gdb écrit plein de trucs ]
7AAQUJa1vnDqaZRJ

Maintenant si on lance le binaire avec le mot de passe

$ ./out/crackme_329455874d977e59dcb72eb58779ec5c.bin 7AAQUJa1vnDqaZRJ        
GG!

Bingo ! Plus qu'à compléter notre fonction solve dans notre classe Binary et envoyer le résultat au serveur.


Résolution

Voici le script python au complet pour résoudre le challenge (ne pas oublier gdbscript.py)

solve.py
import socket
import os
from io import BytesIO
from subprocess import check_output
from zipfile import ZipFile


HOST = 'challenges.404ctf.fr'
PORT_BINARY = 31998
PORT_SOLVE = 31999

class Binary:
    FOLDER = 'out'
    
    @staticmethod
    def fetch() -> 'Binary':
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((HOST, PORT_BINARY))
        archive = b''
        while True:
            data = client.recv(4096)
            if len(data) == 0:
                break
            archive += data
        archive = ZipFile(BytesIO(archive))
        crackme = archive.read('crackme.bin')
        token = archive.read('token.txt').decode()
        return Binary(crackme, token)

    @staticmethod
    def open(filename):
        token = filename.split('_')[1][:-4]
        with open(os.path.join(Binary.FOLDER, filename), 'rb') as f:
            crackme = f.read()
        return Binary(crackme, token)

    def __init__(self, crackme: bytes, token: str) -> None:
        self.crackme = crackme
        self.token = token
        self.filename = f'crackme_{self.token}.bin'
        self.filepath = os.path.join(self.FOLDER, self.filename)
    
    def save(self):
        if not os.path.exists(self.FOLDER):
            os.mkdir(self.FOLDER)
        with open(self.filepath, 'wb') as f:
            f.write(self.crackme)
        os.system(f'chmod +x {self.filepath}')
    
    def solve(self) -> str:
        # Au cas où
        self.save()
        # Récupère la dernière ligne print, donc le mot de passe trouvé
        return check_output(['gdb', '--command', 'gdbscript.py', self.filepath]).splitlines()[-1].strip().decode()


def main() -> None:
    # Récupère le binaire sur le serveur
    binary = Binary.fetch()
    # Lance gdb avec le script 
    password = binary.solve()

    print(f'Token:    {binary.token}')
    print(f'Password: {password}')

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # Se connecte au serveur
    client.connect((HOST, PORT_SOLVE))
    client.recv(2048)
    # Envoi du token pour identifier le binaire sur lequel on travallait
    client.send(f'{binary.token}\n'.encode())
    client.recv(2048)
    # Envoi du mot de passe pour prouver notre résolution
    client.send(f'{password}\n'.encode())
    # Récupération du flag
    print(client.recv(2048).decode())


if __name__ == '__main__':
    main()
PrécédentBugdroid Fight [1/2]SuivantBugdroid Fight [2/2]

Dernière mise à jour il y a 1 an

Cet article vous a-t-il été utile ?

🚩
⚙️
⭐