canary

Leaking a stack canary to allow for buffer overflows.

file-archive
3KB
archive

This problem is the first instance where the stack protector, called the canary is enabled. We need to figure out how to beat that canary to perform a buffer overflow.

When we run checksec on the binary, we notice that the canary is enabled.

[*] '/home/joybuzzer/Documents/vunrotc/public/binex/04-canaries/canary/src/canary'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

PIE is still disabled, meaning we can still use the same techniques we used in the previous binary. However, we can't use the same techniques to overflow the stack. Let's see what happens when we try to overflow the stack.

$ python -c "print('A'*1000)" | ./canary
Hello, what is your name?
...
How can I help you today?
*** stack smashing detected ***: terminated
zsh: done                           python3 -c "print('A' * 1000)" | 
zsh: IOT instruction (core dumped)  ./canary

A stack smashing detected statement in the output indicates that we overwrote the canary. We need to determine how to beat the canary, and then we will proceed as usual.

Static Analysis

It seems that our primary functions are main, read_in, and win.

  • win seems only to print the contents of the flag.

  • main immediately calls read_in, then has a puts statement. We can use gdb to show that reads "You lose!".

read_in does a list of things, so let's break it down further. Checking the arguments as we scroll through gdb:

  • puts("Hello, what is your name?")

  • gets(ebp-0x4c)

  • printf(ebp-0x4c)

  • puts("How can I help you today?")

  • gets(ebp-0x4c)

After this last check, we see that the canary check is made:

From this, we gather a few things:

  • We write to the same buffer both times that we write to memory.

  • The format string vulnerability in the first read means we can leak the canary.

  • The existence of a second read means we can pass in a second payload that uses the canary to pass the check, then performs a buffer overflow.

  • The canary is stored at ebp-0xc.

Payload Part 1: Leaking the Canary

We know the canary is stored on the stack at ebp-0xc. We will use a format string to leak the canary to standard output and then pass that canary to the second payload.

There are two ways to find the offset on the stack that the canary is stored at:

  1. Use gdb to set a breakpoint before the gets call, then count the number of DWORD frames between the stack pointer and canary.

  2. Check the location of the stack pointer at the start of the read_in function, account for the number of operations on the stack pointer, then count the number of DWORD frames between the stack pointer and canary.

The first one is by far easier and more practical.

gdb tells us that the canary is at 0xffffd80b. If we count from our location to the canary, we see that it is 23 DWORDs away. We can verify this using the format string:

This appears to work! Let's turn this into a pwntools script:

Payload Part 2: Overflowing the Buffer

Now that we have the canary, we can overflow the buffer. We need to do this in two steps:

  1. Write data until we reach the canary, then write the canary to the stack (so it doesn't get modified).

  2. Write data from the canary to the return pointer, then overwrite the return pointer with the address of win().

We discussed earlier that the canary is stored at ebp-0xc. Based on the argument passed to gets(), we start writing at ebp-0x4c. This means we need to write 0x40=64 bytes of data before reaching the canary.

Our payload here could look like this:

Then, we need to write from the canary to the return pointer. The canary sits at ebp-0xc, and the return pointer always sits at ebp+0x4 (because the base pointer is at ebp+0x0). Remember that the canary takes four bytes itself, meaning we start to write at ebp-0x8. This means we need to write 0xc bytes of data to reach the return pointer.

Our payload here could look like this:

circle-exclamation

From here, we put it all together into one large payload and send it off!

Running this gets us our flag.

Last updated

Was this helpful?