Building Custom Modules for Core Impact, Part 2 of n-1
Last week, we discussed exactly what we’ll be building and got some of the boilerplate done along the way. I’m sure that you dug into the modules that I strongly hinted that you take a look at for inspiration.
To review, this module will need to:
- Know where the Hashcat executable lives
- Know what hashes we want cracked
- Know the minimum password length to brute force
- Know the maximum password length to brute force
- Know how often to poll Hashcat for the output file
- Extract the hashes specified and write them out to a file
- Execute Hashcat, passing in the list of hashes
- Read the output from Hashcat, and then update the Identity Store to add the cracked passwords back in
Last week, we addressed the first five requirements through setting up the parameters in the __xmldata__ values.
<property key=" parameters="> <property key="TARGET" type="entity_name:identity,multiple"></property> <property key="HashCatPath" type="file:local">c:\hashcat-3.20\hashcat64.exe</property> <property key="Minimum Length" type="int">1</property> <property key="Maximum Length" type="int">8</property> <property key="Polling Interval" type="int">60</property> </property>
We’ll be able to access the values we set for the parameters after they are injected into the module when it executes.
Depending on what a particular Impact module is intended to do, it will import from one or more base classes within the Impact distribution. Most commonly, it will inherit a class called ModuleBase. If you look through other modules, you may see modules inheriting from Module. Don’t copy that. The Module class is being phased out. It may go away at any point, so save yourself the trouble. There are all kinds of interesting and useful utility functions hanging off of ModuleBase, so somebody please remind me to circle back later to explore it in more depth here.
You’ll want to drop this import statement into the module so that we can inherit the ModuleBase properly.
from impact import modulelib
It’s best practice to put the import statements after the __xmldata__ block, but before the module code.
Because we’re working with identities, we’ll also want to mix in the IdentityCopier class to give us the base functionality for working with them. Python’s multiple inheritance function makes this easy.
First, we’ll add:
from impact.identity_manager.copier import IdentityCopier
Then we can get into the meat of it:
Next, we look at getting things setup. Inside the constructor for our class, we need to make sure that we properly call the __init__ functions for both ModuleBase and IdentityCopier
def __init__(self): modulelib.ModuleBase.__init__(self) IdentityCopier.__init__(self)
Next, we start with initialize(), which we inherit from ModuleBase. It’s one of the five base functions that Impact expects to see in a Module (the others are targetSetup(), targetRun(), targetFinish() and finalize(). These are all implemented as stubs in ModuleBase, but those won’t do anything, so we’ll override them here. The reason that we’re overriding initialize() is that we need to setup a couple of lists that are going to be globally available inside this module, and initialize() is the “least bad” place to shove them. Mind you, my python skillset is as rusty as the surface of Mars, so if I’m totally wrong about that, please correct me.
We’ll need to expressly call ModuleBase’s initialize(), but not IdentityCopier, since it doesn’t really have one that’s meaningful.
def initialize(self): modulelib.ModuleBase.initialize(self) self._not_cracked_identities = set() self._cracked_identities = set()
In the function above, we start by calling down to the ModuleBase.initialize() function, then we setup a list of _not_cracked_identities and _cracked_identities. I’m choosing to abide by PEP-8, and use the leading _ to identify these as local objects.
Next, we need something that’s going to require some explaining…
def isOneShoot(self): return True
I actually have to make explanations for two different things. First, this function tells Impact that it should only run this module once and that we’re going to take care of handling multiple targets being passed in ourselves– rather than calling the module for each target. The second thing I have to explain is that as you go through our codebase, there’s often interesting grammatical constructions present, owing to our developers being in Buenos Aires, Argentina – where English is not being their first language. I’m sure that a Linguistics PhD candidate would get a great thesis out of how our internal jargon is a creole of Hacker, English and Argentine Spanish, but that’s a whole ‘nother blog right there.
I mentioned above the five standard functions that Impact expects will be present in a Module. The first one run is "initialize()". The one run on the way out is "finalize()". Unless you are doing something very, very wrong, these should only ever run once.
The other three functions, targetSetup(), targetRun() and targetFinish() may or may not be run more than once. By setting isOneShoot() to return True, we’re telling Impact that those functions are only going to be run once and that we’re taking responsibility for looping over multiple targets ourselves.
def targetSetup(self): self._internalTargetSetup()
Inside of targetSetup(), the only thing we really need to do is call _internalTargetSetup(), which we picked up from IdentityCopier. It’s used for an internal state that we’ll never end up touching directly, and the rest of it doesn’t work unless we call it, so I’m just going to proceed with calling it because this is supposed to be a quick and dirty minimally functional module, and I’m not willing to spend a couple of hours spelunking through code to figure it out. It’s not hurting anything to be there, so we’ll move on to the meat of the thing.
def targetRun(self): self.logMed("Grabbing Target Identities") self._not_cracked_identities = self._GetIdentitiesFromIdentityStore() self.logMed("Outputting for Hashcat") self._ProcessCredentials()
The targetRun() function is where the work of the module is going to be done. From past experience, I have an aversion to really long functions, so I’m breaking some things out into different functions. These are for my own sanity and convenience, so if you’d rather dig through a 2000 line function for your own stuff, I won’t stop you, nor will Impact.
I want to call your attention to the self.logMed() function that I use twice there. It’s one of the useful utility functions that we got from ModuleBase. It’s got a couple of siblings: logLow(),logHigh() and logDebug(). What these functions do is let you print output to the Module Log window in Impact. It’s used for keeping track of what the module is doing at a particular time, as well as being useful for quickly getting debugging output. On the second line I populate _not_cracked_identities with the output from a new function _GetIdentitiesFromIdentityStore().
To support the Identity functionality, we’ll need to import a *few* new modules
from impact.identity_manager import manager from impact.identity_manager.identity import Identity from impact.identity_manager.storage import IdentityEntity, IdentityNotFoundException from impact.password_cracking.identities_handling import IdentitiesFilter, IdentityHashesRetriever
Now we can dig into this thing…
def _GetIdentitiesFromIdentityStore(self): entities = filter(lambda entity: isinstance(entity, IdentityEntity), self.getTargetEntityList()) entity_names = map(lambda identity_entity: identity_entity.get_name(), entities) identities = self.__retrieve_identities_from_storage(entity_names)
We start out by pulling out the target identity entities that were passed into the module as a parameter, then retrieve the names of the entities, and use that to populate a list of the identities from the Identity Manager.
Next, we’ll use some additional filters provided by the IdentitiesFilter class to further reduce our load, and chances of getting bad data before returning the list.
# Filtering out things we do not want identities = IdentitiesFilter.filter_non_windows_ntlm_identities(identities, self.logger) identities = IdentitiesFilter.filter_computer_accounts_identities(identities, self.logger) identities = IdentitiesFilter.filter_already_cracked_identities(identities, self.logger) identities = IdentitiesFilter.filter_already_cracked_identities_using_identities_database(identities, self.logger) identities = IdentitiesFilter.filter_identities_without_hash(identities, self.logger) identities = IdentitiesFilter.filter_identities_with_invalid_hash(identities, self.logger) return identities
Now, let’s take a look at _ProcessCredentials()
def _ProcessCredentials(self): outputFile = os.path.join(impact_sys.temp_path, "Output.%s" % ("".join([random.choice(string.letters + string.digits) for x in range(8)]))) passwdFile = os.path.join(impact_sys.temp_path, "Pass.%s" % ("".join([random.choice(string.letters + string.digits) for x in range(8)]))) self.logMed("Output File: %s" % outputFile) self.logMed("Password File: %s" % passwdFile)
First off, we need to pick a pseudo-random file name to use to feed Hashcat the hashes and one for the responses as well. We’re going to use the impact_sys.temp_path as the base path for these, since Impact will always have rights to access this path, and it cleans itself up when you close the program. We need a couple more imports to make these bits all happen.
Import os Import string Import Impact_sys
Next, we need to open up that file for hashes, and go ahead and write them out.
p = open(passwdFile, "w") for identity in self._not_cracked_identities: username = identity.getPropertyOrDefault(Identity.USERNAME, "") _hash = IdentityHashesRetriever.get_hash(identity) hash = _hash.split(":") p.write(hash) p.write("\n") p.write(hash) p.write("\n") p.close()
With that done, we have one more thing to do before we fire off Hashcat that has to do with how it handles masks and charactsets. Because we’re just brute-forcing here, we need to generate a mask that is as long as the Maximum password length that we passed in as a parameter.
Mask = "?a" * self.getMaximumLength()
The getMaximumLength() function simplifies our code writing burden for accessing the parameters we passed in. I created one for each of the parameters, and they all follow the same basic pattern that you’ll see below.
def getMaximumLength(self): return self.getParameters().get('Maximum Length', None)
Now that we have that, we can use that, and the other parameters, to actually launch Hashcat. Now, Impact gives you about a dozen different ways to execute something from a module, but we want to actually be able to interact with Hashcat, so we want it to run visibly.
PyImpact.execute_client_cmd("cmd.exe /k start %s -m 1000 -a 3 -i --increment-min=%s --increment-max=%s --potfile-disable --outfile=%s %s %s" % (self.getHashCatPath(),self.getMinimumLength(),self.getMaximumLength(),outputFile,passwdFile, Mask),False)
Then, we wait a bit so that we don’t exit prematurely.
DoneWithCrack = False time.sleep(30)
Now, we enter into a loop that will continue running as long as the Hashcat process continues…
while DoneWithCrack == False: if not self.getTasks(str(self.getHashCatPath()).split("\\")[-1]): DoneWithCrack=True if os.path.exists(outputFile): f=open(outputFile) self.logMed("Reading Output") for l in f: if ':' in l: hash,password = l.split(":") #Find the identity that matched the hash #self.logMed("Finding Identity for hash %s" % hash) ident = self.__findIdentityWithHash(self._not_cracked_identities,hash) if ident: self.logMed("Updating Identity") update_ident = self.__UpdateIdentity(ident,password) f.close() else: self.logMed("No Output yet. This may take a while. ") time.sleep(int(self.getPollingInterval()))
Attempt to open the output file and read through it. Any cracked hashes that we find in the output file will be updated back in the identity store.