Summary
In my previous post I discussed how to mess with the OS X syscall table through direct syscall table modification, syscall function inlining, and patching the syscall handler. As I promised, I'll be providing a plugin to find the mess! The code for the check_hooks plugin can be found at github and it incorporates existing detections for the sake of completeness. So let's go through the scenarios discussed earlier.
Syscall Interception by Directly Modifying the Syscall Table
- Replacing a Syscall with Another SyscallDetecting a duplicate syscall entry is straight forward: keep track of the syscalls as they are listed and see if a duplicate appears. The example I'll be using is discussed in my previous post, which was replacing the setuid function with the exit function:
Duplicate syscall function detection |
- Replacing a Syscall with a DTrace hook
This one is an easy catch as well. I just check the syscall name to if contains the word 'dtrace' to detect syscall and mach_trap DTrace hooks.
DTrace syscall hooking detection |
- Replacing a Syscall with an External Function
For this case I'll be using a Rubilyn infected memory sample provided by @osxreverser, which can be found here. This is not a new detection, but it's included for the sake of completeness. As a new feature to this detection, I've included the hook's destination kext (kernel extension) in the output (check_hooks/findKextWithAddress function). As pointed out in the Volatility Blog, this rootkit hooks three functions:
Rubilyn hook detection |
Syscall Function Interception or Inlining
Currently it is not possible to detect an inlined syscall function with the Mac side of the Volatility Framework because it only checks for the direct modification of the syscall table. To be able to detect function inlining, I applied two techniques:
- Check the function's prologue for modification, which will be useful later as well
- Check for the function's flow control
Looking at the syscall function prologues, it can be seen that they contain the following:
For x86:
PUSH RBP
MOV EBP, ESP
For x64:
PUSH RBP
MOV RBP, RSP
The volshell script I used to see this is below:#get sysent addresses for exit and setuid
nsysent = obj.Object("int", offset = self.addrspace.profile.get_symbol("_nsysent"), vm = self.addrspace)
sysents = obj.Object(theType = "Array", offset = self.addrspace.profile.get_symbol("_sysent"), vm = self.addrspace, count = nsysent, targetType = "sysent")
for (i, sysent) in enumerate(sysents):
tgt_addr = sysent.sy_call.v()
print self.addrspace.profile.get_symbol_by_address("kernel", tgt_addr)
buf = self.addrspace.read(tgt_addr, 4)
for op in distorm3.Decompose(tgt_addr, buf, distorm3.Decode64Bits):
print op
The check_hooks/isPrologInlined function checks to see if the prologue conforms with these known instructions.
The check_hooks/isInlined function, on the other hand, looks for calls, jumps or push/ret instructions that end up outside the kernel address space.
If we use the check_hooks plugin on a memory sample with the inlined setuid syscall function that trampolines into the exit syscall function we get the following:
Inlinded Function (setuid) Detection |
Another example of syscall inline hooking is DTrace fbt hooking, which modifies the hooked function's prologue. The check_hooks plugin will detect the DTrace fbt probe that is monitoring the getdirentries64 syscall function as well:
DTrace fbt probe detection |
Patched Syscall Handler or Shadow Syscall Table
The shadowing of the syscall table is a technique that hides the attacker's modifications to the syscall table by creating a copy of it to modify and by keeping the original untouched as discussed in my previous post.
The detection implemented in the check_hooks/isSyscallShadowed function works as follows:
The detection implemented in the check_hooks/isSyscallShadowed function works as follows:
- Check functions known to have references to the syscall table. In this case the functions are unix_syscall_return, unix_syscall64, unix_syscall.
- Disassemble them to find the syscall table references.
- Obtain the references in the function and compare to the address in the symbols table.
After running the attack code sample for the shadow syscall table attack, I ran the check_hooks plugin against the memory sample and received the following output that included hits for the shadow syscall table:
Shadow syscall table detection |
It looks like I have covered the detection of the examples in my previous post, but I'm not done!
Bonus! Scanning Functions in Kernel/Kext Symbol Tables
Now that I have the tools to detect function modifications, I decided to check on the functions in the rest of the kernel and kernel extensions. To be able to accomplish this task, I had to obtain the list of symbols per kernel or kext since the Volatility Framework is currently not able to list kernel or kext symbols from a memory sample.
I followed these steps in the check_hooks/getKextSymbols function:
Next step is to use the addresses obtained form the filtered symbols table to check for hooks.I followed these steps in the check_hooks/getKextSymbols function:
- Get the Mach-o header (e.g. mach_header_64) to get the start of segments.
- Locate the __LINKEDIT segment to get the address for the list of symbols represented as nlist_64 structs, symbols file size and offsets.
- Locate the the segment with the LC_SYMTAB command to get the symbols and strings offsets, which will be used to...
- Calculate the location of the symbols in __LINKEDIT.
- Once we know the exact address, loop through the nlist structs to get the symbols.
- Also find the number of the __TEXT segment's __text section number, which will be used to filter out symbols. According to Apple's documentation the compiler places only executable code in this section.
The nlist structs have a member called n_sect, which stores the section number that the symbol's code lives in. This value, in conjunction with the __text section's number helped in narrowing down the list of symbols to mostly functions' symbols. I say mostly because I have seen structures, such as _mh_execute_header still listed.
Some test output for kernel symbols |
Quick note, while syscall functions had identical function prologues, other functions in the symbols table, such as bcopy, have different ones. Therefore, using the isPrologInlined function produces false positives, which left me with using the isInlined function to detect hooks.
My target for this case is an OS X 10.8.3 VM running Hydra, a kernel extension that intercepts a process's creation, suspends it, and communicates it to a userland daemon, which was written by @osxreverser. Hydra inline hooks the function proc_resetregister in order to achieve its first goal. After compiling and loading the kext, I ran the check_hooks plugin with the -K option to only scan the kernel symbols to see what's detected:
Hydra hook detection |
Note: Most testing was performed on OS X 10.7.5 x64 and 10.8.3 x64. Feedback about outcomes on other OS X versions would be appreciated.
Conclusion
With the check_hooks plugin, now it's possible to detect hooked functions in the syscall table and kext symbols besides a shadow syscall table. While this is great, it doesn't end here. In my next post I'll be exploring OS X IDT hooks so stay tuned!
No comments:
Post a Comment