[LeHack] - Singularity
·3 mins·
0
·
0
·
CTF
Crypto
LeHack
Sommaire
LeHack2025 - Cet article fait partie d'une série.
Partie 3: Cet article
Description #
Le script Python suivant nous est donné, celui-ci permet de retrouver le flag :
import base64
def banner():
BANNER = """CgogICAgICAgICAgICAgICAgICAuYW5kQUhIQWJubi4KICAgICAgICAgICAgICAgLmFBSEhIQUFV
VUFBSEhIQW4uCiAgICAgICAgICAgICAgZEhQXn4iICAgICAgICAifl5USGIuCiAgICAgICAgLiAg
IC5BSEYgICAgICAgICAgICAgICAgWUhBLiAgIC4KICAgICAgICB8ICAuQUhIYi4gICAgICAgICAg
ICAgIC5kSEhBLiAgfAogICAgICAgIHwgIEhIQVVBQUhBYm4gICAgICBhZEFIQUFVQUhBICB8CiAg
ICAgICAgSSAgSEZ+Il9fX19fICAgICAgICBfX19fIF1ISEggIEkgICAgCiAgICAgICBISEkgSEFQ
SyIifl5ZVUhiICBkQUhISEhISEhISEggSUhICiAgICAgICBISEkgSEhIRCZndDsgLmFuZEhIICBI
SFVVUF5+WUhISEggSUhIICAgICAgIFdlbGNvbWUgYmFjayB0byB0aGUgc2luZ3VsYXJpdHkgIQog
ICAgICAgWVVJIF1ISFAgICAgICJ+WSAgUH4iICAgICBUSEhbIElVUAogICAgICAgICIgIGBISyAg
ICAgICAgICAgICAgICAgICBdSEgnICAiICAgICAgICAgICAgICAgQ2FuIHlvdSBmaW5kIG15IHBh
c3N3b3JkID8KICAgICAgICAgICAgVEhBbi4gIC5kLmFBQW4uYi4gIC5kSEhQCiAgICAgICAgICAg
IF1ISEhIQUFVUCIgfn4gIllVQUFISEhIWwogICAgICAgICAgICBgSEhQXn4iICAuYW5ubi4gICJ+
XllISCcKICAgICAgICAgICAgIFlIYiAgICB+IiAiIiAifiAgICBkSEYKICAgICAgICAgICAgICAi
WUFiLi5hYmRISGJuZGJuZEFQIgogICAgICAgICAgICAgICBUSEhBQWIuICAuYWRBSEhGCiAgICAg
ICAgICAgICAgICAiVUhISEhISEhISEhVIgogICAgICAgICAgICAgICAgICBdSEhVVUhISEhISFsK
ICAgICAgICAgICAgICAgIC5hZEhIYiAiSEhISEhibi4KICAgICAgICAgLi5hbmRBQUhISEhISGIu
QUhISEhISEhBQWJubi4uCiAgICAubmRBQUhISEhISFVVSEhISEhISEhISFVQXn4ifl5ZVUhISEFB
Ym4uCiAgICAgICJ+XllVSEhQIiAgICJ+XllVSEhVUCIgICAgICAgICJeWVVQXiIKICAgICAgICAg
ICAiIiAgICAgICAgICJ+fiIK"""
print(base64.b64decode(BANNER).decode())
def check_password(password):
singularity = lambda t: (ord(t[1])+t[0])^0x42 == bytes.fromhex(
base64.b64decode('MDExMjMyN2IzNDdiMzgxODI1MjA3OGMyMjkxMTNmYzYzYzM3MzMzZDNiMTljMDE1MWQ=').decode()
)[t[0]]
return all(map(singularity, enumerate(password)))
def main():
banner()
password = input("Enter the password> ")
if check_password(password):
print("You can validate with leHACK{{{}}}".format(password))
else:
print("Error: Wrong password")
if __name__ == '__main__':
main()
Analyse #
- La première partie du script affiche un ASCII art (bien que très joli, celui-ci est inutile pour la résolution du challenge)
- Ensuite le script nous demande d’entrer un mot de passe qui sera donné à la fonction
check_password
- Si le mot de passe n’est celui attendu, le script renvoi le message ‘Error: Wrong password’
- Si le mot de passe est valide, le script renvoi le message ‘You can validate with leHACK{}’
Notre but est donc de retrouver le mot de passe vérifié par la fonction
check_password
car celui-ci est le flag !
Analysons plus en profondeur la fonction check_password
:
- C’est une fonction anonyme (représenté par le mot
lambda
) prenant un tuple :- t[0] → l’index du caractère dans le mot de passe
- t[1] → le caractère à cette position
- Cette fonction réalise une opération mathématique pour chaque caractère du mot de passe renseigné :
- Elle récupère le code ASCII du caractère (
ord(t[1])
) - Additionne sa position (
+ t[0]
) - Et XOR le résultat avec 0x42 (= 66 en décimal)
- Elle récupère le code ASCII du caractère (
- Enfin cette fonction va comparer le résultat obtenu avec une chaîne obfusquée
- Cette chaîne est encodé en base64, une fois décodée elle donne un résultat en hexadécimal, pour enfin être transformée en liste d’octets
- La liste d’octets donne :
[1, 18, 50, 123, 52, 123, 56, 24, 37, 32, 120, 194, 41, 17, 63, 198, 60, 55, 51, 61, 59, 25, 192, 21, 29]
Résolution #
Le mot de passe attendu se trouve donc dans cette chaîne, afin de le retrouver on peut prendre la logique de la fonction :
(ord(char) + i) ^ 0x42 == valeur_attendue[i]
char
est le caractère à la position i dans le mot de passe, valeur_attendue
est la liste d’octetsEt l’inverser :
ord(char) = (valeur_attendue[i] ^ 0x42) - i
Avec un script Python qui effectue cette action pour tous les octets de la liste et qui affiche le résultat finale, on obtient :
import base64
# Étape 1 : Décoder la chaîne pour obtenir la liste des octets
encoded = 'MDExMjMyN2IzNDdiMzgxODI1MjA3OGMyMjkxMTNmYzYzYzM3MzMzZDNiMTljMDE1MWQ='
hex_string = base64.b64decode(encoded).decode()
target_bytes = bytes.fromhex(hex_string)
# Étape 2 : Inverser la formule pour retrouver le mot de passe
# On a : (ord(c) + i) ^ 0x42 == target_bytes[i]
# Qui devient : ord(c) = (target_bytes[i] ^ 0x42) - i
password_chars = []
for i, val in enumerate(target_bytes):
c = (val ^ 0x42) - i
password_chars.append(chr(c))
password = ''.join(password_chars)
# Etape 3 : afficher le mot de passe
print(password)
On obtient le résultat : COn6r4tS_Y0u_Found_leFl@G
🚩 Flag : leHACK{COn6r4tS_Y0u_Found_leFl@G}
LeHack2025 - Cet article fait partie d'une série.
Partie 3: Cet article