0x41 - back to bugs
COTS - there's a similar German word
The typical Windows scenario, Common Of The Shelf binary, leads to reverse engineering the target application to gain insight. Finding vulnerabilities can be a time-consuming task. Here're some motivating techniques to save tons of time.
Don't give up, just because...
The general methodology is quite self-explaining - similar for every audit process:
- behavior analysis:
- how does the target look like,
- which dialogues occur,
- which files are required, what are the paths,
- installer process,
- uninstall process,
- functions involved in the installer process: for example how does the shiny little personal AV, firewall get uninstalled. Which functions are used to deactivate it?
- More. In fact this list is very much dependent on how you aim to audit. Everybody has a different set of familiar skills. I prefer staring with file-permissions, but I don't generally avoid any entry point as long as I'm not sure that it's secured.
- modules/components: It's easy to find out. Think of Firefox. It has got a bunch of default add ons. Within the installer directory, there're a bunch of files, related to xulrunner and maybe other components. The architectural stuff is OpenSource and there's much documentation. Getting a list of components therefore is a piece of cake and doesn't require reverse engineering.
- interactions/relationships: so there're configuration files involved - which leads to file permissions. Dynamically compiled apps load a bunch of libraries. Focusing on Windows, speaking of PE and DLLs - you know what to try: DLL injection
- trust level: each interaction a software does happens with a certain level of trust.
- entry point probing: actually - just here. In the last phase. This is where the fuzzing starts.
- ManualBreakpoint(self, address, key, shiftkey, font)
- # Set a Manual Breakpoint.
- setUnconditionalBreakpoint(self, address, font="fixed")
- # Set an Unconditional Breakpoint.
- setConditionalBreakpoint(self, address, font="fixed")
- # Set a Conditional Breakpoint.
- setLoggingBreakpoint(self, address)
- # Set a Logging Breakpoint.
- setWatchPoint(self, address)
- # Set a watching Breakpoint.
- setTemporaryBreakpoint(self, address, continue_execution=False, stoptrace=False)
- # Set a Temporary Breakpoint.
- setBreakpoint(self, address)
- # Set a Breakpoint.
- DWORD setBreakpointOnName(self, name)
- # Set a Breakpoint.
- disableBreakpoint(self, address)
- # Disable Breakpoint.
- deleteBreakpoint(self, address, address2=0)
- # Delete Breakpoint.
- #...
- setHardwareBreakpoint
- #...
- BpHook - sets breakpoint
- LogBpHook - just logs
- AllExceptHook - if you're after SEH
- PostAnalysisHook - if you want to perform static decoding of basic function blocks, this is your hook
- CreateThreadHook/ExitThreadHook - I have even used this for non-security related debugging
- LoadDLL/UnloadDLL Hook - dynamically compiled applications, Windows, DLLs.
- ...
- from immlib import *
- class Threadstuff( CreateThreadHook ):
- def __init__( self ):
- CreateThreadHook.__init__( self )
- def run ( regs ):
- logToFile()
It gets a large list of things required to find out. Documentation is crucial. I like commenting IDA Pro DBs, dradis, and paper to plot stuff in an ugly way.
You shouldn't get demotivated. First of all: once you finish a couple of vulns you realize the patterns in the same way experienced programmers realize design patterns in code. I think as well as there are patterns that mark specific vulnerabilities, there should be security design patterns to guide development processes. I haven't seen much of those. However I see new attack patterns pup up every now and then.
Useful tools for each phase
This list cannot be complete. You should write target specific stuff sometimes. For example it's very practical to use ctypes to hijack any Windows client service binary. Very much depending on how it's compiled and which level of trust is set of course. So the most useful tool guiding this process is Python.
if you have a named pipe at 0.0.0.0 - use python to send code. if you have any kind of RPC client, use pymsrpc. There're literally hundreds of modules waiting for you.
pyDBG - with specific fuzzers is extraordinary useful to restart targets over and over again, automatically attached to the debugger to gain insight into the crashes (5th phase, entry point probing).
Paimei with its (cross plattform) pstalker backend can help you get a list of basic blocks to identify function offsets.
In the first phase often there's a huge mass of information. For example if you analyze the installer process. Sloppy installers lead to low hanging fruits. If they automatically set a default file handler, that may be useful as a client sided attack with a link. If they automatically generate a couple of temporary files, that may be useful to go through to find deprecated functions that still are entry points which reach code.
Sysinternals tools, and well... good old Powershell these days. You can log process lists, open sockets to files with PoSh very easily.
There're a few, but very important, tutorials regarding the Windows registry. It's important to know where to look for default file handlers, context menu entries, autostart applications, codec lists or startup parameters.
Disassembling is the most widely known reversing method. However you can get vulns without using disassemblers of any kind easily. Using a disassembler is interesting if you are finding out how functions (identified in phase 3) interact with each other.
It may be useful to look for imports at the .idata segment, to find anything like "dbg, debug, test" and to be able to cross-reference the function names. That's the first thing I try. Same thing for variable names. Most often I start with cross-referencing to get familiar on how to navigate through the binary. Cross-referencing can be automated with capable disassemblers.
You want to reconstruct the model. At this point familiarity with programming is crucial. If you don't know the design patterns used within the target software - you're out. Knowledge about compilers is necessary: if you don't know why the compiler added certain function pro- and epilogues - you are out. You will not find exploitable vulnerabilities if you try to find stack buffer overflows in a /GS compiled PE binary. It's unlikely. Even of down- and upgraph show that you reach the entry point.
- IDA surely is useful. More or less because of its plugin API, speaking of binary code coverage, winDBG integration, VMware support, graphing and and and...
It's just about the breakpoints
This hopefully doesn't read like you need a bunch of (expensive) tools. The single one, most important abilities to learn are: breakpoints and hooks. With breakpoints and hooks you can rule the internet. Maybe with binary differing on Wednesday mornings, too ;).
This simply concludes: read the manual of your debugger. I use Immunity Debugger with immlib "because I can"[tm], because:
Whether you script it, or perform it directly via the GUI doesn't really matter as long as you don't write customized event handlers (you should do that).
The debuggers - the "endless loop" - halts your application based on debug events. Debug events are breakpoints. Soft and hard breakpoints are a must-know. Furthermore memory-breakpoints. Soft-breakpoint modify the opcode, hard-breakpoints use CPU debug registers, memory breakpoints are about permissions regarding memory pages. It's mostly "read, write, execute" or guard. A guard page exception is a one-time event. The pages gets changed, raises your debug events and returns to it's state. Read, Write, Execute - garbage in/out/execute. You shouldn't just use software breakpoints because of the opcode modifications that affect the runtime behavior.
Immunity Debugger is Captain Hook!
My real reason for imDBG isn't just how it programmatically handles breakpoints. ollyDBG can be extended, and at some point GDB's new python interface is very capable too (more for scenarios with source).
When you install imDBG you get a set of very interesting Python scripts. Some are for dynamic Heap layout analysis (Libs.libhook), very important for application specific exploit dev.
Others are for PyHooks - a set of soft-hooking PyScripts. Look at Libs.libhook here. Here're some of the most useful ones:
A low target process impact
Soft-hooking has an impact on the target-process. As well as soft-breakpoints have. Every soft-* has. Generally I tend to keep the reversing footprint low in order to prevent causing my own problems. Without halting, less-invasive therefore - ready to analyze the application specific runtime behavior.
Under the Hook
Instrumenting heap-routines works very will with hard hooks and DLL injection.
hippy.py at this point is an interesting piece of documented source. Very helpful ;).
Summary
The process from an operational perspective mainly became clear. As every audit process it starts with some boring enumeration (1st phase). After that it can be fun when it comes to debugging and disassembling. However it's important to be able to return to the results of the enumeration phases if the entry points turn out to be unexploitable for some reasons.
Have fun,
wishi

Post new comment