Home pointer overflow 2024 Reverse100-1
Writeup
Cancel

Reverse100-1

Highlighted techniques

  • how to patch files in ghidra
  • how to retype strings to make them readable in ghidra
  • small gdb tutorial on how to set a breakpoint and skip the function with jump

Learning the game

We are presented with a file called Reverse100-1. I run the file command against it:

Reverse100-1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9604fd8c1b649b4686112951a38c3b7280449fc5, for GNU/Linux 3.2.0, not stripped

it seems to be an ELF file, so I’ll be running it with my docker debian container.

1
2
## ./Reverse100-1
Encoded flag: ΑΑ̠ȗ̠̍ʠȍȗ

The program seems to be printing out the flag after it has had some sort of transformation applied, time to put on the gloves.

Playing the game

Alright time to get a bit more serious, I’ll be showcasing two ways to solve this, by performing dynamic and static analysis.

For the sake of understanding we’ll start by looking at the program through the eyes of the dragon (ghidra, you guessed correctly)

We saw from the file command that this program is not stripped so ghidra should have plenty of information to work with.

decompiled “main” function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
undefined8 main(void)

{
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined7 local_20;
  undefined4 uStack_19;
  
  local_38 = 0x77757b6674636f70;
  local_30 = 0x31775f6e315f7073;
  local_28 = 0x33723368375f336e;
  local_20 = 0x7572375f35315f;
  uStack_19 = 0x7d6837;
  obfuscate(&local_38);
  printf("Encoded flag: %s\n",&local_38);
  return 0;
}

Hmm… Okay so let’s break it down:

  • first of all we can see a bunch of weird variables with unrecognized types being defined, we’ll come back to it later
  • after that we have a call to obfuscate() with a pointer to local_38 (one of our weird variables) being passed as an argument
  • then we have the following call printf("Encoded flag: %s\n",&local_38);

So we can clearly guess that the last two lines we identified are obfuscating/encoding the flag and then printing it, also the obfuscation is performed in place since both the obfuscation function and the print function receive the same pointer.

Let’s solve the mistery of those weird variables, you may have already guessed that they are actually meant to be a single variable, a string, the flag. Why does ghidra show us a long string like this? Well because the disassembly looks like this:

img.png

As you can see there are a bunch of weird values being moved into registers, ghidra sees each of these as a new variable, but we can solve that.

How to spoon feed a string to Ghidra

  • Right click the first variable generated and look for the “Retype Variable” option (or do Ctrl+L)

img_1.png

  • Retype it to char[n] where n is the amount of characters in the desired string, to get that you can add up how many bytes each of the variables you want to merge is using, but in this case since we only have this string as a variable we can check the function prologue to know how many bytes we need (remember that values in the disassembly are in hex)

img_2.png img_3.png

img_4.png

It’s not perfect but a lot more readable than before, with this we could already reconstruct the entire flag by hand.

What if I don’t want to rebuild the flag like a LEGO?

Okay then you could create a script for ghidra that gets the variable and reconstructs it… Okay let’s just see how to patch the program to make the obfuscation never happen.

  • We go to the call to obfuscate in the disassembly and look for the Patch Instruction option (or use Ctrl+shift+G)

img_5.png

  • We’ll patch it so it becomes a NOP instruction, I don’t know if there’s a NOP instruction that is long enough but I just added two of them

img_6.png

after this we can export the program and run it.

  • press O to export the program, select “Original File”, change it’s name and then click “Ok”

img_7.png

  • after this you can run your patched file.

img_8.png

Extra: How to do it all from the terminal and feel like a superhero

Lastly I’ll show you how to solve this from a linux terminal (bash) using gdb (dynamic analysis).

  • run the program with gdb
1
gdb Reverse100-1
  • set a breakpoint in the obfuscate function (we can do this since the program has symbols left on it)

img_9.png

  • Run the program and let it hit the breakpoint, let them come to us.

img_10.png

  • Now, enter the matrix by enabling the disassembly layout
layout asm

img_11.png

  • From here we can see everything, and by everything I mean that we can see the instruction we are currently about to execute and what follows. Let’s show a bit more of our power and jump straight to the prologue of the function, ignoring the rest of the instructions.

img_12.png

BOOM!!

Got ‘em, they don’t even know what hit them ;)

Flag

user: root:
Trending Tags