Skip to content

Commit

Permalink
Fixes #4
Browse files Browse the repository at this point in the history
  • Loading branch information
eth0izzle committed Jan 2, 2018
1 parent b7e9c60 commit 7aa5e9a
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
*.pyc
.idea/
.virtualenv/
Expand Down
42 changes: 30 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# cracke-dit
**cracke-dit** *("Cracked It")* **makes it easier to perform regular password audits against Active Directory environments.**

Ensuring your users have strong passwords throughout the organisation is still your best line of defence against common attacks. Many organisations over estimate just how secure their users' passwords are. "London123", "Winter2017", "Passw0rd" - all complex passwords, according to the default Group Policy rules.
Ensuring your users have strong passwords throughout the organisation is still your best line of defence against common attacks. Many organisations over estimate just how secure their users' passwords are. "London123", "Winter2017", "Passw0rd" - all complex passwords, according to the default Group Policy rules, and probably your users.

By performing regular audits, you can identify users with weak passwords and take action inline with your policies and procedures. See [General Tips](#general-tips).

Expand All @@ -12,36 +12,49 @@ Python 2.7+ and pip are required. Then just:
1. `git clone https://github.com/eth0izzle/cracke-dit.git`
2. *(optional)* Create a virtualenv with `pip install virtualenv && virtualenv .virtualenv && source .virtualenv/bin/activate`
2. `pip install -r requirements.txt`
3. `python cracked-dit.py --help` (and see [Usage](#usage))
3. `python cracke-dit.py --help` (and see [Usage](#usage))

## Usage
### 1. Extracting the database
The first step in your password cracking adventure is to extract a copy of the Active Directory database, ntds.dit, which contains the password hashes. I like to involve and get as much buy-in as possible from the Admins so I will ask them very nicely to extract the files for me. However if you have domain credentials you can do it yourself:
### 1a. Extracting the database
The first step in your password cracking adventure is to extract a copy of the Active Directory database, ntds.dit, which contains the password hashes. Depending on your persuasion you have a few options:

#### 1. Remote extraction with cracke-dit *(faster)*
1. `python cracke-dit.py --username administrator --password passw0rd --target 192.168.1.1`

#### 2. Remote extraction with metasploit
1. Run module `auxiliary/admin/smb/psexec_ntdsgrab` and fill in the required options.
2. Follow 1b to extract the hashes from ntds.dit.

#### 3. Nicely ask a Sys Admin
1. Follow 1b to extract the hashes from ntds.dit.

#### 4.Local extraction
1. On a Domain Controller open up an elevated command prompt.
2. Run `ntdsutil "ac i ntds" "ifm" "create full c:\temp" q q`.
3. **Securely** extract `c:\temp\Active Directory\ntds.dit` and `c:\temp\registry\SYSTEM` to your system with cracke-dit.

Or remotely via metasploit. Run the module `auxiliary/admin/smb/psexec_ntdsgrab` and fill in the required options. This requires SMB access via the C$ share.
4. Follow 1b to extract the hashes from ntds.dit.

(if you just want to have a play, you can use the sample files in `./samples`)

### 2. Extracting the hashes
### 1b. Extracting the hashes

*(not required if you remotely extracted with cracke-dit)*

All password hashes are protected with 3 layers of encryption. Thankfully everything we need to decrypt is within the SYSTEM hive. The next step is to extract the hashes and usernames in to a separate file for cracking:

1. Run `python cracked-it.py --system SYSTEM --ntds ntds.dit` (optionally with `--no-history` flag if you don't care about historic passwords)
1. Run `python cracke-dit.py --system SYSTEM --ntds ntds.dit` (optionally with `--no-history` flag if you don't care about historic passwords)
2. Once complete, your username:hash file will be at `<domain>.hashes.ntlm` - delicious.

(if you have a powerful GPU use oclhashcat)

### 3. Cracking the hashes
### 2. Cracking the hashes
cracke-dit doesn't actually *crack* passwords, you will need to use your favourite password cracker for that. cracke-dit just needs a `.pot` file (hash:password) for processing. I'm partial to hashcat so:

1. `hashcat -m 1000 --potfile-path <domain>.pot --username <domain>.hashes.ntlm /usr/share/Wordlists/rockyou.txt` which will be pretty quick.
2. Do a second pass with [H0bRules](https://github.com/praetorian-inc/Hob0Rules): `hashcat -m 1000 --potfile-path <domain>.pot --username <domain>.hashes.ntlm /usr/share/Wordlists/rockyou.txt -r hob064.rule`
3. ...

### 4. Processing the passwords
### 3. Processing the passwords
Now we have cracked a bunch of hashes, let's load them in to cracke-dit!

1. `python cracke-dit.py --pot <domain>.pot --domain <domain>`. Optionally pass in `--only-users` or `--only-enabled` - hopefully they are self explanatory.
Expand All @@ -57,7 +70,7 @@ Using the ntds.dit and SYSTEM in `./samples` we get the following output:
2. Password scores are based on [Dropbox's zxcvbn](https://github.com/dropbox/zxcvbn):

| Score | Description | Guesses |
|------:|:----------------------| -----:|
|------:|:----------------------| :-----|
| 0 | **Too guessable**: risky password. | < 10^3 |
| 1 | **Very guessable**: protection from throttled online attacks. | < 10^6 |
| 2 | **Somewhat guessable**: protection from unthrottled online attacks. | < 10^8 |
Expand Down Expand Up @@ -90,4 +103,9 @@ Check out the [issue tracker](https://github.com/eth0izzle/cracke-dit/issues) an

## License

MIT. See [LICENSE]
cracke-dit is under MIT. See [LICENSE](LICENSE)
Impacket is under a slightly modified version of the Apache Software License. See [LICENSE](impacket/LICENSE)

## Credits

Huge thanks [CoreSecurity's Impacket](https://github.com/CoreSecurity/impacket)!
16 changes: 10 additions & 6 deletions cracke-dit.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
group = parser.add_argument_group("1. Cracking", "cracke-dit can take your raw ntds.dit and SYSTEM hive "
"and turn them in to a user:hash file for cracking "
"within your favourite password cracker")
group.add_argument("--system", action="store", help="SYSTEM hive to parse")
group.add_argument("--ntds", action="store", help="ntds.dit file to parse")
group.add_argument("--system", action="store", help="(local) SYSTEM hive to parse")
group.add_argument("--ntds", action="store", help="(local) ntds.dit file to parse")
group.add_argument("--username", action="store", help="(remote) Domain Admin username to connect to the target")
group.add_argument("--password", action="store", help="(remote) Domain Admin username to connect to the target")
group.add_argument("--target", action="store", help="(remote) IP address of the Domain Contoller to connect to")
group.add_argument("--out", action="store", help="File to write user:hash to")
group.add_argument("--no-history", action="store_false", dest="historic", default=True,
help="Set to disable historic password processing. Will speed up significantly.")
Expand All @@ -43,8 +46,11 @@
help="Output module to visualise the data: %s " % available_outputs)
args = parser.parse_args()

if args.system and args.ntds:
domain, hashes = ntds.process(args.system, args.ntds, args.historic)
local = (args.system and args.ntds)
remote = (args.username and args.password and args.target)

if local or remote:
domain, hashes = ntds.process_local(args.system, args.ntds, args.historic) if local else ntds.process_remote(args.username, args.password, args.target, args.historic)
ntlm_file = args.out or "{0}.hashes.ntlm".format(domain)

with HashDatabase(args.database_name, domain, raise_if_table_doesnt_exist=False) as db:
Expand Down Expand Up @@ -80,5 +86,3 @@ def update(stopper):
spinner.join()
else:
parser.print_help()


84 changes: 84 additions & 0 deletions impacket/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Licencing
---------

We provide this software under a slightly modified version of the
Apache Software License. The only changes to the document were the
replacement of "Apache" with "Impacket" and "Apache Software Foundation"
with "CORE Security Technologies". Feel free to compare the resulting
document to the official Apache license.

The `Apache Software License' is an Open Source Initiative Approved
License.


The Apache Software License, Version 1.1
Modifications by CORE Security Technologies (see above)

Copyright (c) 2000 The Apache Software Foundation. All rights
reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.

3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:
"This product includes software developed by
CORE Security Technologies (http://www.coresecurity.com/)."
Alternately, this acknowledgment may appear in the software itself,
if and wherever such third-party acknowledgments normally appear.

4. The names "Impacket" and "CORE Security Technologies" must
not be used to endorse or promote products derived from this
software without prior written permission. For written
permission, please contact oss@coresecurity.com.

5. Products derived from this software may not be called "Impacket",
nor may "Impacket" appear in their name, without prior written
permission of CORE Security Technologies.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.



Smb.py and nmb.py are based on Pysmb by Michael Teo
(http://miketeo.net/projects/pysmb/), and are distributed under the
following license:

This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.

3. This notice cannot be removed or altered from any source
distribution.
49 changes: 42 additions & 7 deletions ntds_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,43 @@
from binascii import hexlify
from threading import Thread, Event

from impacket.examples.secretsdump import LocalOperations, NTDSHashes
from impacket.examples.secretsdump import LocalOperations, RemoteOperations, NTDSHashes
from impacket.smbconnection import SMBConnection, SessionError
from socket import error as socket_error


def process(system, ntds, historic):
def process_remote(username, password, target, historic):
hashes = list()

print("Attempting to connect to {}...".format(target))
try:
connection = SMBConnection(target, target)
connection.login(username, password, "", "", "")

ops = RemoteOperations(connection, False, None)
ops.setExecMethod("smbexec")

stopper = Event()
spinner = Thread(target=__update, args=(stopper, hashes))
spinner.start()
NTDSHashes(None, None, isRemote=True, remoteOps=ops, noLMHash=True, useVSSMethod=False,
justNTLM=True, printUserStatus=True, history=historic,
perSecretCallback=lambda type, secret: hashes.append(__process_hash(secret))).dump()
stopper.set()
spinner.join()

if len(hashes) == 0:
raise Exception("Extraction seemingly finished successfully but I didn't find any hashes...")

return __get_domain(hashes), hashes
except socket_error:
raise Exception("Failed to connect to {}".format(target))
except SessionError as e:
if e.error == 3221225581:
raise Exception("Username or password incorrect - please try again.")


def process_local(system, ntds, historic):
hashes = list()

print("Attempting to grab decryption key...")
Expand All @@ -18,7 +51,7 @@ def process(system, ntds, historic):

print("Found key: 0x{0}.".format(hexlify(bootKey)))
stopper = Event()
spinner = Thread(target=update, args=(stopper, hashes))
spinner = Thread(target=__update, args=(stopper, hashes))
spinner.start()
NTDSHashes(ntds, bootKey, noLMHash=ops.checkNoLMHashPolicy(), useVSSMethod=True, justNTLM=True,
printUserStatus=True, history=historic,
Expand All @@ -27,9 +60,7 @@ def process(system, ntds, historic):
stopper.set()
spinner.join()

domain = hashes[-1]["username"].split("\\")[0]

return domain, hashes
return __get_domain(hashes), hashes


def __process_hash(hash):
Expand All @@ -44,7 +75,11 @@ def __process_hash(hash):
return {"username": user, "ntlmhash": nthash, "password": None, "enabled": True if enabled == "Enabled" else False}


def update(stopper, hashes):
def __get_domain(hashes):
return [hash["username"].split("\\")[0] for hash in hashes if "\\" in hash["username"]][0]


def __update(stopper, hashes):
spinner = itertools.cycle(['-', '/', '|', '\\'])

while not stopper.is_set():
Expand Down

0 comments on commit 7aa5e9a

Please sign in to comment.