PWN0: Dead-Canary canary overwrite and format string.

PWN0

Hello everyone, this is my first write-up about the PWN challenge named "Dead-Canary", it's really a cool challenge, let's begin !

Terms used

- Format string
- Canary

checksec




- PIE is disabled, so ASLR don't apply for the binary (but it apply for libc page and the other pages of memory)

- Partial Relro (We can think of overwriting the Global Offset Table)


reverse engineering

file

This is the result of running 'file' command :


- We are dealing with a 64 bit binary

- Stripped binaries (the functions used for the binary like 'main' get some weirdo names just to make the reverse engineering harder this is called security by obfuscation)

ghidra

-As we said thie binary is stripped when we look at the defined functions in ghidra this is what we see :


FUN_004005e0 ?? what this function is doing ??
let's click it and see the disassembly in the right



As you see this function is doing nothing ?
Let's enumerate and go for the rest of the functions until we see something interesting ...

And when I clicked this function :


and see the disassembly it looks like it's the main :


I don't have any proof why this is the main but it looks good to consider it the main, let's click the function name in prototype and press "l" to change it's name to "main".



Press "l" a window will pop-up :



change it to main but note the address FUN_00400737 (this is the address to main):


Press ok !

This is the result :



But as you can see a lot of variables inside the function takes weirdo names too let's change them the same way:

Press the variable/function name you want to change > press "l" > type the name you want > press "enter"

This is the final result, it looks neat and easy to read huh ?! :



So now we need to see what this function is doing and where the vulnerability (or vulnerabilities) reside !

As you can see :

-The function is allocating a buffer of size 264 and read to it 0x120 bytes of data or (288 bytes) so we have here 24 bytes overflow.

- The function is not using printf correctly, it's using printf(buffer) uncorrectly it should be printf("%s", buffer)

- In the last the function is checking if the canary is safe (not overwritten), and if it's not the case it will trigger the __stack_chk_fail() function.

The plan

As you know the __stack_chk_fail() resides in the GOT, and we have PIE disabled and partial RELRO.

1 - Overwrite the __stack_chk_fail() in the GOT with the address of main() function using the format string vulnerability (to run the program unlimited times), the idea here is if we overwrite the canary using the 24 bytes overflow (we only need 1 or 2 bytes overflow) we will trigger __stack_chk_fail() that will call main() again and we have another QWORD write.

2 - Leak a libc address using the format string again, and calculate the libc base address to get the address of system(), and again overwrite the canary to return to main().

3 - Overwrite the printf GOT to system(), and again overwrite the canary to iterate one more time to main()

4 - now printf() is behaving like system() so this time just pass "/bin/sh" as input and the printf(buffer) will evaluate as system(buffer)

5 - Enjoy !

The exploit


First let's play with the program a little bit and take notes of it's behavior :

let's pass %p as input and see what will happen :


Good we already expected this, now let's see the offset to our input in the stack

let's see this is practice :




very interesting ! our AAAAAAAA get's written in the 6 position 0x4141414141414141 (consider nil too) , remember this number 6, we will use it later on.

Now let's do the first step in the plan
Write main() to __stack_chk_fail() GOT

we can do it for 2 lines of code using pwntools like that :


This is fairly easy, just using fmtstr_payload() from pwntools, but remember the 6 we talked about earlier we will use it now, and write dictionary is just what to write where.

And in the last we added a bunch of "M" to overwrite the canary and return to main again.

Let's leak libc :


How I leaked libc is just by test and fail, until I found the right position.
I just sent a pattern like this "%p %p %p %p %p ... %p" and see if I leaked an address from libc (suspicious address).

The first 100 of leaked address were just stack and binary addresses, but when I tried this I leaked the libc:
"%148$p %149$p %150$p 151$p %152$p %153$p %154$p %155$p"

The %149$p gives the address of the __libc_start_main + 243

__libc_start_main + 243 @ 0x7f266ae9eb97

it's easy now to get the libc base address!




But don't forget to add junk data to overwrite the canary and return to main again !

Let's change printf to system :

I guess this is easy for you now to change printf to system because you already did the stack_chk_fail() to main() one.

This is its part :



And again don't forget to add the junk to overwrite the canary (god I'm repeating a lot here but whatever) xD

Now the binary will look like this :


Now all you need to do is send "/bin/sh" to input and you're done because it will interpret it as system("/bin/sh")



You maybe wondering :

what this line will do ??

system("What is your name: ");

It just nothing, open your teminal now and type whetever you want and press enter the shell will just complain a little bit but it will not kick you out.

the line of code we wanna achieve is this :
system(buffer);

And that's all !


The result of running the exploit :



You can get the binary and full exploit in my github repo here :

https://github.com/MaherAzzouzi/PWNing/blob/master/redpwn/dead_canary/bin/solve.py

please if you have extra time give me your feedback, I love to hear your opinions about this styles of write-ups, follow me for another coming write-ups.

Enjoy !


Comments

Post a Comment