# Hitcon 2014 stkof

Let's take a look at the binary, and the libc:

```
$    file stkof
stkof: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=4872b087443d1e52ce720d0a4007b1920f18e7b0, stripped
$    pwn checksec stkof
[*] '/home/guyinatuxedo/Desktop/hitcon14/stkof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
$    ./stkof
1
58
1
OK
2
9
FAIL
$ ./libc-2.23.so
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 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 5.4.0 20160609.
Available extensions:
  crypt add-on version 2.1 by Michael Glad and others
  GNU Libidn by Simon Josefsson
  Native POSIX Threads Library by Ulrich Drepper et al
  BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
```

So we can see that we are dealing with a `64` bit binary, with a Stack Canary and Non-Executable stack. When we run the binary it scans in input and responds with either `OK` or `FAIL`. In addition to that we are dealing with the libc version `libc-2.23.so ` (full disclosure I'm not sure if this is the original libc for the challenge, but it works with the unlink attack).

## Reversing

When we take a look at the binary in Ghidra, we find a function at `0x00400c58` that appears to be the menu function:

```

undefined8 main(void)

{
  int menuChoice;
  char *bytesRead;
  long in_FS_OFFSET;
  int result;
  char input [104];
  long stackCanary;
 
  stackCanary = *(long *)(in_FS_OFFSET + 0x28);
  alarm(0x78);
  do {
    bytesRead = fgets(input,10,stdin);
    if (bytesRead == (char *)0x0) {
      if (stackCanary == *(long *)(in_FS_OFFSET + 0x28)) {
        return 0;
      }
                    /* WARNING: Subroutine does not return */
      __stack_chk_fail();
    }
    menuChoice = atoi(input);
    if (menuChoice == 2) {
      result = scanData();
    }
    else {
      if (menuChoice < 3) {
        if (menuChoice == 1) {
          result = allocateChunk();
        }
        else {
LAB_00400ce3:
          result = -1;
        }
      }
      else {
        if (menuChoice == 3) {
          result = freeFunction();
        }
        else {
          if (menuChoice != 4) goto LAB_00400ce3;
          result = printData();
        }
      }
    }
    if (result == 0) {
      puts("OK");
    }
    else {
      puts("FAIL");
    }
    fflush(stdout);
  } while( true );
}
```

So we can see, we have four different menu options. `1` for allocating chunks, `2` for scanning data, `3` for free a chunk, and `4` for printing data. Also there is a system where the functions will report back if they were successful, and that is what triggers either the `OK` or `FAIL`. Let's take a look at `allocateChunk`:

```
undefined8 allocateChunk(void)

{
  long lVar1;
  size_t __size;
  void *ptr;
  undefined8 uVar2;
  long in_FS_OFFSET;
  char sizeInp [104];
 
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  fgets(sizeInp,0x10,stdin);
  __size = atoll(sizeInp);
  ptr = malloc(__size);
  if (ptr == (void *)0x0) {
    uVar2 = 0xffffffff;
  }
  else {
    ptrCount = ptrCount + 1;
    *(void **)(&ptrArray + (long)(int)ptrCount * 8) = ptr;
    printf("%d\n",(ulong)ptrCount);
    uVar2 = 0;
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}
```

So we can see here, it prompts us for a size then mallocs that many bytes. There is no check on the size we pass it (only a check to ensure that malloc didn't return a null pointer). After that it will increment the bss integer `ptrCount` at `0x602100`, and store the pointer in `ptrArray` at `0x602140` (also it is one indexed so the pointers start at `0x602148`). Next up we have the `scanData` function:

```
undefined8 scanData(void)

{
  long lVar1;
  int bytesReadCpy;
  ulong index;
  undefined8 result;
  size_t bytesRead;
  long in_FS_OFFSET;
  size_t size;
  void *ptr;
  char input [104];
  long canary;
 
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  fgets(input,0x10,stdin);
  index = atol(input);
  if ((uint)index < 0x100001) {
    if (*(long *)(&ptrArray + (index & 0xffffffff) * 8) == 0) {
      result = 0xffffffff;
    }
    else {
      fgets(input,0x10,stdin);
      size = atoll(input);
      ptr = *(void **)(&ptrArray + (index & 0xffffffff) * 8);
      while( true ) {
        bytesRead = fread(ptr,1,size,stdin);
        bytesReadCpy = (int)bytesRead;
        if (bytesReadCpy < 1) break;
        ptr = (void *)((long)ptr + (long)bytesReadCpy);
        size = size - (long)bytesReadCpy;
      }
      if (size == 0) {
        result = 0;
      }
      else {
        result = 0xffffffff;
      }
    }
  }
  else {
    result = 0xffffffff;
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return result;
}
```

Here we can see that it prompts us an index to `ptrArray` for where to scan in data. Then it prompts us for the amount of bytes to scan in. Notice that it doesn't check the size we pass it, so we have a heap overflow bug here. Next up we have `freeFunction`:

```

undefined8 freeFunction(void)

{
  long lVar1;
  ulong index;
  undefined8 result;
  long in_FS_OFFSET;
  char indexInput [104];
  long stackCanary;
 
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  fgets(indexInput,0x10,stdin);
  index = atol(indexInput);
  if ((uint)index < 0x100001) {
    if (*(long *)(&ptrArray + (index & 0xffffffff) * 8) == 0) {
      result = 0xffffffff;
    }
    else {
      free(*(void **)(&ptrArray + (index & 0xffffffff) * 8));
      *(undefined8 *)(&ptrArray + (index & 0xffffffff) * 8) = 0;
      result = 0;
    }
  }
  else {
    result = 0xffffffff;
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return result;
}
```

Here we can see, it prompts us for an index to free. If it passes the check, it will free the pointer, and clear it out (so no use after free). Next up we have `printData`:

```
undefined8 printData(void)

{
  ulong uVar1;
  undefined8 uVar2;
  size_t sVar3;
  long in_FS_OFFSET;
  char local_78 [104];
  long local_10;
 
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  fgets(local_78,0x10,stdin);
  uVar1 = atol(local_78);
  if ((uint)uVar1 < 0x100001) {
    if (*(long *)(&ptrArray + (uVar1 & 0xffffffff) * 8) == 0) {
      uVar2 = 0xffffffff;
    }
    else {
      sVar3 = strlen(*(char **)(&ptrArray + (uVar1 & 0xffffffff) * 8));
      if (sVar3 < 4) {
        puts("//TODO");
      }
      else {