01 Aug 2011
Password cracking comes in two main flavors, offline and online. With offline password cracking you are trying to crack a list of password hashes stored locally and you can try passwords as fast as your CPU/GPU and storage will allow. The speed of offline password cracking allows you to try billions of passwords in a reasonable amount of time. With online password cracking you are trying to log into a service either locally or remotely and have to wait for the service to respond. The speed of online password cracking is determined by the resources of the service and the speed of the network. Trying billions of passwords with online password cracking is not feasible. Understanding the password policy of the service and including only passwords that meet the policy requirements will prevent you from wasting time.
Creating a Password List
The first step to creating passwords for online password cracking is to find good word lists and a tool to mangle those words. You can find good word lists at Skull Security and Korelogic. To mangle the word lists, I use hashcat. Use the following command to create passwords from a word list using the best64 mangling rules:
hashcat-cli32.exe wordlist.txt -r rules/best64.rule --stdout > passwords.txt
The next step is to find only the passwords that meet specific criteria such as a having a minimum or maximum length or meeting the Windows complexity requirements.
Filtering the Passwords
Typical password policies specify a minimum length, may specify a maximum length, and may specify whether upper and lower case letters, digits, or symbols are required. With Web-based services the password requirements vary widely and can usually be found on the registration page. Windows computers specify a minimum length and may require complexity. If complexity is required the password must use three of the four groups: lowercase letters, uppercase letters, digits, and symbols. If complexity is not required then the password only has to meet the minimum length.
Passfilter.py is a python script that can be used to read a list of words from a file or stdin and print only the words that meet the defined password policy. You can specify the minimum and maximum length, which character groups are required, and the list of symbols that can be used. The usage for Passfilter.py is printed below:
usage: Passfilter.py [-h] [-w | -r string] [-m min] [-x max] [-s symbols]
[-f wordlist]
Passfilter.py reads a file or stdin and returns words that meet the
defined requirements. For most password policies the set of allowed letters
and numbers is the same. The set of allowed symbols varies widely between
policies. Passfilter.py defines a default set of symbols which can be
overridden using the -s flag.
Examples:
Return all words 3 to 10 characters long.
passfilter.py wordlist
Return all words 3 to 10 characters long that meet the windows complexity
requirements.
passfilter.py -w wordlist
Return all words 5 to 9 characters long that have at least two lowercase
letters and at least one digit.
passfilter.py -m 5 -x 9 -r lld wordlist
optional arguments:
-h, --help show this help message and exit
-w Passwords must meet Windows complexity requirements.
-r string String representing the character groups and count required.
-m min Minimum password length. (default: 3)
-x max Maximum password length. (default: 10)
-s symbols Symbols allowed in the password. (default:
!"#$%&'()*+,-./:;?@[\]^_`{|}~)
-f wordlist Wordlist to parse (default: stdin).
You can download Passfilter.py here.
15 Jul 2011
Obtaining Domain Admin Access Using Psexec
On a recent pentest, one of the goals was to gain domain admin access to the network. If you can compromise a domain controller you can use the commands net user username password /ADD /DOMAIN
and net group "Domain Admins" username /ADD /DOMAIN
. I found the domain controllers but was unable to identify a suitable vulnerability that would allow me to access them. However, I found another box on the domain that was vulnerable to MS08-067 (yes, there are still boxes that have not been patched for this vulnerability). Using metasploit I was able to get a meterpreter shell on this box. I then used the ps command to find out what processes were running. I found a few processes running under the administrator account for the domain. Next, I migrated to one of the processes and used the upload command in meterpreter to upload psexec to the box. Meterpreter uploads files to the default path for the user. Next, I used the shell command to drop into a shell and ran the commands psexec \\nameofdc net user username password /ADD /DOMAIN
and psexec \\nameofdc net group "Domain Admins" username /ADD /DOMAIN
. I now had my own user account with domain admin credentials.
A Few Notes
- If you are using BT5 you can find psexec in the
/pentest/windows-binaries/pstools
folder.
- You can run the
net user
and net group
commands from any process running under any domain admin account. To identify the domain admins you can use the enum_domain_group_users post module. You need to background your meterpreter session and type use post/windows/gather/enum_domain_group_users
. Then set the group name, set GROUP "Domain Admins"
, and session set SESSION 1
. Then run it. You will get a list of domain admin users.
- If you try to drop to a shell and get an error like this
stdapi_sys_process_execute: Operation failed: 1314
, do this instead execute -f cmd.exe -c -i -H
. This is a known issue in meterpreter.
09 Jun 2011
I have written a couple of post exploitation modules for Metasploit and have had fun doing it. I thought I would share a few tips for anyone else who wanted to write a post module.
Start with a Template
There are a number of good post exploitation modules available in the modules/post directory within the framework directory. Your best bet is to find one you like and then modify it. You need to pay particular attention to the initialize method of your post module. Here is an example:
def initialize(info={})
super( update_info( info,
'Name' => 'Gather Linux System Information Enumeration',
'Description' => %q{
This module gathers basic system information from Linux systems.
Enumerates users, hashes, services, network config, routing table, installed packages,
,screenshot, and bash_history
},
'License' => MSF_LICENSE,
'Author' =>
[
'Stephen Haywood ',
'sinn3r', #Modified the original, and more testing
'Carlos Perez ', # get_packages and get_services
],
'Version' => '$Revision: 12842 $',
'Platform' => [ 'linux' ],
'SessionTypes' => [ "shell" ]
))
register_options(
[
OptBool.new('VERBOSE', [false, 'Show detailed status messages', false]),
], self.class)
end
You need to modify the Name, Description, Author, Platform, and SessionTypes. You can also add options using the register_options method.
Registering Options
These are the options that are listed when you type show options
at the msfconsole prompt. If you look at the option definition the first argument is the name of the option. The second argument is an array of values, the first says whether the option is required, the second is the description of the option, the third is the default value of the option. You do not have to define a default value.
SessionTypes
After you exploit a box you will have one of two types of sessions, either a meterpreter session or a shell session. When you define your post module you need to determine whether it will work with meterpreter or shell sessions or both. Keep in mind that meterpreter is not stable for all platforms and shell is not as capable as meterpreter. This means your module may run in only one session type. The best thing to do is test your module.
Don't Reinvent the Wheel
If you look in the lib/msf/core/post directory in the framework directory you will find a number of ruby modules that define methods that can be used in your post module by using the require statement. Here is an example:
require 'msf/core/post/common'
require 'msf/core/post/file'
The common.rb file defines the cmd_exec method, which can be used to execute commands on your target whether you are using a meterpreter session or a shell session. The file.rb module defines methods to allow you to read, write, and append to files on the target machine. Use these methods.
Always Store your Loot
If your module is designed to gather data you should always store the data in the loot using the store_loot method. The store_loot method saves data in a unique file in your ~/.msf3/loot folder and adds indexing information to the metasploit database, if you are using it, to allow you to quickly find data stored in your loot folder. Here is a quick rundown of the store_loot method.
loot = store_loot(ltype, ctype, session, data, nil, info)
The ltype is the loot type, you can make this anything relevant. If you are gathering linux passwords it might be "linux.passwords". The ctype is the file or mime type. The session variable is already defined, it represents the session (meterpreter or shell) that you are currently in. The data variable holds the data you want to write to the file. Next is the filename, which you can leave as nil, and finally you have info, which is stored in the database and can be used to help you identify the data in the loot file. The store_loot method will return the full path of the loot file that was created.
Don't Forget to Run it
Metasploit uses the run method as the starting point to your module so make sure you define the run method. You can use as many other methods as you like but the run method is where you start.
This tutorial is over for now. Go have fun and create some cool post modules. One more thing, if you have questions the #metasploit channel on irc.freenode.net is an excellent place to ask.
27 May 2011
This is in response to @Wh1t3Rabbit's blog post Impending Doom and IT Security's Downward Spiral. I think he's on the right track but I have to take exception to one statement.
I think we need to train our IT Security people twice. First train them to be a business analyst and understand the corporate mindset, strategy and delivery model and only then can we train them to be good security people. Learning technology is easy ...applying it to the business is hard.
The problem is your are removing the responsibility for risk mitigation from the business owner and placing it on the IT security professional. IT security is just another business risk, the same as theft, fraud, physical disasters, poor marketing decisions and poor customer service. It is the responsibility of the business owner to protect the assets of the business and to ensure the business keeps running. While a competent IT security professional who understands the business is extremely valuable in helping the business owner understand the business risk associated with IT security and in developing a risk mitigation strategy, it is still the business owners responsibility to secure their data.
Until the business owners become educated on the IT security risks to their business and start developing strategies to mitigate those risks IT security professionals don't have a snowball's chance in hades of lifting us out of the spiral.
13 May 2011
Update: I wrote this article before I realized that ubuntu and therefore BT5 comes with a script called update-alternatives. The update-alternatives script allows you to switch between different versions of java and ruby. If you are having trouble running a ruby script in BT5 try running update-alternative --config ruby
and changing the ruby version. If that doesn't work then keep reading the rest of this article for other possible fixes.
I was playing around with BT5 today and noticed that it uses Ruby 1.9.2 as the default Ruby interpreter. Ruby 1.8 is installed as well if you would rather use it. To run a Ruby script using 1.8 just run ruby1.8 script.rb
. If you come across a Ruby-based tool in BT5 that does not work then try running it using ruby1.8 before troubleshooting. If you want to run the script under Ruby 1.9.2 you may run into some problems I have listed a few below and how to fix them.
Problem 1: Load rubygems Before Loading Installed Gems
Often times when running a Ruby script that requires gems Ruby has to be told to load 'rubygems' before it loads installed gems. This can be done either by adding require 'rubygems'
to each Ruby script before require
ing other gems or by setting the environment variable RUBYOPT. To set the environment variable add the line export RUBYOPT="rubygems"
to your .bashrc file in your home directory and then run source .bashrc
Problem 2: Tell Ruby Where to Find the Gems
For whatever reason Ruby cannot find the gems loaded for Ruby 1.9.2. To fix this set the environment variable GEM_HOME by adding the line export GEM_HOME=/var/lib/gems/1.9.2
to the .bashrc file in your home directory. You will need to run source .bashrc
for the changes to take effect.
Problem 3: You cannot Require Files in the Current Directory
For security reasons Ruby 1.9.2 does not search the current directory when looking for require
d files. There are three options for dealing with this:
- The easiest way to correct this is to change the line
require 'cewl_lib'
to require './cewl_lib'
, which defeats the purpose of not searching the current directory.
- Another method would be to move cewl_lib.rb to a lib folder and change
require 'cewl_lib'
to require_relative 'lib/cewl_lib'
. This is not backward compatible with Ruby 1.8.7
- A third option is to modify
require 'cewl_lib'
to require File.expand_path(File.join(File.dirname(__FILE__), 'cewl_lib'))
, which is backward compatible with Ruby 1.8.7. This is the method I used.