For this tutorial I've written a simple program in C that overflows a buffer on the stack with whatever it reads from the network. I cross-compiled it for MIPS Linux and ran it using QEMU chrooted into the unpacked filesystem of the Netgear WNDR3700v3 (Firmware 1.0.0.18).
The program, vulnerable.c contains the following function:
/* * vulnerable function. * reads up to 2048 off a socket onto a small buffer on the stack. */ int receive_data(int sockfd) { int read_bytes; char buf[512]; if(sockfd < 0) { return -1; } read_bytes=recv(sockfd,buf,2048,0); if(read_bytes < 0) { perror("recv"); }else { printf("read %d bytes.\n",read_bytes); } return read_bytes; }
This is a contrived example[1] but it should make it easier to focus on the mechanics of using Crossbow without getting bogged down in real-world complications.
The first module to know about when developing a buffer overflow is bowcaster.overflow_development.overflowbuilder. This module contains classes that will be useful for building an overflow buffer. There are two main classes to choose from when bulding your buffer, OverflowBuffer, and EmptyOverflowBuffer. They each represent a different way of solving the same set of problems, and they each have their advantages. For now, we'll use the first; OverflowBuffer. I'll do a subsequent tutorial show how to use the second.
The OverflowBuffer class starts you out with a buffer of a specified length filled with a pattern string consisting of upper and lower alphabetic characters and numbers (to help with debugging). One at a time, you can start replacing sections of that buffer with things like ROP gadgets or your payload.
Here's an example:
buf=OverflowBuffer(LittleEndian,2048)
Here we instantiate the OverflowBuffer object, passing it "LittleEndian" and the size of the buffer we want to create; 2048.
The LittleEndian object is a constant that OverflowBuffer will use whenever data encoding is endianness-sensitive. It is imported and made available for use like so:
from bowcaster.common.support import LittleEndian
The OverflowBuffer object can be converted to a string and sent to the target:
sock.send(str(buf))
With a debugger attached to the vulnerable program we can witness our first crash, controlling the function's return address:
The $ra register contains 0x41367241.
OverflowBuffer provides a function, find_offset(), that takes a string or an integer and will locate that value in the overflow string.
I like to add an option to my exploit program that lets me provide a search string on the command line and find the offset.
if len(sys.argv) == 2: search_value=sys.argv[1] if search_value.startswith("0x"): value=int(search_value,16) offset=buf.find_offset(value) if(offset < 0): print "Couldn't find value %s in the overflow buffer." % search_value else: print "Found value %s at\noffset: %d" % (search_value,offset) exit(0)
When searching for 0x41367241, it is found 528 bytes from the start of the overflow buffer.
Found value 0x41367241 at offset: 528
Now we can insert the first of a series of ROP gadgets at offset 528.
Controlling several S registers is important for staging ROP gadgets, but unfortunately this vulnerable function doesn't restore any S registers before returning. We can only control the $ra register. That means we need to return into a function epilogue that does restore several S registers.
While bowcaster doesn't provide the capability to search for ROP gadgets, this can be done in IDA Pro or using objdump from your compiler toolchain[2].
In the next part I'll cover how to describe your ROP gadgets and add them to your overflow using Crossbow.
------------------------
[1] Although, in the world of embedded MIPS Linux, not as contrived as one might think. :-/
[2] If anyone wants to contribute, a MIPS ROP finder that can be used independently of IDA would be super double awesome.
UPDATE 3/29/2013: Added syntax highlighting using https://github.com/Siarc/code2html. Thanks for the tip, @0xKD.
UPDATE 4/8/2013: References to Crossbow have been changed to Bowcaster