Rekalling Mimikatz
I'm not really sure that everybody knows that Rekall memory forensics framework contains a Mimikatz plugin: with this post I want to address this shortcoming, since the plugin has many good features and it can be easily extended.
behind the scenes
The act of rekall-ing Mimikatz started when I met Michael Cohen in Prague (SANS DFIR 2014) and a few months later in Dublin (DFRWS 2015). Despite the fact that I learnt so much by speaking with Michael, he deserves the credits to have pushed this plugin development: he released a first version on April 2015, based on what I did with Volatility (see et voilĂ le mimikatz offline). So by hangout-ing during the night, we co-authored the actual Rekall mimikatz plugin: it was an awesome dive in Windows memory and Rekall internals, guided by Michael who truly has a talent for explaining complicated things in a simple way.
Before going further credits and thanks must go to the awesome reverse engineering research made by Benjamin Delpy: the plugin is based on the knowledge he shared and currently shares.
If you don’t know how (and why) Windows keeps in RAM system and users passwords or hashes, I provide some references: Cached and Stored Credentials Technical Overview; Credentials Processes in Windows Authentication; and, obviously, Gentil Kiwi blog.
debugging symbols power
When Michael showed me the plugin's first version I was amazed since it was really short compared with my Volatility version. Despite the fact my code was a PoC and not an example of well-written Python code for sure, this first version leveraged one of the Rekall key features: the capability to fetch and use in real time Windows debugging symbols.
In order to extract the right information from memory we need both the locations of globals variables and structs layouts. In my Volatility plugin I used byte patterns (provided by Benjamin in his code) to find variables' location in memory: given the fact that Microsoft provides the core symbols we need to achieve our goals (lsasrv!LogonSessionList; lsasrv!hAesKey; lsasrv!h3DesKey; wdigest!l_LogSessList; and so on...), Rekall simplifies the first half of the job.
This is not the right place to fully address this capability, but once Rekall is instructed to create a profile for a given module (sys, exe or dll), it will extract its GUID from PE debug section and it will fetch from the Windows servers the right symbols by providing the GUID. From this point of view, the Rekall mimikatz plugin behaves like mimilib (the Mimikatz WinDBG extension).
In other words the plugin does not have to search structures in memory using the byte patterns (or anchors) Benjamin provided: the resulting plugin code is smaller and re-usable, since all supported Windows versions use the same symbols names (more on this later), while anchors based on byte pattern tend to change more frequently. The fact that we don't need to worry about searching for byte patterns makes plugin writing much simpler.
Structs layouts had to be reversed from Mimikatz, without the need to hand code them in the plugin: there is an automated process to pull those from the Mimikatz source code, as explained in the next section.
callable vtypes
Another great capability of the Rekall framework is the vtypes struct definition language, which is not new (see "Rekall Profiles" and "Memory Forensics with Volatility"): vtypes indeed provide a fantastic way to write compact and readable code. Let me try to show their power with an example in which we used a Mimikatz reversed structure in Rekall.
The widgest module is well-known for keeping in memory the user password: to get the credentials we used Benjamin's _KIWI_WDIGEST_LIST_ENTRY structure defined in kuhl_m_sekurlsa_wdigest.h.
We could have manually written the struct's vtype as it was done in the previous Volatility plugin. However, it is much simpler to automatically extract the vtype definitions from Mimikatz's own debugging information, since Rekall can already parse the PDB file format, it's enough to provide the Mimikatz PDB file to Rekall to get all the structures defined and referenced by the code in it: no manual work! Additionally we don’t have to clutter the source code with inline definitions of these structs - we can simply store the vtype definitions in the Rekall profile repository and fetch them on demand.
We compiled the Mimikatz code with PDB debugging symbols, then we used the parse_pdb Rekall command to get the json files with the structures we needed. Then we pushed the gzipped json file into the profile repository to have it easily available. By doing it for 32 and 64 bits architectures we are able to support both transparently, with really few lines in the plugin! In the next figure the _KIWI_WDIGEST_LIST_ENTRY resulting vtype is shown (64bits).
The previous definition explicitly defines Blink and Flink (backward and forward) pointers for the double linked list, but the Rekall framework has already implemented re-usable code to parse this type of list: see _LIST_ENTRY class and ListMixIn class defined in "overlays\basic.py". Moreover the kiwi structures do not say anything about credentials. We can extend the vtype by creating an overlay. The overlay "corrects" the generated vtype definition by overriding some fields and adding other fields:
With the previous (and incomplete) overlay, we declare the usage of _LIST_ENTRY (at offset 0) and adding the credential structure. The Cred field contains a _KIWI_GENERIC_PRIMARY_CREDENTIAL struct, located at an offset specified by a callable (which will be evaluated on access to the field) at 8 or 12 bytes (see later) following the end of the LocallyUniqueIdentifier field. With this simple action we are able to get all the list elements with these few lines of code:
That is amazing. The final wdigest code in the plugin becomes incredibly short, and it's reusable for all Windows versions and architectures known so far!
Note that Rekall profiles are specific to a binary. So each DLL and executable refers to its own profile. It is a good idea to avoid mixing multiple and possibly different vtype definitions coming from different PDB files. Another point is that we are not using all structures exported by Mimikatz, but only what we really need (see mimikatz_vtypes). Notice that we can simply get the exact address of the global constant "l_LogSessList" directly from debugging symbols, and therefore we do not need to scan for it:
logons = self.get_constant_object('l_LogSessList', target=...)
before switching to features...
The previous two sections should not be considered a "how to write a Rekall plugin" but an aid to understand the plugin code and why I said it's easy to extend it. Obviously there is a bit more to do to achieve a fully functional plugin, but the Rekall capabilities helps a lot to have a top-down approach, to write less code and to improve readability.
features
The plugin implements the lsasrv module, which is mandatory to decrypt credentials, the wdigest and livessp modules: the last two SSPs are known to provide the users' passwords if enabled. So we should have good coverage for Windows 7 and 8. Windows 10 will be supported soon. Regarding Windows XP see below.
The plugin logic is the following:
- switch to lsass process context
- get crypto material, to be able to decrypt data
- get all logons to the system and build a LUID dictionary
- for each LUID get the primary credentials (aka LM, NTLM and SHA1 hashes)
- if wdigest is used, for each LUID get the credentials
- if livessp is used, for each LUID get the credentials
- for each LUID, get DPAPI master keys from lsasrv
- render all the data obtained
- (note: decryption occurs where needed)
To use it just type rekal.py -f Win7SP1x86.raw mimikatz. If you get some troubles, just add -v parameter to get some details: they are particularly useful when reporting issues. Plugin's results in the following (small) screenshot.
If widgest/livessp is disabled you will unfortunately not get any cleartext passwords from the module. Additional problems include the needed memory being paged out, thus preventing the plugin from achieve its result. You could avoid this problem by using Rekall to dump memory together with the pagefile (use the aff4acquire plugin).
Remember that for DPAPI decryption you only need the SHA1 hash (unless using live logins, for which I will write another post in the future) and that you could find the user's passwords or some hints by accessing lsasecrets and/or decrypting system DPAPI secretes (as WiFi passwords, for example). See my previous post Happy DPAPI.
deprecated xp support
The problem with past XP support was the different type of encryption, DESX, and how the OS was using the DESX key. I spent some time to develop a Python decryption class, which is included in Rekall: for the details see my post UnDesXing. So we have added support for Windows XP/2003! Note that I lacked XP x64 ram images, so in that cases something could go bad: please report and, even better, provide ram images.
conclusions
I used the Rekall mimikatz plugin in different scenarios and I find it really useful, as much as I enjoyed writing it with Michael. The plugin's main goal is to get users' passwords, or SHA1 hashes to decrypt DPAPI at least. But it could be used from a “pure forensics” point of view, since it lists all logons to the system: actually timestamps are not reported, but it's a matter of adding a "print" statement. Or, when the proper kerberos module will be included, to detect an evil Mimikatz usage.
The plugin is included in Rekall, ready to be used! Just download the latest package from GitHub.
Comments
Post a Comment