Attachment 'recoverPasswords.pl'

Download

   1 #!/usr/bin/perl
   2 use strict;
   3 use warnings;
   4 use utf8;
   5 use Net::LDAP;	#Debian package: libnet-ldap-perl
   6 use Crypt::SmbHash qw(lmhash nthash); #Debian package: libcrypt-smbhash-perl 
   7 #use Time::HiRes;
   8 #use Data::Dumper;
   9 
  10 
  11 # YOU NEED TO INSTALL THE FOLLOWING PACKAGES (Debian or ubuntu)
  12 # libnet-ldap-perl libcrypt-smbhash-perl 
  13 
  14 
  15 
  16 my $pwdumpfile = 'pwdump.txt';          # This file can be load in ophcrack
  17 my $ophcrackfile = 'ophcrack.txt';      # This file should hold the results of ophcrack
  18 my $csv = 'users';                      # präfix for csv-files
  19 my $seperator = ",";                    # Seperator for csv-file
  20 my $ldapurl = "ldap";                   # ldap-hostname or IP-address.
  21 my $base = "dc=skole,dc=skolelinux,dc=no";	# The LDAP-base
  22 my $ldapUsers = "ou=People,$base";      # LDAP-users
  23 my $ldapGroups = "ou=Group,$base";      # LDAP-groups
  24 my $pass = 'secret';                    # The LDAP-admin-Password
  25 my $templatename = "newstudent";
  26 my $useTemplateFromFunction =0;        # If 1, the returnvalue of the function template (see below) is used you can fit this function to your needs
  27 my $checkAgain = 1;						# check every password found by ophcrack again (1: yes, 0: no)
  28 										
  29 #my $one = [Time::HiRes::gettimeofday];
  30 
  31 
  32 my $ldap;
  33 my @result;
  34 
  35 
  36 ####################
  37 #Handle Parameters #
  38 ####################
  39 
  40 #No Parameter
  41 if(!defined $ARGV[0] or $ARGV[0] eq '-h' or ($ARGV[0] ne 'pwdump' and $ARGV[0] ne 'csv' and $ARGV[0] ne 'check')){
  42 	usage();
  43 	exit 0;
  44 }
  45 
  46 #pwdump
  47 if($ARGV[0] eq "pwdump"){
  48 	#(&(uid=*)(objectClass=posixAccount)(objectClass=imapUser))
  49 	#(&(uid=*)(objectClass=posixAccount)(!(homeDirectory=/dev/null)))
  50 	my $searchFor = '(&(uid=*)(objectClass=posixAccount)(objectClass=imapUser))';
  51 	my @attrs = ('userPassword','sambaLMPassword','sambaNTPassword','cn','uid','gidNumber');
  52 	@result = search($searchFor, \@attrs, $ldapUsers);
  53 	buildPwdumpFile(@result);
  54 	print "Generated file '$pwdumpfile'.\n";
  55 	#print "Lasted ".(Time::HiRes::tv_interval ($one, [Time::HiRes::gettimeofday]))." seconds.\n";
  56 	exit 0;
  57 }
  58 
  59 
  60 # csv
  61 my %users;
  62 if($ARGV[0] eq "csv"){
  63 	# Faster way to find groups for all users than calling findGroupsOfUser
  64 	my $searchFor = '(&(cn=*)(objectClass=posixGroup)(!(description=* personal group)))';
  65 	my @attrs = ('memberUid','cn');
  66 	my @result = search($searchFor, \@attrs, $ldapGroups);
  67 	my $entry;
  68 	foreach $entry (@result){
  69 		my $cn = $entry->get_value ('cn');
  70 		my @members = $entry->get_value ('memberUID');
  71 		my $member;
  72 		foreach $member (@members){
  73 			$users{$member}{groups} = (exists $users{$member}{groups})?$users{$member}{groups}.$seperator.$cn: $cn;
  74 		}
  75 		
  76 	}
  77 
  78 	# Passwords from ophcrack output
  79 	open(OPHCRACK,'<'.$ophcrackfile) || die "Can't open file $ophcrackfile: $!. Did you run ophcrack?\n";
  80 	my @lines= <OPHCRACK>;
  81 	close(OPHCRACK);
  82 	chomp(@lines); # remove \n
  83 	my $line;
  84 	foreach $line (@lines){
  85 		my @items = split(/:/,$line);
  86 		$items[5] = "" if(!defined $items[5]); # beware no LM Pwd 2 and NT-password
  87 		#$users{$items[0]}{groups} = findGroupsOfUser($items[0]); # done faster see above
  88 		if(!defined $items[6]){
  89 			$users{$items[0]}{password} = testNThash($items[4].$items[5],$items[3]);
  90 		}else{
  91 			$users{$items[0]}{password} = $items[6];
  92 		}
  93 	}
  94 
  95 	# cn, homedir, ... for users
  96 	$searchFor = '(&(uid=*)(objectClass=posixAccount)(objectClass=imapUser))';
  97 	@attrs = ('sambaNTPassword','sambaLMPassword','cn','uid','homeDirectory','uidNumber','gidNumber');
  98 	@result = search($searchFor, \@attrs, $ldapUsers);
  99 	my %templates;
 100 	my $count = 0;
 101 	foreach $entry (@result){
 102 		# Get attributes
 103 		my $nt = $entry->get_value ('sambaNTPassword');
 104 		my $uid = $entry->get_value ('uid');
 105 		my $home = $entry->get_value ('homeDirectory');
 106 		my $cn = $entry->get_value ('cn');
 107 		my ($firstname,$surname) =	getFirstLastNameFromCN($cn);
 108 		# user defined
 109 		if(!defined $users{$uid}){
 110 			print "User '$uid' not in ldap. Skipping this user.\n";
 111 			next;
 112 		}
 113 		my $template = $templatename;
 114 		if($useTemplateFromFunction == 1){
 115 			$template = template($uid,$home,$cn,$entry->get_value ('uid'),$entry->get_value ('gidNumber'),$users{$uid}{groups});			
 116 		}
 117 		# Got password from ophcrack?
 118 		if(!defined $users{$uid}{password}){
 119 			$users{$uid}{reason} = "No password. Check ophcrack";
 120 			print $users{$uid}{reason}." for user '$uid'. Skipping this user.\n";
 121 			$users{$uid}{nthash} = $nt;
 122 			$users{$uid}{lmhash} = $entry->get_value ('sambaLMPassword');
 123 			$users{$uid}{template} = $template;
 124 			$users{$uid}{surname} = $surname;
 125 			$users{$uid}{firstname} = $firstname;
 126 			$users{$uid}{cn} = $cn;
 127 			$users{$uid}{home} = $home;
 128 			next;
 129 		}
 130 		# Passwordcheck enabled and passwordcheck failed?
 131 		if($checkAgain == 1 and ($nt ne nthash($users{$uid}{password}))){
 132 			$users{$uid}{reason} = "Passwordcheck failed";
 133 			print $users{$uid}{reason}." for user '$uid'. Skipping this user.\n";
 134 			$users{$uid}{nthash} = $nt;
 135 			$users{$uid}{lmhash} = $entry->get_value ('sambaLMPassword');
 136 			$users{$uid}{template} = $template;
 137 			$users{$uid}{surname} = $surname;
 138 			$users{$uid}{firstname} = $firstname;
 139 			$users{$uid}{cn} = $cn;
 140 			$users{$uid}{home} = $home;
 141 			next;
 142 		}
 143 		# build csv-file-entry. In %users we have something like %users{username} => [password => <the password>, groups => <the groups>]
 144 		my $csvline = oneLineForCSV($uid,$firstname,$surname,$users{$uid}{password},$users{$uid}{groups});
 145 		# each key in %templates is associated to an array which holds the csv-file-entries 
 146 		push (@{$templates{$template}},$csvline);
 147 		$count++;
 148 		delete $users{$uid};
 149 		
 150 		
 151 	}
 152 	print "-------------\n";
 153 	my $template;
 154 	foreach $template (keys %templates){
 155 		my @csvcontent = @{$templates{$template}};
 156 		my $file = $csv."_".$template.".csv";
 157 		print "Generate file $file for users with template '$template' (".(scalar @csvcontent)." users).\n";
 158 		open(CSV,'>'.$file) || die "Can not write file $file: $! \n";
 159 		my $output = join("\n",@csvcontent );
 160 		print CSV $output;
 161 		close(CSV);
 162 	}
 163 
 164 	# in %users only skiped users left.
 165 	if(%users){
 166 		print "Skiped ".(scalar keys %users)." users. ";
 167 		my $user;
 168 		my $template = $templatename;
 169 		my $file = "$csv.skiped";
 170 		open(SKIPED,'>'.$file) || die "Can not write file $file: $! \n";
 171 		foreach $user (keys %users){			
 172 			print SKIPED "Username:\t".$user."\n";
 173 			print SKIPED "Reason:\t\t".$users{$user}{reason}."\n";
 174 			print SKIPED "Firstname:\t".$users{$user}{firstname}."\n";
 175 			print SKIPED "Surname:\t".$users{$user}{surname}."\n";
 176 			print SKIPED "Fullname:\t".$users{$user}{cn}."\n";
 177 			print SKIPED "Password:\t";
 178 			print SKIPED $users{$user}{password} if(defined $users{$user}{password});
 179 			print SKIPED "\n";
 180 			print SKIPED "Homedir:\t".$users{$user}{home}."\n";
 181 			print SKIPED "Template:\t".$users{$user}{template}."\n";
 182 			print SKIPED "LM-hash:\t".$users{$user}{lmhash}."\n";
 183 			print SKIPED "NT-hash:\t".$users{$user}{nthash}."\n";
 184 			print SKIPED "-------------------------------\n";
 185 		}	
 186 		close SKIPED;
 187 		print "Wrote them to file '$file'.\n";
 188 	}
 189 	#print "Lasted ".(Time::HiRes::tv_interval ($one, [Time::HiRes::gettimeofday]))." seconds.\n";
 190 	#print Dumper %users;
 191 	exit 0;
 192 }
 193 
 194 #check <username> <password>
 195 if($ARGV[0] eq 'check' and defined $ARGV[1] and defined $ARGV[2]){
 196 	my $searchFor = "(&(uid=".$ARGV[1].")(objectClass=posixAccount)(objectClass=imapUser))";
 197 	my @attrs = ('sambaNTPassword','cn','uid','homeDirectory','gidNumber', 'uidNumber');
 198 	@result = search($searchFor, \@attrs, $ldapUsers);
 199 	if(@result){
 200 		my $nt = $result[0]->get_value ('sambaNTPassword');
 201 		# Test password
 202 		my $password = testNThash($ARGV[2], $nt);
 203 		defined $password or die "Passwort don't match the NT-hash!\n";
 204 		# Generate csv-file-entry
 205 		my $username = $result[0]->get_value ('uid');
 206 		my $cn = $result[0]->get_value ('cn');
 207 		my $home = $result[0]->get_value ('homeDirectory');
 208 		my ($firstname,$surname) =	getFirstLastNameFromCN($cn);
 209 		my $groups = findGroupsOfUser($ARGV[1]);
 210 		my $template = ($useTemplateFromFunction == 1)?template($username,$home,$cn,$result[0]->get_value ('uidNumber'),$result[0]->get_value ('gidNumber'),$groups):$templatename;
 211 		#build output
 212 		my $csvline = oneLineForCSV($username,$firstname,$surname,$password,$groups);
 213 		print "Real password is: $password\n";
 214 		print "You can use the following line in csv-file (Template: $template).\n";
 215 		print "$csvline\n";
 216 	}else{
 217 		print "User not found in LDAP (entry uid=".$ARGV[1]." ou=People,$base does not exist).\n";
 218 		#print "Lasted ".(Time::HiRes::tv_interval ($one, [Time::HiRes::gettimeofday]))." seconds.\n";
 219 		exit 0;
 220 	}
 221 }else{
 222 	usage();
 223 }
 224 
 225 
 226 
 227 # Connect to ldap an Initialize an ldap search
 228 #
 229 # parameter 1: the searchstring
 230 # parameter 2: Attributes to get from LDAP
 231 # parameter 3: LDAP-base
 232 #
 233 # returns searchresult (as array)
 234 sub search{
 235 	# Connect to LDAP
 236 	$ldap = Net::LDAP->new ( $ldapurl ) or die "$@ Can't connect to ldap.\n";
 237 	$ldap->start_tls(verify => 'none',  );
 238 	my $mesg = $ldap->bind ( "cn=admin,$ldapUsers",
 239 								password => $pass,
 240 								version => 3 ); 
 241 	# LDAP-search
 242 	my $result = LDAPsearch( $ldap, $_[0], $_[1], $_[2] );
 243 	my @entries = $result->entries;
 244 	return @entries;
 245 }
 246 
 247 
 248 # Performs an ldap search specified by the given parameters
 249 #
 250 # parameter 1: ldap object
 251 # parameter 2: searchstring
 252 # parameter 3: Attributes to get from LDAP
 253 # parameter 4: LDAP-base
 254 sub LDAPsearch
 255  {
 256    my ($ldap,$searchString,$attrs,$base) = @_;
 257    my $result = $ldap->search ( base    => "$base",
 258                                 scope   => "sub",
 259                                 filter  => "$searchString",
 260                                 attrs   =>  $attrs
 261                               );
 262 
 263 }
 264 
 265 
 266 # builds a file using the pwdump-Syntax
 267 #
 268 # parmater 1: results of an ldap-search
 269 sub buildPwdumpFile{
 270 	my @entries = @_;
 271 	my $entry;
 272 	open(PWDUMP,'>'.$pwdumpfile);
 273 	foreach $entry(@entries){
 274 		my $lm = $entry->get_value ('sambaLMPassword');
 275 		my $nt = $entry->get_value ('sambaNTPassword');
 276 		my $uid = $entry->get_value ('uid');
 277 		my $gid = $entry->get_value ('gidNumber');
 278 		print PWDUMP $uid.":".$gid.":".$lm.":".$nt.":::\n";
 279 	}
 280 	close(PWDUMP);
 281 }
 282 
 283 
 284 # get first- and surname from cn (parameter) (looks like 'firstname <space> surname')
 285 sub getFirstLastNameFromCN{
 286 	my @cnSplit=split(/ /,$_[0]);
 287 	my $surname = ($#cnSplit > 0)?pop(@cnSplit):"";# If there was no space in $_[0]
 288 	my $firstname = join(" ",@cnSplit); # Maybe more than one space
 289 	return ($firstname, $surname);
 290 }
 291 
 292 
 293 # Performs an LDAP-search for the groups of one user.
 294 #
 295 # parameter 1: username
 296 #
 297 # returns: groups as string seperate by $seperator
 298 sub findGroupsOfUser{
 299 	my $searchFor = "(&(cn=*)(objectClass=posixGroup)(memberUid=".$_[0].")(!(description=* personal group)))";
 300 	my @attrs = ('cn');
 301 	my @groupsearch = search($searchFor, \@attrs, $ldapGroups);
 302 	my $entry;
 303 	my $groups="";
 304 	foreach $entry (@groupsearch){
 305 		$groups = ($groups eq "")?$entry->get_value ('cn'):$groups.$seperator.$entry->get_value ('cn');
 306 	}
 307 	return $groups;	
 308 }
 309 
 310 
 311 # Concats parameters to use as csv-file-entry.
 312 sub oneLineForCSV{
 313 	my ($username,$firstname,$surname,$password,$groups) = @_;
 314 	return $username.$seperator.$firstname.$seperator.$surname.$seperator.$password.$seperator.$groups;
 315 }
 316 
 317 
 318 
 319 # Builds all uppercase - lowercase combinations, and test it against nt-hash.
 320 # If we have a match, it returns these combination
 321 #
 322 # parameter 1: string to permute
 323 # parameter 2: nt-hash
 324 #
 325 # returns: the permutation of parameter 1 which fits the nt-hash or undef
 326 sub testNThash{
 327 	# String to array
 328 	my @chars = split(//,$_[0]);
 329 	my $length =@chars;
 330 	# Number of all possibilities
 331 	my $n = 2** length($_[0]);
 332 	my $char;
 333 	for(my $i = 0; $i < $n; $i++) {
 334 		my $perm="";
 335 		# Build one permutation
 336 		for(my $j = 0; $j < $length; $j++){
 337 			# if the $j's bit of binary representation of $i is 1, 
 338 			# we take the uppercase letter for this permutation
 339 			if(($i >> $j & 1) != 0){
 340 				$char = uc $chars[$j];
 341 			}else{
 342 				$char = lc $chars[$j];
 343 			}
 344 			$perm = $perm.$char;
 345 		}
 346 		# test the nt-hash
 347 		if(nthash($perm) eq $_[1]){
 348 			return $perm;
 349 		}
 350 	}
 351 	return undef;
 352 }
 353 
 354 
 355 # Returns a string, which will be used as templatename (if $useTemplateFromFunction == 1)
 356 # In the default implementation it seperates the subfolder from /skole/tjener/home0 the users
 357 # homedirectory is located in.
 358 # Example: In /skole/tjener/home0 you have subfolders like 'year2011', 'year2012', 'year2013', ...
 359 #   Users who started in 2011 have their homedirs in the subfolder 'year2011'. In this case this 
 360 #   function will return the foldername year2011. If there is no subfolder, it will return 'teachers'.
 361 #
 362 # parameter 1: username (uid)
 363 # parameter 2: homedirectory (like /skole/tjener/home0/year2011/joe)
 364 # parameter 3: cn
 365 # parameter 4: uidNumber
 366 # parameter 5: gidNumber
 367 # parameter 6: Groups of this user, seperated by $seperator
 368 sub template{
 369 	my($user,$home,$cn,$uid,$gid,$groups) = @_;
 370 	my $skole = '/skole/tjener/home0/';
 371 	my $template = $home;
 372 	$template =~ s/$skole//; # remove path 
 373 	$template =~ s/$user//;	# remove username from homedir
 374 	$template =~ s/\/$//; # remove slash
 375 	if($template ne ""){
 376 		return $template; 
 377 	}else{	
 378 		return "teachers";
 379 	}
 380 
 381 }
 382 
 383 # Print little help
 384 sub usage{
 385 	print "\n$0 - get clear text passwords from (skolelinux)-ldap\n\n";
 386 	print "1. First run '$0 pwdump' to generate an pwdump-file.\n\n";
 387 	print "2. Then run ophcrack (http://ophcrack.sourceforge.net/). Check if you have suitable rainbow tables (Download here: http://ophcrack.sourceforge.net/tables.php). Load the file generated in step 1 or run 'ophcrack -f $pwdumpfile', then hit 'crack'.\n\n";
 388 	print "3. Save results from ophcrack as file (Save -> Save to file) with filename $ophcrackfile. Then run '$0 csv'.\n\n";
 389 	print "4. Step 3 generates some csv-files. You can import this csv-files to ldap, e. g. by using gosa2.\n\n\n";
 390 	print "If there were a problem for some users, you'll be find this users in file '$csv.skiped'.\nExample: If the password is longer than 14 characters, ophcrack only finds the first 14 characters. If you have an idea of the missing characters, you can run '$0 check <username> <cleartext password>'. This will check all upper-lowercase-combinations of the password against the nt-hash. If it matches, you will get an output which you can use in a csv-file.\n\n";
 391 
 392 	print "USAGE:\n";
 393 	print "\t To get the pwdump-file:\n";
 394 	print "\t\t $0 pwdump\n\n";
 395 	print "\t To get the csv-file for import:\n";
 396 	print "\t\t $0 csv \n\n";
 397 	print "\t To check a password against NT-hash (sambaNTpassword):\n";
 398 	print "\t\t $0 check <username> <cleartext password> \n\n";
 399 	print "columns in *.csv-files are: ";
 400 	print "username".$seperator."firstname".$seperator."surname".$seperator."password".$seperator."groups\n\n";
 401 }

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.