Estamos ante un ELF un poco más interesante que los vistos anteriormente. Básicamente porque es divertido y fácil encontrar la solución en el decompilado y quizá por evocar recuerdos de tiempos pretéritos.
ELF Decompilado
/* This file was generated by the Hex-Rays decompiler version 8.4.0.240320. Copyright (c) 2007-2021 Hex-Rays <info@hex-rays.com> Detected compiler: GNU C++ */ #include <defs.h> //------------------------------------------------------------------------- // Function declarations __int64 (**init_proc())(void); __int64 sub_400650(); // weak // int printf(const char *format, ...); // int puts(const char *s); // void __noreturn exit(int status); // size_t strlen(const char *s); // __int64 __fastcall MD5(_QWORD, _QWORD, _QWORD); weak // int sprintf(char *s, const char *format, ...); // __int64 ptrace(enum __ptrace_request request, ...); // __int64 strtol(const char *nptr, char **endptr, int base); // __int64 __isoc99_scanf(const char *, ...); weak // int memcmp(const void *s1, const void *s2, size_t n); void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void)); void *sub_400730(); __int64 sub_400760(); void *sub_4007A0(); __int64 sub_4007D0(); void fini(void); // idb void term_proc(); // int __fastcall _libc_start_main(int (__fastcall *main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void *stack_end); // __int64 _gmon_start__(void); weak //------------------------------------------------------------------------- // Data declarations _UNKNOWN main; _UNKNOWN init; __int64 (__fastcall *funcs_400E29)() = &sub_4007D0; // weak __int64 (__fastcall *off_601DF8)() = &sub_4007A0; // weak __int64 (*qword_602010)(void) = NULL; // weak char *off_602080 = "FLAG-%s\n"; // idb char a7yq2hryrn5yJga[16] = "7Yq2hrYRn5Y`jga"; // weak const char aO6uH[] = "(O6U,H\""; // idb _UNKNOWN unk_6020B8; // weak _UNKNOWN unk_6020C8; // weak char byte_6020E0; // weak char s1; // idb char byte_602110[]; // weak char byte_602120[33]; // weak char byte_602141[7]; // idb __int64 qword_602148; // weak __int64 qword_602150; // weak __int64 qword_602158; // weak __int64 qword_602160; // weak __int64 qword_602178; // weak //----- (0000000000400630) ---------------------------------------------------- __int64 (**init_proc())(void) { __int64 (**result)(void); // rax result = &_gmon_start__; if ( &_gmon_start__ ) return (__int64 (**)(void))_gmon_start__(); return result; } // 6021D8: using guessed type __int64 _gmon_start__(void); //----- (0000000000400650) ---------------------------------------------------- __int64 sub_400650() { return qword_602010(); } // 400650: using guessed type __int64 sub_400650(); // 602010: using guessed type __int64 (*qword_602010)(void); //----- (0000000000400700) ---------------------------------------------------- // positive sp value has been detected, the output may be wrong! void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void)) { __int64 v3; // rax int v4; // esi __int64 v5; // [rsp-8h] [rbp-8h] BYREF char *retaddr; // [rsp+0h] [rbp+0h] BYREF v4 = v5; v5 = v3; _libc_start_main((int (__fastcall *)(int, char **, char **))main, v4, &retaddr, (void (*)(void))init, fini, a3, &v5); __halt(); } // 400706: positive sp value 8 has been found // 40070D: variable 'v3' is possibly undefined //----- (0000000000400730) ---------------------------------------------------- void *sub_400730() { return &unk_6020C8; } //----- (0000000000400760) ---------------------------------------------------- __int64 sub_400760() { return 0LL; } //----- (00000000004007A0) ---------------------------------------------------- void *sub_4007A0() { void *result; // rax if ( !byte_6020E0 ) { result = sub_400730(); byte_6020E0 = 1; } return result; } // 6020E0: using guessed type char byte_6020E0; //----- (00000000004007D0) ---------------------------------------------------- __int64 sub_4007D0() { return sub_400760(); } //----- (00000000004007D7) ---------------------------------------------------- __int64 __fastcall main(int a1, char **a2, char **a3) { size_t v3; // rax size_t v4; // rax int i; // [rsp+1Ch] [rbp-24h] int n; // [rsp+20h] [rbp-20h] int m; // [rsp+24h] [rbp-1Ch] int k; // [rsp+28h] [rbp-18h] int j; // [rsp+2Ch] [rbp-14h] if ( ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL) == -1 ) goto LABEL_2; if ( a1 > 4 ) { qword_602148 = strtol(a2[1], 0LL, 10); if ( qword_602148 ) { qword_602150 = strtol(a2[2], 0LL, 10); if ( qword_602150 ) { qword_602158 = strtol(a2[3], 0LL, 10); if ( qword_602158 ) { qword_602160 = strtol(a2[4], 0LL, 10); if ( qword_602160 ) { if ( -24 * qword_602148 - 18 * qword_602150 - 15 * qword_602158 - 12 * qword_602160 == -18393 && 9 * qword_602158 + 18 * (qword_602150 + qword_602148) - 9 * qword_602160 == 4419 && 4 * qword_602158 + 16 * qword_602148 + 12 * qword_602150 + 2 * qword_602160 == 7300 && -6 * (qword_602150 + qword_602148) - 3 * qword_602158 - 11 * qword_602160 == -8613 ) { qword_602178 = qword_602158 + qword_602150 * qword_602148 - qword_602160; sprintf(byte_602141, "%06x", qword_602178); v4 = strlen(byte_602141); MD5(byte_602141, v4, byte_602110); for ( i = 0; i <= 15; ++i ) sprintf(&byte_602120[2 * i], "%02x", (unsigned __int8)byte_602110[i]); printf(off_602080, byte_602120); exit(0); } } } } } LABEL_2: printf("password : "); __isoc99_scanf("%s", &s1); if ( strlen(&s1) > 0x10 ) { puts("the password must be less than 16 character"); exit(1); } for ( j = 0; j < strlen(&s1); ++j ) *(&s1 + j) ^= 6u; if ( !strcmp(&s1, a7yq2hryrn5yJga) ) { v3 = strlen(&s1); MD5(&s1, v3, byte_602110); for ( k = 0; k <= 15; ++k ) sprintf(&byte_602120[2 * k], "%02x", (unsigned __int8)byte_602110[k]); printf(off_602080, byte_602120); exit(0); } puts("bad password!"); exit(0); } printf("password : "); __isoc99_scanf("%s", &s1); if ( strlen(&s1) > 0x10 ) { puts("the password must be less than 16 character"); exit(1); } for ( m = 0; m < strlen(&s1); ++m ) { *(&s1 + m) ^= 2u; ++*(&s1 + m); *(&s1 + m) = ~*(&s1 + m); } if ( !memcmp(&s1, &unk_6020B8, 9uLL) ) { for ( n = 0; n < strlen(aO6uH); n += 2 ) { aO6uH[n] ^= 0x45u; aO6uH[n + 1] ^= 0x26u; } puts(aO6uH); } else { puts("bad password!"); } return 0LL; } // 4006A0: using guessed type __int64 __fastcall MD5(_QWORD, _QWORD, _QWORD); // 4006E0: using guessed type __int64 __isoc99_scanf(const char *, ...); // 602148: using guessed type __int64 qword_602148; // 602150: using guessed type __int64 qword_602150; // 602158: using guessed type __int64 qword_602158; // 602160: using guessed type __int64 qword_602160; // 602178: using guessed type __int64 qword_602178; //----- (0000000000400DE0) ---------------------------------------------------- void __fastcall init(unsigned int a1, __int64 a2, __int64 a3) { signed __int64 v3; // rbp __int64 i; // rbx v3 = &off_601DF8 - &funcs_400E29; init_proc(); if ( v3 ) { for ( i = 0LL; i != v3; ++i ) (*(&funcs_400E29 + i))(); } } // 601DF0: using guessed type __int64 (__fastcall *funcs_400E29)(); // 601DF8: using guessed type __int64 (__fastcall *off_601DF8)(); //----- (0000000000400E54) ---------------------------------------------------- void term_proc() { ; } // nfuncs=33 queued=10 decompiled=10 lumina nreq=0 worse=0 better=0 // ALL OK, 10 function(s) have been successfully decompiled
Análisis estático
Anti-debug
Si la función ptrace retorna -1, significa que el programa está siendo depurado y redirige a LABEL_2.
if (ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL) == -1) { goto LABEL_2; }
Cálculos y validaciones
El programa espera al menos 5 argumentos (nombre del programa y cuatro números enteros). Si se proporcionan los cuatro números enteros, se realizan los siguientes cálculos:
if (-24 * qword_602148 - 18 * qword_602150 - 15 * qword_602158 - 12 * qword_602160 == -18393 && 9 * qword_602158 + 18 * (qword_602150 + qword_602148) - 9 * qword_602160 == 4419 && 4 * qword_602158 + 16 * qword_602148 + 12 * qword_602150 + 2 * qword_602160 == 7300 && -6 * (qword_602150 + qword_602148) - 3 * qword_602158 - 11 * qword_602160 == -8613)
Esto es un sistema de ecuaciones lineales mondo y lirondo que debe ser resuelto para encontrar los valores correctos de qword_602148, qword_602150, qword_602158 y qword_602160. Una vez resuelto el sistema de ecuaciones se realiza la operación:
qword_602178 = qword_602158 + qword_602150 * qword_602148 - qword_602160;
A continuación se pasa el resultado de la variable qword_602178 a hexadecimal y se genera su hash MD5.
Solución en Python
Lo más rápido en esta ocasión es usar Python, pero esto se puede resolver hasta con lápiz y papel 😉
from sympy import symbols, Eq, solve import hashlib # Definir las variables A, B, C, D = symbols('A B C D') # Definir las ecuaciones eq1 = Eq(-24*A - 18*B - 15*C - 12*D, -18393) eq2 = Eq(9*C + 18*(A + B) - 9*D, 4419) eq3 = Eq(4*C + 16*A + 12*B + 2*D, 7300) eq4 = Eq(-6*(A + B) - 3*C - 11*D, -8613) # Resolver el sistema de ecuaciones solution = solve((eq1, eq2, eq3, eq4), (A, B, C, D)) # Verificar si se encontró una solución if solution: print("Solución encontrada:") print(solution) # Obtener los valores de A, B, C y D A_val = solution[A] B_val = solution[B] C_val = solution[C] D_val = solution[D] # Mostrar los valores encontrados print(f"A = {A_val}") print(f"B = {B_val}") print(f"C = {C_val}") print(f"D = {D_val}") # Calcular qword_602178 qword_602178 = C_val + B_val * A_val - D_val qword_602178 = int(qword_602178) # Convertir a entero de Python print(f"qword_602178 = {qword_602178}") # Convertir qword_602178 a una cadena en formato hexadecimal byte_602141 = f"{qword_602178:06x}" print(f"byte_602141 (hex) = {byte_602141}") # Calcular el MD5 de la cadena md5_hash = hashlib.md5(byte_602141.encode()).hexdigest() print(f"MD5 hash = {md5_hash}") # Generar la flag flag = f"FLAG-{md5_hash}" print(f"Flag = {flag}") else: print("No se encontró una solución.")
Al ejecutar el script veremos algo como esto:
Solución encontrada: {A: 227, B: 115, C: 317, D: 510} A = 227 B = 115 C = 317 D = 510 qword_602178 = 25912 byte_602141 (hex) = 006538 MD5 hash = 21a84f2c7c7fd432edf1686215db.... Flag = FLAG-21a84f2c7c7fd432edf1686215db....