Election
Um salve pro @paypain por este crackme!
A minha solução para este desafio não é nada complexa ou inovadora, basicamente a idéia é fazer o hijack de alguma função que escreva no stdout.
O arquivo é um ELF executável, dinamicamente linkado, não stripado e com informações de debug. Isso nos ajuda bastante.
Executando é possível perceber que ele imprime um ranking contendo valores randômicos.
$ ./election
Welcome to election of United Crackme of One .
Counting the votes . . .
Total votes: 88
The final result is:
Bob -> 1 Place with 48 votes
Sar -> 2 Place with 38 votes
Poe -> 3 Place with 2 votes
Podemos hookar as funções que ele usa da libc.so.6.
$ ldd ./election
linux-vdso.so.1 (0x00007ffe9391b000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fd855f0c000)
/lib/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fd85610e000)
$ objdump -T ./election
election: formato de ficheiro elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getenv
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 puts
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.17 clock_gettime
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 printf
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 srand
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 ptrace
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 rand
Porém precisamos saber quando elas são chamadas e o que fazem, ou seja, precisamos debugar.
$ ltrace ./election
getenv("LD_PRELOAD") = nil
ptrace(0, 0, 1, 0) = -1
puts("[Anti-debug ALERT] Corruption ! "...[Anti-debug ALERT] Corruption ! S4r is trying to manipulate the election !!
) = 76
+++ exited (status 1) +++
Como podemos ver, existem proteções contra modificações em runtime.
função getenv está verificando se a variável de ambiente “LD_PRELOAD” possui algum valor. Isto impede o hook, porém é bem easy de bypassar.
char *
// getenv original: https://code.woboq.org/userspace/glibc/stdlib/getenv.c.html
getenv(const char *name)
{
return NULL;
}
Sabendo isto, é só fazer o hook de algumas funçõezinhas… e será possível fazer o Sar sempre ganhar.
$ LD_PRELOAD=./libmeeseeks.so ./election
Welcome to election of United Crackme of One .
Counting the votes . . .
Total votes: 190
The final result is:
Bob -> 2 Place with 39 votes
Sar -> 1 Place with 113 votes
Poe -> 3 Place with 38 votes
Codígo-fonte da solução:
/// filename: meeseeks.c
/// compile: gcc -fpic -ldl -shared -o libmeeseeks.so meeseeks.c
/// usage: $ LD_PRELOAD=./libmeeseeks.so ./election
#define _GNU_SOURCE
#include <dlfcn.h>
#include <gnu/lib-names.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <time.h>
void* handle = NULL;
int (*orig_printf)(const char*, ...) = NULL;
unsigned printf_count = 0, sar = 0, bob = 0, poe = 0;
char*
getenv(const char* name)
{
return NULL;
}
int
printf(const char* str, ...)
{
if (orig_printf == NULL) {
if (handle == NULL)
handle = dlopen(LIBC_SO, RTLD_LAZY);
orig_printf = dlsym(handle, "printf");
}
if (printf_count == 0) {
srand(time(NULL));
sar = 55 + rand() % 80;
bob = rand() % 50;
poe = rand() % 40;
orig_printf("Total votes: %d\n", sar + bob + poe);
++printf_count;
} else {
unsigned bob_pos, poe_pos;
if (bob > poe) {
bob_pos = 2;
poe_pos = 3;
} else {
poe_pos = 2;
bob_pos = 3;
}
orig_printf("The final result is:\n"
"\tBob -> %d Place with %d votes\n"
"\tSar -> 1 Place with %d votes\n"
"\tPoe -> %d Place with %d votes\n",
bob_pos,
bob,
sar,
poe_pos,
poe);
}
return 0;
}