Escrito em 02/09/2020, por Growlnx.

Election

Link para o desafio

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.

$ file ./election
election: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=11eeb817ee59b456bb7e9ecd3d93c475cd048056, with debug_info, not stripped

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;
}