This paper was published on NDSS2015. Virtual table hijacking is an attack to overwrite the virtual table pointer(vfptr) by utilizing the use-after-free or heap-overflow vulnerability, which finally hijacks the control flow of the program. In this paper, the author proposed an protection on the binary by instrumentation.
In this paper, the author assumes that the attacker can (1)read arbitrary readable memory, (2)write to writable memory, (3)not read or write directly to a register.
Generally speaking, there are three types of exploitation techniques on virtual table
(1) VTable Corruption Attacks: Directly corrupt the existing vtable’s contents.
(2) VTable Injection Attacks: Inject a fake vtable into the memory of application and overwrite vfptr to point to the fake vtables.
(3) VTable Reuse Attacks: Similar to VTable Injection, overwrite the vfptr to point to existing vtables.
A typical virtual function call is given below:
Design and Implementation
The basic idea of VTint has three parts shown as following:
The basic security policy enforced by VTint is simple and effective. It enforces that all vtables are read-only. In particular, VTint moves vtables to a read-only section, and instruments them with a special ID. At runtime, the virtual function dispatches’ target vtables are validated against this ID.
PEParser:: Parse the target executables and recover the section table, export table and the relocation table.
BitCover:: Based on the the information recovered above, this phase conducts a recursive disassembly analysis and identifies all the functions, instructions and control tables.Then based on the data-flow analysis to identify vtables and virtual function dispatches.
VRewriter:: Move all the identified virtual tables to a read-only area, and add an id to each virtual table. Then instrument the security check at the calling location of these virtual calls.
In this part, the PE parser parses the binary code of the target executable and retrieves the relocation table information of PE. According to author, since the address of vtable will be used in the constructor function, by retrieving the information in PE, PE parser can find all candidate vtables in the code.
The BitCover will recursively disassemble the binary code. Based on the observation that each vtable will be used in the associated class’s constructor function, and a pointer will be assigned to the generated object’s vfptr. The author proposes a new forward data-flow analysis to identify these vtable assignment operations, and filter real vtables. Then based on a general virtual function pointer fetching pattern, the author also identifies all virtual function callsite.
VRewriter will first moves the identified vtables to another section in memory then change the area to read-only. Since we have no idea about the size and hierarchy of the object, it must get the size of the vtable first. Scan the words from the beginning of the vtable and increase the size one by one, and stop counting if one of the following conditions is met. First if a word scanned is not a relocation entry. Second, a word scanned is directly referenced by some data or code. After getting the size of the object, suppose the size to be N. Then the author conservatively copy this vtable and at most (4 + 4*N) bytes data into the new read-only area.
Besides the read-only area, VRwriter will put a security checking cookie in the first word of the page where the new memory is allocated.
Enforced Binary Code
Thus after instrumenting the security check binary code will look like following:
Thus for virtual function call in the binary code, VRewriter will instrument two checks before the call site: checking whether the area where vtable allocated is writable or not, checking if the ID of the vtable matches.
Since in C++, there is no effective way to check whether an area is writable or not, the author utilize SEH in windown to achieve this goal. By registering a exception handler in stack and attempting to write some data to the target vtable, if the writing operation was successful, it will trigger the exception handler and jump to the error handling route.