Reverse Eng

Series: Assembly

  • Reading time : “8 min”

The Digital Storm Chronicles

Episode 1: The Binary Tempest

A storm rages outside as two security experts face their first challenge

Scene 1: The Two Old Cracker Friends

Thunder crashes outside a dimly lit apartment in C City

Monaquimbamba: “Hey, how are you?”

Howard: “Thanks for coming! I’m having trouble with this binary that a winter phenomenon sent to my bitcoin wallet.”

./bitcoinnewallet
arm-binfmt-P: Could not open '/lib/ld-linux.so.3': No such file or directory

Howard: “Shit, let me check the processor architecture…”

uname -a
Linux Howard 6.8.0-48-generic #48~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Oct  7 11:24:13 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

Monaquimbamba: “Looks like we got a problem. You have an X86 CPU but the binary was changed to ARM architecture. Wait… I remember something about QEMU emulator!”

Scene 2: The Debug Dance

Lightning flashes as Monaquimbamba sets up two terminals

Terminal 1:

gdb-multiarch -q -nx
(gdb) file bitcoinnewallet
Reading symbols from bitcoinnewallet...
(No debugging symbols found in bitcoinnewallet)
(gdb) target remote:8989
Remote debugging using :8989
warning: remote target does not support file transfer, attempting to access files from local filesystem.
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
0x3ffdde08 in ?? ()
(gdb) c
Continuing.
[Inferior 1 (process 1) exited with code 05]

Terminal 2:

qemu-arm -L /usr/arm-linux-gnueabi/ -g 8989 ./bitcoinnewallet
Please input password

Scene 3: The Ghidra Analysis

Rain pounds against the windows as they dive into the decompiled code


void FUN_00008470(int param_1,int param_2)

{
  size_t sVar1;
  byte *__s;
  int __status;
  int local_14;

  if (param_1 != 2) {
    puts("Please input password");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  __s = *(byte **)(param_2 + 4);
  printf("Checking %s for password...\n",__s);
  sVar1 = strlen((char *)__s);
  if (sVar1 != 6) {
    puts("Loser...");
                    /* WARNING: Subroutine does not return */
    exit(sVar1);
  }
  sVar1 = strlen((char *)__s);
  local_14 = -sVar1 + 6;
  if (*__s != __s[5]) {
    local_14 = -sVar1 + 7;
  }
  if (*__s + 1 != (uint)__s[1]) {
    local_14 = local_14 + 1;
  }
  if (__s[3] + 1 != (uint)*__s) {
    local_14 = local_14 + 1;
  }
  if (__s[2] + 4 != (uint)__s[5]) {
    local_14 = local_14 + 1;
  }
  if (__s[4] + 2 != (uint)__s[2]) {
    local_14 = local_14 + 1;
  }
  __status = local_14 + (__s[3] ^ 0x72) + (uint)__s[6];
  if (__status == 0) {
    puts("Success, you rocks!");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("Loser...");
                    /* WARNING: Subroutine does not return */
  exit(__status);
}



Scene 4: The C Translation

Howard: “Let me clean this up in C programming language…”


#include <stdio.h>
#include <string.h>




void main(int argc,char **argv){
  size_t len_passwd;
  char *input_passwd;
  int test;
  int local_var;

  if (argc != 2) {
    puts("Please input password");
    /* WARNING: Subroutine does not return */
    exit(1);
  }
  input_passwd = argv[1];
  printf("Checking %s for password...\n",input_passwd);
  len_passwd = strlen((char *)input_passwd);
  if (len_passwd != 6) {
    puts("Loser...");
     /* WARNING: Subroutine does not return */
    exit(len_passwd);
  }


  len_passwd = strlen((char *)input_passwd);
  local_var = -len_passwd + 6;               // local_var =0

  if (*input_passwd != input_passwd[5]) {   // pos[0] != pos[5]
    local_var = -len_passwd + 7;            // local_var =1
  }
  if (*input_passwd + 1 != input_passwd[1]) {  // pos[1] != pos[1] ????
    local_var = local_var + 1;                 // local_var =2
  }
  if (input_passwd[3] + 1 != *input_passwd) {  // pos[4] != pos[0]
    local_var = local_var + 1;                 // local_var =3
  }
  if (input_passwd[2] + 4 != input_passwd[5]) {  // pos[6] != pos[5]
    local_var = local_var + 1;                   // local_var =4
  }
  if (input_passwd[4] + 2 != input_passwd[2]) {  // pos[6] != pos[2]
    local_var = local_var + 1;                   // // local_var =5
  }

  test = local_var + (input_passwd[3] ^ 'r') + input_passwd[6];  // pos[0] != pos[5]

  if (test == 0) {
    puts("Success, you rocks!");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("Loser...");
                    /* WARNING: Subroutine does not return */
  exit(test);
}

Monaquimbamba: “Let me help break down how to exploit this program”

First, let’s understand how we can bypass the constraints:

We need a 6-character password that will pass the initial length check But we also need to access the 7th character (index 6) which is beyond our password length

Let’s solve the password constraints we know:

  • input_passwd[0] = input_passwd[5] (first and last char must be same)
  • input_passwd[0] = input_passwd[3] + 1
  • input_passwd[1] = input_passwd[0] + 1
  • input_passwd[5] = input_passwd[2] + 4
  • input_passwd[2] = input_passwd[4] + 2

The final test equation is: cCopytest = 5 + (input_passwd[3] ^ ‘r’) + input_passwd[6] = 0

Exploitation approach:

We can craft a 6-character password that satisfies the character relationships Then, by adding a 7th character (which technically shouldn’t be allowed), we can control input_passwd[6] The program will first check length (6 chars) but then still access the 7th byte in memory

Let’s solve this:

  • Let’s say input_passwd[3] = ‘r’ so the XOR equals 0
  • Then input_passwd[0] = ’s' (because [0] = [3] + 1)
  • input_passwd[1] = ’t' (because [1] = [0] + 1)
  • Let’s make input_passwd[4] = ‘p’
  • Then input_passwd[2] = ‘r’ (because [2] = [4] + 2)
  • input_passwd[5] = ‘v’ (because [5] = [2] + 4)
  • Finally input_passwd[6] = -5 (to make final equation = 0)

Therefore, a working exploit would be:

Password: “strprv” (6 characters) Followed by a byte with value -5 (or 251 in unsigned)

Howard: You can try this with something like: strprv\xFB

Scene 5: The Storm’s Secret

Thunder crashes as they attempt their solution

qemu-arm -L /usr/arm-linux-gnueabi/ -g 8989 ./exo5 "strprv\xFB"
Checking strprv\xFB for password...
Loser...

Rain intensifies outside, making Monaquimbamba jump

Monaquimbamba: screaming in fear “What is this huge storm?!”

Howard: eyes widening “The storm… STORMS! That’s it! You’re a genius, Monaquimbamba! Let’s try that!”

qemu-arm -L /usr/arm-linux-gnueabi/ -g 8989 ./exo5 "storms"
Checking storms for password...
Success, you rocks!