Aperi’CTF 2019 - Alien crackme
Challenge details
Event | Challenge | Category | Points | Solves |
---|---|---|---|---|
Aperi’CTF 2019 | Alien crackme | Reverse | 250 | 1 |
Green aliens made a crackme just for you, try to find the flag …
Fichier :crackme.exe - md5sum: 77fe1f25e4a88f00a60f871997adae19
TL;DR
It was a character comparison with an “uu” encode. The script uses its own source code to compare characters, which makes debugging less easy.
Methodology
Strings
Le binaire est un exécutable prévu pour être lancé sur Windows cependant au vu des strings utilisée le binaire utilise cuda. CUDA est une technologie développé par Nvidia pour exécuter des calculs sur une carte graphiques.
On remarque ci-dessous plusieurs chaine intéressantes, on peut supposer:
- “Enter password :” l’utilisateur doit entrer un mot de passe.
- “encryption failed!”: il y’a probablement un algorithme de chiffrement à l’intérieur du programme.
- “The flag is %s\n”: le programme affiche le flag directement si le mot de passe est bon.
Main
La chaine “The flag is %s\n” est un bon point de départ pour afficher le flag. Comme on peut le voir sur la fonction ci-dessous la chaine de format est passé à la fonction sub_14000F400 qui n’est rien d’autre que la fonction printf.
Dst est passer en deuxième argument, on peut en déduire qu’il contient le flag. Cependant il est utilisé plus haut dans la fonction en argument de la fonction sub_14000FDB0 qui correspond à la fonction scanf.
Pour passer dans le morceau de code qui affiche le flag que l’on entre il faut satisfaire les conditions de la boucle loc_14000FD00. Elle compare deux tableaux var_A50 et var_930 avec l’instructions ucomisd :
UCOMISD – Unordered compare scalar double-precision floating-point values and set EFLAGS
Chaque éléments du tableau var_A50 doit être égal avec celui de var_930 au vue de l’instruction jnz, l’instruction jnp juste avant permet de s’assurer que l’opérande source n’est pas un NaN.
Nous avons aussi une indication sur la taille du tableau, la boucle s’arrête quand RDI = 0x118 (cmp rdi,0x118) , les tableaux sont constitué de 0x118/8 = 35 doubles.
Le tableau var_A50 (renommé cipher) est initialisé au début de la fonction, donc le tableau var_930 (renommé userinput) est probablement calculé à partir de l’entrée utilisateur.
Chiffrement
loc_14000FC30 (renommé encryption loop) semble correspondre au début de la boucle de chiffrement.
RSI pointe sur l’entrée utilisateur (+1), comme on peut le voir chaque caractère est converti en double avec l’instruction cvtdq2pd , la boucle ne traite que des blocs de 5 caractères (compter le nombre cvtdq2pd).
Chaque caractère converti en double est mis dans une structure pointée par R12.
Le cœur de la fonction de chiffrement semble être sub_14000F760, qui prend en arguments :
- RDX : pointeur vers une structure correspondant a l’entrée utilisateur
R8 : R8 pointe sur un buffer Memory qui est utilisé en sortie c’est probablement le résultat du chiffrement du bloc de 5 caractères.
RCX : c’est probablement une clef de chiffrement.
Regardons de plus près ce qui est pointé par RCX (provenant de RBX):
La fonction sub_14000F4C0 semble retourner la clef de chiffrement:
- RCX : pointe sur un tableau constant de doubles var_B70
- R8 : 5
- RDX : 5
Lorsque l’on décode le tableau var_B70 cela donne : 109.0, 97.0, 116.0, 114.0, 105.0, 120.0, 95.0 … qui est une suite de caractère ascii valide.
Ci-dessous le script pour extraire la clef complètement ( l contient la liste des offsets des doubles car la clef n’est pas stockée de manière continue dans le binaire)
import struct
key = ""
f = open("../files/crackme.exe","rb")
l = [0x10890,0x108E0,0x10920,0x108B0,0x10880,0x10910,0x108F0,0x108C0,0x10870,0x108D0,0x108A0,0x10900]
for pos in l:
f.seek(pos)
d1 = f.read(8)
d2 = f.read(8)
x1 = struct.unpack("d",d1)[0]
x2 = struct.unpack("d",d2)[0]
print(x1)
print(x2)
key+=chr(int(x1))+chr(int(x2))
f.seek(0x10858)
data = f.read(8)
last_c = chr(int(struct.unpack("d",data)[0]))
print(key + last_c)
Ce qui nous donne matrix_is_awesome_on_gpu! , la clef fait 25 caractères (5 x 5), si l’ont analyse plus en détails la fonction, elle alloue une structure de la forme.
struct _matrix
{
int nrows;
int ncols;
double data[nrows * ncols];
}
C’est un chiffrement à partir de matrice, a partir de mon moteur de recherche préféré on peut deviner le chiffrement utilisé :
Bonus
On peut désassembler le code machine de la fonction de multiplication de matrice sur GPU avec l’outil cuobjdump :
> cuobjdump.exe --dump-sass "D:\Projets\aperictf\reverse\gpu_crackme\files\crackme.exe"
Fatbin elf code:
================
arch = sm_35
code version = [1,7]
producer = cuda
host = windows
compile_size = 64bit
code for sm_35
Function : _Z33_8d081f4b2b766e92b4ab59bcf4a9b620PdS_S_i
.headerflags @"EF_CUDA_SM35 EF_CUDA_PTX_SM(EF_CUDA_SM35)"
/* 0x089c80a0108c10ac */
/*0008*/ MOV R1, c[0x0][0x44]; /* 0x64c03c00089c0006 */
/*0010*/ S2R R7, SR_TID.Y; /* 0x86400000111c001e */
/*0018*/ MOV32I R6, 0x8; /* 0x74000000041fc01a */
/*0020*/ MOV R8, c[0x0][0x158]; /* 0x64c03c002b1c0022 */
/*0028*/ S2R R0, SR_TID.X; /* 0x86400000109c0002 */
/*0030*/ IMAD R3, R7, c[0x0][0x28], R0; /* 0x51080000051c1c0e */
/*0038*/ ISETP.GE.AND P0, PT, R8, 0x1, PT; /* 0xb3681c00009c201d */
/* 0x08808010b810a0b0 */
/*0048*/ ISCADD R2.CC, R3, c[0x0][0x150], 0x3; /* 0x60c40c002a1c0c0a */
/*0050*/ IMAD.HI.X R3, R3, R6, c[0x0][0x154]; /* 0x931818002a9c0c0e */
/*0058*/ ST.E.64 [R2], RZ; /* 0xe5800000001c0bfc */
/*0060*/ @!P0 EXIT; /* 0x180000000020003c */
/*0068*/ LOP32I.AND R10, R8, 0x3; /* 0x20000000019c2028 */
/*0070*/ IMUL R7, R7, c[0x0][0x158]; /* 0x61c018002b1c1c1e */
/*0078*/ MOV R9, RZ; /* 0xe4c03c007f9c0026 */
/* 0x08b0acb0acb09810 */
/*0088*/ MOV R4, RZ; /* 0xe4c03c007f9c0012 */
/*0090*/ MOV R5, RZ; /* 0xe4c03c007f9c0016 */
/*0098*/ ISETP.NE.AND P0, PT, R10, RZ, PT; /* 0xdb581c007f9c281e */
/*00a0*/ @!P0 BRA 0x200; /* 0x12000000ac20003c */
/*00a8*/ ISETP.NE.AND P0, PT, R10, 0x1, PT; /* 0xb3581c00009c281d */
/*00b0*/ @!P0 BRA 0x1a0; /* 0x120000007420003c */
/*00b8*/ ISETP.NE.AND P0, PT, R10, 0x2, PT; /* 0xb3581c00011c281d */
/* 0x08909c108010b010 */
/*00c8*/ @P0 ISCADD R10.CC, R7, c[0x0][0x140], 0x3; /* 0x60c40c0028001c2a */
/*00d0*/ @P0 MOV32I R9, 0x1; /* 0x740000000083c026 */
/*00d8*/ @P0 IMAD.U32.U32.HI.X R11, R7, R6, c[0x0][0x144]; /* 0x9210180028801c2e */
/*00e0*/ IADD R17, -R9, RZ; /* 0xe09000007f9c2446 */
/*00e8*/ @P0 ISCADD R14.CC, R0, c[0x0][0x148], 0x3; /* 0x60c40c002900003a */
/*00f0*/ IADD R16, R7, R9; /* 0xe0800000049c1c42 */
/*00f8*/ @P0 LD.E.64 R10, [R10]; /* 0xc580000000002828 */
/* 0x088c909c8010a010 */
/*0108*/ @P0 IMAD.U32.U32.HI.X R15, R0, R6, c[0x0][0x14c]; /* 0x921018002980003e */
/*0110*/ IADD32I R9, R9, 0x1; /* 0x40000000009c2425 */
/*0118*/ @P0 LD.E.64 R12, [R14]; /* 0xc580000000003830 */
/*0120*/ LOP.AND R17, R17, c[0x0][0x28]; /* 0x62000000051c4446 */
/*0128*/ ISCADD R18.CC, R16, c[0x0][0x140], 0x3; /* 0x60c40c00281c404a */
/*0130*/ IADD R17, R17, R0; /* 0xe0800000001c4446 */
/*0138*/ IMAD.U32.U32.HI.X R19, R16, R6, c[0x0][0x144]; /* 0x92101800289c404e */
/* 0x088c10b810a4fcb0 */
/*0148*/ ISCADD R16.CC, R17, c[0x0][0x148], 0x3; /* 0x60c40c00291c4442 */
/*0150*/ IMAD.U32.U32.HI.X R17, R17, R6, c[0x0][0x14c]; /* 0x92101800299c4446 */
/*0158*/ @P0 DFMA R12, R12, R10, RZ; /* 0xdb83fc0005003032 */
/*0160*/ @P0 ST.E.64 [R2], R12; /* 0xe580000000000830 */
/*0168*/ @P0 MOV R4, R12; /* 0xe4c03c0006000012 */
/*0170*/ LD.E.64 R14, [R18]; /* 0xc5800000001c4838 */
/*0178*/ @P0 MOV R5, R13; /* 0xe4c03c0006800016 */
/* 0x08b010a010aca4fc */
/*0188*/ LD.E.64 R10, [R16]; /* 0xc5800000001c4028 */
/*0190*/ DFMA R4, R10, R14, R4; /* 0xdb801000071c2812 */
/*0198*/ ST.E.64 [R2], R4; /* 0xe5800000001c0810 */
/*01a0*/ IADD R10, R7, R9; /* 0xe0800000049c1c2a */
/*01a8*/ IMAD R11, R9, c[0x0][0x28], R0; /* 0x51080000051c242e */
/*01b0*/ ISCADD R14.CC, R10, c[0x0][0x140], 0x3; /* 0x60c40c00281c283a */
/*01b8*/ IADD32I R9, R9, 0x1; /* 0x40000000009c2425 */
/* 0x08aca4fca0b010a0 */
/*01c8*/ IMAD.U32.U32.HI.X R15, R10, R6, c[0x0][0x144]; /* 0x92101800289c283e */
/*01d0*/ ISCADD R10.CC, R11, c[0x0][0x148], 0x3; /* 0x60c40c00291c2c2a */
/*01d8*/ LD.E.64 R14, [R14]; /* 0xc5800000001c3838 */
/*01e0*/ IMAD.U32.U32.HI.X R11, R11, R6, c[0x0][0x14c]; /* 0x92101800299c2c2e */
/*01e8*/ LD.E.64 R10, [R10]; /* 0xc5800000001c2828 */
/*01f0*/ DFMA R4, R10, R14, R4; /* 0xdb801000071c2812 */
/*01f8*/ ST.E.64 [R2], R4; /* 0xe5800000001c0810 */
/* 0x0880b010a010b8b0 */
/*0208*/ ISETP.GE.U32.AND P0, PT, R8, 0x4, PT; /* 0xb3601c00021c201d */
/*0210*/ @!P0 EXIT; /* 0x180000000020003c */
/*0218*/ IADD R8, R7, R9; /* 0xe0800000049c1c22 */
/*0220*/ IMAD R11, R9, c[0x0][0x28], R0; /* 0x51080000051c242e */
/*0228*/ ISCADD R14.CC, R8, c[0x0][0x140], 0x3; /* 0x60c40c00281c203a */
/*0230*/ IADD32I R12, R9, 0x1; /* 0x40000000009c2431 */
/*0238*/ IMAD.U32.U32.HI.X R15, R8, R6, c[0x0][0x144]; /* 0x92101800289c203e */
/* 0x08b010a090109c10 */
/*0248*/ ISCADD R10.CC, R11, c[0x0][0x148], 0x3; /* 0x60c40c00291c2c2a */
/*0250*/ IADD R8, R7, R12; /* 0xe0800000061c1c22 */
/*0258*/ LD.E.64 R14, [R14]; /* 0xc5800000001c3838 */
/*0260*/ IMAD R13, R12, c[0x0][0x28], R0; /* 0x51080000051c3036 */
/*0268*/ IMAD.U32.U32.HI.X R11, R11, R6, c[0x0][0x14c]; /* 0x92101800299c2c2e */
/*0270*/ LD.E.64 R10, [R10]; /* 0xc5800000001c2828 */
/*0278*/ ISCADD R16.CC, R8, c[0x0][0x140], 0x3; /* 0x60c40c00281c2042 */
/* 0x08b810a4fcb08010 */
/*0288*/ IMAD.U32.U32.HI.X R17, R8, R6, c[0x0][0x144]; /* 0x92101800289c2046 */
/*0290*/ IADD32I R19, R9, 0x2; /* 0x40000000011c244d */
/*0298*/ ISCADD R12.CC, R13, c[0x0][0x148], 0x3; /* 0x60c40c00291c3432 */
/*02a0*/ IMAD.U32.U32.HI.X R13, R13, R6, c[0x0][0x14c]; /* 0x92101800299c3436 */
/*02a8*/ DFMA R4, R10, R14, R4; /* 0xdb801000071c2812 */
/*02b0*/ ST.E.64 [R2], R4; /* 0xe5800000001c0810 */
/*02b8*/ IADD R8, R7, R19; /* 0xe0800000099c1c22 */
/* 0x08b08010b8108c10 */
/*02c8*/ LD.E.64 R14, [R16]; /* 0xc5800000001c4038 */
/*02d0*/ ISCADD R20.CC, R8, c[0x0][0x140], 0x3; /* 0x60c40c00281c2052 */
/*02d8*/ LD.E.64 R10, [R12]; /* 0xc5800000001c3028 */
/*02e0*/ IMAD R19, R19, c[0x0][0x28], R0; /* 0x51080000051c4c4e */
/*02e8*/ IMAD.U32.U32.HI.X R21, R8, R6, c[0x0][0x144]; /* 0x92101800289c2056 */
/*02f0*/ IADD32I R12, R9, 0x3; /* 0x40000000019c2431 */
/*02f8*/ ISCADD R18.CC, R19, c[0x0][0x148], 0x3; /* 0x60c40c00291c4c4a */
/* 0x08808c10b810a4fc */
/*0308*/ IMAD.U32.U32.HI.X R19, R19, R6, c[0x0][0x14c]; /* 0x92101800299c4c4e */
/*0310*/ DFMA R14, R10, R14, R4; /* 0xdb801000071c283a */
/*0318*/ ST.E.64 [R2], R14; /* 0xe5800000001c0838 */
/*0320*/ IADD R8, R7, R12; /* 0xe0800000061c1c22 */
/*0328*/ LD.E.64 R10, [R20]; /* 0xc5800000001c5028 */
/*0330*/ ISCADD R22.CC, R8, c[0x0][0x140], 0x3; /* 0x60c40c00281c205a */
/*0338*/ LD.E.64 R4, [R18]; /* 0xc5800000001c4810 */
/* 0x08b8a4fcb0809c10 */
/*0348*/ IMAD R13, R12, c[0x0][0x28], R0; /* 0x51080000051c3036 */
/*0350*/ IADD32I R9, R9, 0x4; /* 0x40000000021c2425 */
/*0358*/ IMAD.U32.U32.HI.X R23, R8, R6, c[0x0][0x144]; /* 0x92101800289c205e */
/*0360*/ ISCADD R16.CC, R13, c[0x0][0x148], 0x3; /* 0x60c40c00291c3442 */
/*0368*/ IMAD.U32.U32.HI.X R17, R13, R6, c[0x0][0x14c]; /* 0x92101800299c3446 */
/*0370*/ DFMA R12, R4, R10, R14; /* 0xdb803800051c1032 */
/*0378*/ ST.E.64 [R2], R12; /* 0xe5800000001c0830 */
/* 0x0880b810a4fc8c10 */
/*0388*/ LD.E.64 R14, [R22]; /* 0xc5800000001c5838 */
/*0390*/ ISETP.GE.AND P0, PT, R9, c[0x0][0x158], PT; /* 0x5b681c002b1c241e */
/*0398*/ LD.E.64 R10, [R16]; /* 0xc5800000001c4028 */
/*03a0*/ DFMA R4, R10, R14, R12; /* 0xdb803000071c2812 */
/*03a8*/ ST.E.64 [R2], R4; /* 0xe5800000001c0810 */
/*03b0*/ @!P0 BRA 0x218; /* 0x12007fff3020003c */
/*03b8*/ MOV RZ, RZ; /* 0xe4c03c007f9c03fe */
/* 0x08000000000000b8 */
/*03c8*/ EXIT; /* 0x18000000001c003c */
/*03d0*/ BRA 0x3d0; /* 0x12007ffffc1c003c */
/*03d8*/ NOP; /* 0x85800000001c3c02 */
/*03e0*/ NOP; /* 0x85800000001c3c02 */
/*03e8*/ NOP; /* 0x85800000001c3c02 */
/*03f0*/ NOP; /* 0x85800000001c3c02 */
/*03f8*/ NOP; /* 0x85800000001c3c02 */
.......................................................
Fatbin ptx code:
================
arch = sm_35
code version = [6,3]
producer = cuda
host = windows
compile_size = 64bit
compressed
ptxasOptions =
Déchiffrement du flag
Pour déchiffrer un message avec le chiffre de Hill il suffit de multiplier le message par l’inverse de la matrice de chiffrement.
import struct
import numpy as np
import math
key = ""
cipher = []
f = open("../files/crackme.exe","rb")
# extract key
l = [0x10890,0x108E0,0x10920,0x108B0,0x10880,0x10910,0x108F0,0x108C0,0x10870,0x108D0,0x108A0,0x10900]
for pos in l:
f.seek(pos)
d1 = f.read(8)
d2 = f.read(8)
x1 = struct.unpack("d",d1)[0]
x2 = struct.unpack("d",d2)[0]
key+=chr(int(x1))+chr(int(x2))
f.seek(0x10858)
data = f.read(8)
last_c = chr(int(struct.unpack("d",data)[0]))
key = key + last_c
l = [
0x10950, # +00h
0x10960, # +10h
0x109A0, # +20h
0x109B0, # +30h
0x10940, # +40h
0x10A10, # +50h
0x109E0, # +60h
0x10970, # +70h
0x10980, # +80h
0x10930, # +90h
0x10A00, # +A0h
0x109F0, # +B0h
0x10A20, # +C0h
0x10A30, # +D0h
0x10990, # +E0h
0x109D0, # +F0h
0x109C0, # +100h
0x10860, # +110h
]
for pos in l:
f.seek(pos)
d1 = f.read(8)
d2 = f.read(8)
x1 = struct.unpack("d",d1)[0]
x2 = struct.unpack("d",d2)[0]
cipher.append(x1)
cipher.append(x2)
flag=""
m = np.matrix(np.array(np.frombuffer(key,dtype=np.uint8),dtype=np.float32).reshape(5,5))
for i in range(0,35,5):
message = np.matrix(np.array(cipher[i:i+5],dtype=np.float32).reshape(5,1))
res = m.I.dot(message)
for i in range(0,5):
flag+=chr(int(round(res[i])))
print(flag)
Flag
flag : APRK{Did_you_see_GPU_instructions?}
Tomtombinary