Codebase list enum4linux / dc0eb9c
New upstream version 0.9.1 Joseph O'Gorman 2 years ago
4 changed file(s) with 306 addition(s) and 140 deletion(s). Raw diff Collapse all Expand all
.enum4linux.pl.swp less more
Binary diff not shown
0 2021-06-28 Enum4Linux v0.9.1
1
2 * Cleaned up CHANGELOG.
3 * Thanks to DidierA.
4 * Added check for writable shares.
5 * Hid writable shares sheck being -A flag to ensure no change to default behaviour.
6 * Fixed check for writable shares to cope with Samba being chatty.
7
8 2021-04-10 Enum4Linux v0.9.0
9
10 * Thanks to jtpereyda, logicsec, mrefish, Kawsay, NoxNoctis,
11 BrashEndeavours, real-datagram, rastating.
12 * Added protocol mismatch detection.
13 * Cleaned up error messages.
14 * Attempts to guess domain based on 0.in-addr.arpa records
15 from default DNS server.
16 * Bug fix: Adjusted regexes to escape hyphens.
17 * Bug fix: Added error handling for smbclient yielding no OS
18 info.
19 * Updated with color coding and fixed globals to remove
20 errors.
21 * Bug fix: Fixed -a help typo.
22 * Bug fix: Misparsing when sharename IPC exists.
23 * Bug fix: Updated enum4linux.pl to better handle single
24 quote character in username and password.
25 * Bug fix: Updated issues with IPC not parsing correctly.
26 * Added handling of NT_STATUS_INVALID_PARAMETER when finding users.
27 * Added README.md.
28
029 2012-11-30 Enum4linux v0.8.9
130
231 * -d option decodes some of the acb_info field including
534 2011-06-16 Enum4linux v0.8.8
635
736 * Bug fix: -w option should now work better. Allows
8 domain crenedials to be used:
9 -w domain -u user -p pass ...
37 domain crenedials to be used:
38 -w domain -u user -p pass ...
1039
1140 2011-02-21 Enum4linux v0.8.7
1241
0 # enum4linux
1 A Linux alternative to enum.exe for enumerating data from Windows and Samba hosts.
2
3 Enum4linux is a tool for enumerating information from Windows and Samba systems. It attempts to offer similar functionality to enum.exe formerly available from www.bindview.com.
4
5 It is written in Perl and is basically a wrapper around the Samba tools smbclient, rpclient, net and nmblookup.
6
7 The tool usage can be found below followed by examples, previous versions of the tool can be found at the bottom of the page.
8
9 Also see: https://labs.portcullis.co.uk/tools/enum4linux/
4545 use File::Basename;
4646 use Data::Dumper;
4747 use Scalar::Util qw(tainted);
48
49 my $VERSION="0.8.9";
48 use Term::ANSIColor;
49
50 my $VERSION="0.9.1";
5051 my $verbose = 0;
5152 my $debug = 0;
53 my $aggressive = 0;
5254 my $global_fail_limit = 1000; # no command line option yet
5355 my $global_search_until_fail = 0; # no command line option yet
5456 my $heighest_rid = 999999;
55 my $global_workgroup = undef;
57 my $global_workgroup = '';
5658 my $global_username = '';
5759 my $global_password = '';
5860 my $global_dictionary = 0;
59 my $global_filename = undef;
60 my $global_share_file = undef;
61 my $global_filename = '';
62 my $global_share_file = '';
6163 my $global_detailed = 0;
6264 my $global_passpol = 0;
6365 my $global_rid_range = "500-550,1000-1050";
6466 my $global_known_username_string = "administrator,guest,krbtgt,domain admins,root,bin,none";
6567 my @dependent_programs = qw(nmblookup net rpcclient smbclient);
66 my @optional_dependent_programs = qw(polenum.py ldapsearch);
68 my @optional_dependent_programs = qw(polenum ldapsearch);
6769 my %odp_present = ();
6870 my $null_session_test = 0;
6971 my %opts;
155157
156158 Additional options:
157159 -a Do all simple enumeration (-U -S -G -P -r -o -n -i).
158 This opion is enabled if you don't provide any other options.
160 This option is enabled if you don't provide any other options.
159161 -h Display this help message and exit
160162 -r enumerate users via RID cycling
161163 -R range RID ranges to enumerate (default: $global_rid_range, implies -r)
172174 -w wrkg Specify workgroup manually (usually found automatically)
173175 -n Do an nmblookup (similar to nbtstat)
174176 -v Verbose. Shows full commands being run (net, rpcclient, etc.)
177 -A Aggressive. Do write checks on shares etc
175178
176179 RID cycling should extract a list of users from Windows \(or Samba\) hosts
177180 which have RestrictAnonymous set to 1 \(Windows NT and 2000\), or \"Network
208211 $ENV{'PATH'} =~ s/^\.://;
209212 $ENV{'PATH'} =~ s/:\.//;
210213
211 getopts('UMNSPGlLDu:dp:f:rR:s:k:vow:hnaiPK:', \%opts);
214 getopts('UMNSPGlLDu:dp:f:rR:s:k:vAow:hnaiPK:', \%opts);
212215
213216 # Print help message if required
214217 if ($opts{'h'}) {
218221
219222 # Read host and untaint
220223 my $global_target = shift or die $usage;
221 if ($global_target =~ /^([a-zA-Z0-9\._-]+)$/) {
224 if ($global_target =~ /^([a-zA-Z0-9\._\-]+)$/) {
222225 $global_target = $1;
223226 } else {
224227 print "ERROR: Target hostname \"$global_target\" contains some illegal characters\n";
258261 $global_known_username_string = $opts{'k'} if $opts{'k'};
259262 $global_workgroup = $opts{'w'} if $opts{'w'};
260263 $verbose = $opts{'v'} if $opts{'v'};
264 $aggressive = 1 if $opts{'A'};
261265 $opts{'r'} = 1 if $opts{'R'};
262266
263267 $global_search_until_fail = 1 if defined($opts{'K'});
273277 print "ERROR: $prog is not in your path. Check that samba package is installed\n";
274278 $dependency_error = 1;
275279 } else {
276 print "[V] Dependent program \"$prog\" found in $which_output\n" if $verbose;
280 print_verbose("Dependent program \"$prog\" found in $which_output\n") if $verbose;
277281 }
278282 }
279283 foreach my $prog (@optional_dependent_programs) {
283287 print "WARNING: $prog is not in your path. Check that package is installed and your PATH is sane.\n";
284288 $odp_present{$prog} = 0;
285289 } else {
286 print "[V] Dependent program \"$prog\" found in $which_output\n" if $verbose;
290 print_verbose("Dependent program \"$prog\" found in $which_output\n") if $verbose;
287291 $odp_present{$prog} = 1;
288292 }
289293 }
295299
296300 # Untaint workgroup if supplied on command line
297301 if (defined($global_workgroup)) {
298 if ($global_workgroup =~ /^([a-zA-Z0-9\.-_]*)$/) {
302 if ($global_workgroup =~ /^([a-zA-Z0-9\.\-_]*)$/) {
299303 $global_workgroup = $1;
300304 } else {
301 print "ERROR: Workgroup \"$global_workgroup\"contains some illegal characters\n";
305 print "ERROR: Workgroup \"$global_workgroup\" contains some illegal characters\n";
302306 exit 1;
303307 }
304308 }
308312 foreach my $known_username (@global_known_usernames) {
309313 $known_username =~ s/'/'\''/g; ($known_username) = $known_username =~ /(.*)/;
310314 }
311 $global_username =~ s/'/'\''/g; ($global_username) = $global_username =~ /(.*)/;
312 $global_password =~ s/'/'\''/g; ($global_password) = $global_password =~ /(.*)/;
315 $global_username =~ s/'/'\\''/g; ($global_username) = $global_username =~ /(.*)/;
316 $global_password =~ s/'/'\\''/g; ($global_password) = $global_password =~ /(.*)/;
313317
314318 # Output message about options used
315319 print "Starting enum4linux v$VERSION ( http://labs.portcullis.co.uk/application/enum4linux/ ) on " . scalar(localtime) . "\n";
356360 sub get_domain_sid {
357361 print_heading("Getting domain SID for $global_target");
358362 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' $global_target -c 'lsaquery' 2>&1";
359 print "[V] Attempting to get domain SID with command: $command\n" if $verbose;
363 print_verbose("Attempting to get domain SID with command: $command\n") if $verbose;
360364 my $domain_sid_text = `$command`;
361365 chomp $domain_sid_text;
362366 print $domain_sid_text;
363367 print "\n";
364368 if ($domain_sid_text =~ /Domain Sid: S-0-0/) {
365 print "[+] Host is part of a workgroup (not a domain)\n";
369 print_plus("Host is part of a workgroup (not a domain)\n");
366370 } elsif ($domain_sid_text =~ /Domain Sid: S-\d+-\d+-\d+-\d+-\d+-\d+/) {
367 print "[+] Host is part of a domain (not a workgroup)\n";
368 } else {
369 print "[+] Can't determine if host is part of domain or part of a workgroup\n";
371 print_plus("Host is part of a domain (not a workgroup)\n");
372 } else {
373 print_plus("Can't determine if host is part of domain or part of a workgroup\n");
370374 }
371375 }
372376
373377 # Get workgroup from nbstat info - we need this for lots of rpcclient calls
374378 sub get_workgroup {
375379 print_heading("Enumerating Workgroup/Domain on $global_target");
376 print "[V] Attempting to get domain name with command: nmblookup -A '$global_target'\n" if $verbose;
380 print_verbose("Attempting to get domain name with command: nmblookup -A '$global_target'\n") if $verbose;
377381
378382 # Workgroup might already be known - e.g. from command line or from get_os_info()
379383 unless ($global_workgroup) {
381385 $global_workgroup = `nmblookup -A '$global_target'`; # Global var. Erg!
382386 ($global_workgroup) = $global_workgroup =~ /\s+(\S+)\s+<00> - <GROUP>/s;
383387 unless (defined($global_workgroup)) {
384 print "[E] Can\'t find workgroup/domain\n";
385 print "\n";
386 return undef;
387 }
388 unless (defined($global_workgroup) and $global_workgroup =~ /^[A-Za-z0-9_\.-]+$/) {
389 print "ERROR: Workgroup \"$global_workgroup\"contains some illegal characters\n";
388 # dc.example.org. hostmaster.example.org. 1 900 600 86400 3600
389 $global_workgroup = `dig +short 0.in-addr.arpa`;
390 ($global_workgroup) = $global_workgroup =~ /.*\. hostmaster\.(.*?)\. .*/s;
391 if (defined($global_workgroup)) {
392 print "[+] Domain guessed: $global_workgroup\n";
393 } else {
394 $global_workgroup = "WORKGROUP";
395 print_error("Can\'t find workgroup/domain\n");
396 print "\n";
397 return;
398 }
399 }
400 unless (defined($global_workgroup) and $global_workgroup =~ /^[A-Za-z0-9_\.\-]+$/) {
401 print_error("Workgroup \"$global_workgroup\"contains some illegal characters\n");
390402 exit 1;
391403 }
392404 }
393 print "[+] Got domain/workgroup name: $global_workgroup\n";
405 print_plus("Got domain/workgroup name: $global_workgroup\n");
394406 }
395407
396408 # Get long domain name via LDAP
398410 sub get_ldapinfo {
399411 print_heading("Getting information via LDAP for $global_target");
400412 my $command = "ldapsearch -x -h '$global_target' -p 389 -s base namingContexts 2>&1";
401 print "[V] Attempting to long domain name: $command\n" if $verbose;
413 print_verbose("Attempting to long domain name: $command\n") if $verbose;
402414 unless ($odp_present{"ldapsearch"}) {
403 print "[E] Dependent program \"ldapsearch\" not present. Skipping this check. Install ldapsearch to fix.\n\n";
415 print_error("Dependent program \"ldapsearch\" not present. Skipping this check. Install ldapsearch to fix.\n\n");
404416 return 0;
405417 }
406418
407419 my $output = `$command`;
408420
409421 if ($output =~ /ldap_sasl_bind/) {
410 print "[E] Connection error\n";
422 print_error("Connection error\n");
411423 return 0;
412424 }
413425 my $parent = 0;
418430 my $long_domain = $1;
419431 $long_domain =~ s/DC=//g;
420432 $long_domain =~ s/,/./g;
421 print "[+] Long domain name for $global_target: $long_domain\n";
433 print_plus("Long domain name for $global_target: $long_domain\n");
422434 }
423435 }
424436
425437 if ($parent == 1) {
426 print "[+] $global_target appears to be a root/parent DC\n";
427 } else {
428 print "[+] $global_target appears to be a child DC\n";
438 print_plus("$global_target appears to be a root/parent DC\n");
439 } else {
440 print_plus("$global_target appears to be a child DC\n");
429441 }
430442
431443 }
434446 sub make_session {
435447 print_heading("Session Check on $global_target");
436448 my $command = "smbclient -W '$global_workgroup' //'$global_target'/ipc\$ -U'$global_username'\%'$global_password' -c 'help' 2>&1";
437 print "[V] Attempting to make null session using command: $command\n" if $verbose;
449 print_verbose("Attempting to make null session using command: $command\n") if $verbose;
438450 my $os_info = `$command`;
439451 chomp $os_info;
452 if ($os_info =~ /protocol negotiation failed: NT_STATUS_CONNECTION_RESET/) {
453 print_error("Protocol mismatch. smbclient doesn\'t support the same protocol versions as the server. You likely need to install a later version of Samba.\n");
454 }
440455 if ($os_info =~ /case_sensitive/) {
441 print "[+] Server $global_target allows sessions using username '$global_username', password '$global_password'\n";
442 } else {
443 print "[E] Server doesn't allow session using username '$global_username', password '$global_password'. Aborting remainder of tests.\n";
456 print_plus("Server $global_target allows sessions using username '$global_username', password '$global_password'\n");
457 } else {
458 print_error("Server doesn't allow session using username '$global_username', password '$global_password'. Aborting remainder of tests.\n");
444459 exit 1;
445460 }
446461
447462 # Use this info to set workgroup if possible
448463 unless ($global_workgroup) {
449464 ($global_workgroup) = $os_info =~ /Domain=\[([^]]*)\]/;
450 print "[+] Got domain/workgroup name: $global_workgroup\n";
465 print_plus("Got domain/workgroup name: $global_workgroup\n");
451466 }
452467 }
453468
454469 # Get OS info
455470 sub get_os_info {
456471 print_heading("OS information on $global_target");
472
473
457474 my $command = "smbclient -W '$global_workgroup' //'$global_target'/ipc\$ -U'$global_username'\%'$global_password' -c 'q' 2>&1";
458 print "[V] Attempting to get OS info with command: $command\n" if $verbose;
475 print_verbose("Attempting to get OS info with command: $command\n") if $verbose;
459476 my $os_info = `$command`;
460477 chomp $os_info;
478
461479 if (defined($os_info)) {
462 ($os_info) = $os_info =~ /(Domain=[^\n]+)/s;
463 print "[+] Got OS info for $global_target from smbclient: $os_info\n";
480 if ($os_info =~ /(Domain=[^\n]+)/s) {
481 ($os_info) = $os_info =~ /(Domain=[^\n]+)/s;
482 print_plus("Got OS info for $global_target from smbclient: ");
483 print "$os_info\n";
484 } else {
485 print_error("Can't get OS info with smbclient\n");
486 }
464487 }
465488
466489 $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'srvinfo' '$global_target' 2>&1";
467 print "[V] Attempting to get OS info with command: $command\n" if $verbose;
490 print_verbose("Attempting to get OS info with command: $command\n") if $verbose;
468491 $os_info = `$command`;
469492 if (defined($os_info)) {
470493 if ($os_info =~ /error: NT_STATUS_ACCESS_DENIED/) {
471 print "[E] Can't get OS info with srvinfo: NT_STATUS_ACCESS_DENIED\n";
472 } else {
473 print "[+] Got OS info for $global_target from srvinfo:\n$os_info";
494 print_error("Can't get OS info with srvinfo\n");
495 } else {
496 print_plus("Got OS info for $global_target from srvinfo: ");
497 print "$os_info\n";
474498 }
475499 }
476500 }
477501
478502 sub enum_password_policy {
479503 print_heading("Password Policy Information for $global_target");
480 my $command = "polenum.py '$global_username':'$global_password'\@'$global_target' 2>&1";
481 unless ($odp_present{"polenum.py"}) {
482 print "[E] Dependent program \"polenum.py\" not present. Skipping this check. Download polenum from http://labs.portcullis.co.uk/application/polenum/\n\n";
504 my $command = "polenum '$global_username':'$global_password'\@'$global_target' 2>&1";
505 unless ($odp_present{"polenum"}) {
506 print_error("Dependent program \"polenum\" not present. Skipping this check. Download polenum from http://labs.portcullis.co.uk/application/polenum/\n\n");
483507 return 0;
484508 }
485 print "[V] Attempting to get Password Policy info with command: $command\n" if $verbose;
509 print_verbose("Attempting to get Password Policy info with command: $command\n") if $verbose;
486510 my $passpol_info = `$command`;
487511 chomp $passpol_info;
488512 if (defined($passpol_info)) {
489513 if ($passpol_info =~ /Account Lockout Threshold/) {
490514 print $passpol_info;
491515 } elsif ($passpol_info =~ /Error Getting Password Policy: Connect error/) {
492 print "[E] Can't connect to host with supplied credentials.\n";
493 } else {
494 print "[E] Unexpected error from polenum.py:\n";
516 print_error("Can't connect to host with supplied credentials.\n");
517 } else {
518 print_error("Unexpected error from polenum:\n");
495519 print $passpol_info;
496520 }
497521 } else {
498 print "[E] polenum.py gave no output.\n";
522 print_error("polenum gave no output.\n");
499523 }
500524 $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c \"getdompwinfo\" 2>&1";
501 print "[V] Attempting to get Password Policy info with command: $command\n" if $verbose;
525 print_verbose("Attempting to get Password Policy info with command: $command\n") if $verbose;
502526 $passpol_info = `$command`;
503527 chomp $passpol_info;
504528 print "\n";
505529 if (defined($passpol_info) and $passpol_info !~ /ACCESS_DENIED/) {
506 print "[+] Retieved partial password policy with rpcclient:\n\n";
530 print_plus("Retieved partial password policy with rpcclient:\n\n");
507531 if ($passpol_info =~ /password_properties: 0x[0-9a-fA-F]{7}0/) {
508532 print "Password Complexity: Disabled\n";
509533 } elsif ($passpol_info =~ /password_properties: 0x[0-9a-fA-F]{7}1/) {
514538 print "Minimum Password Length: $minlen\n";
515539 }
516540 } else {
517 print "[E] Failed to get password policy with rpcclient\n";
541 print_error("Failed to get password policy with rpcclient\n");
518542 }
519543 print "\n";
520544 }
521545
522546 sub enum_lsa_policy {
523547 print_heading("LSA Policy Information on $global_target");
524 print "[E] Internal error. Not implmented in this version of enum4linux.\n";
548 print_error("Not implemented in this version of enum4linux.\n");
525549 }
526550
527551 sub enum_machines {
528552 print_heading("Machine Enumeration on $global_target");
529 print "[E] Internal error. Not implmented in this version of enum4linux.\n";
553 print_error("Not implemented in this version of enum4linux.\n");
530554 }
531555
532556 sub enum_names {
533557 print_heading("Name Enumeration on $global_target");
534 print "[E] Internal error. Not implmented in this version of enum4linux.\n";
558 print_error("Not implemented in this version of enum4linux.\n");
535559 }
536560
537561 sub enum_groups {
540564 # Get list of groups
541565 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c 'enumalsgroups $grouptype' 2>&1";
542566 if ($grouptype eq "domain") {
543 print "[V] Getting local groups with command: $command\n" if $verbose;
544 print "\n[+] Getting local groups:\n";
545 } else {
546 print "[V] Getting $grouptype groups with command: $command\n" if $verbose;
547 print "\n[+] Getting $grouptype groups:\n";
567 print_verbose("Getting local groups with command: $command\n") if $verbose;
568 print_plus(" Getting local groups:\n");
569 } else {
570 print_verbose("Getting $grouptype groups with command: $command\n") if $verbose;
571 print_plus("Getting $grouptype groups:\n");
548572 }
549573 my $groups_string = `$command`;
550574 if ($groups_string =~ /error: NT_STATUS_ACCESS_DENIED/) {
551575 if ($grouptype eq "domain") {
552 print "[E] Can't get local groups: NT_STATUS_ACCESS_DENIED\n";
576 print_error("Can't get local groups: NT_STATUS_ACCESS_DENIED\n");
553577 } else {
554 print "[E] Can't get $grouptype groups: NT_STATUS_ACCESS_DENIED\n";
578 print_error("Can't get $grouptype groups: NT_STATUS_ACCESS_DENIED\n");
555579 }
556580 } else {
557581 ($groups_string) = $groups_string =~ /(group:.*)/s;
562586 # Get group members
563587 my %rid_of_group = $groups_string =~ /\[([^\]]+)\]/sg;
564588 if ($grouptype eq "domain") {
565 print "\n[+] Getting local group memberships:\n";
566 } else {
567 print "\n[+] Getting $grouptype group memberships:\n";
589 print_plus(" Getting local group memberships:\n");
590 } else {
591 print_plus(" Getting $grouptype group memberships:\n");
568592 }
569593 foreach my $groupname (keys %rid_of_group) {
570594 $groupname =~ s/'/'\\''/g;
571595 $rid_of_group{$groupname} =~ s/^0x//;
572596 $rid_of_group{$groupname} = hex($rid_of_group{$groupname});
573597 $command = "net rpc group members '$groupname' -W '$global_workgroup' -I '$global_target' -U'$global_username'\%'$global_password' 2>&1\n";
574 print "[V] Running command: $command\n" if $verbose;
598 print_verbose("Running command: $command\n") if $verbose;
575599 my $members = `$command`;
576600 my @members = split "\n", $members;
577601 foreach my $m (@members) {
578 print "Group '$groupname' (RID: " . $rid_of_group{$groupname} . ") has member: $m\n";
602 print colored("Group: ", 'magenta');
603 print "$groupname' (RID: " . $rid_of_group{$groupname} . ") has member: $m\n";
579604 }
580605 }
581606 if ($global_detailed) {
582607 foreach my $groupname (keys %rid_of_group) {
583 print "[+] Getting detailed info for group $groupname (RID: " . $rid_of_group{$groupname} . ")\n";
608 print_plus("Getting detailed info for group $groupname (RID: " . $rid_of_group{$groupname} . ")\n");
584609 get_group_details_from_rid($rid_of_group{$groupname});
585610 }
586611 }
590615 sub enum_dom_groups {
591616 # Get list of groups
592617 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c \"enumdomgroups\" 2>&1";
593 print "[V] Getting domain groups with command: $command\n" if $verbose;
594 print "\n[+] Getting domain groups:\n";
618 print_verbose("Getting domain groups with command: $command\n") if $verbose;
619 print_plus(" Getting domain groups:\n");
595620
596621 my $groups_string = `$command`;
597622 if ($groups_string =~ /error: NT_STATUS_ACCESS_DENIED/) {
598 print "[E] Can't get domain groups: NT_STATUS_ACCESS_DENIED\n";
623 print_error("Can't get domain groups: NT_STATUS_ACCESS_DENIED\n");
599624 } else {
600625 ($groups_string) = $groups_string =~ /(group:.*)/s;
601626 $groups_string = "" unless defined($groups_string);
604629
605630 # Get group members
606631 my %rid_of_group = $groups_string =~ /\[([^\]]+)\]/sg;
607 print "\n[+] Getting domain group memberships:\n";
632 print_plus(" Getting domain group memberships:\n");
608633
609634 foreach my $groupname (keys %rid_of_group) {
610635 $groupname =~ s/'/'\\''/g;
611636 $rid_of_group{$groupname} =~ s/^0x//;
612637 $rid_of_group{$groupname} = hex($rid_of_group{$groupname});
613638 $command = "net rpc group members '$groupname' -W '$global_workgroup' -I '$global_target' -U'$global_username'\%'$global_password' 2>&1\n";
614 print "[V] Running command: $command\n" if $verbose;
639 print_verbose("Running command: $command\n") if $verbose;
615640 my $members = `$command`;
616641 my @members = split "\n", $members;
617642 foreach my $m (@members) {
618 print "Group '$groupname' (RID: " . $rid_of_group{$groupname} . ") has member: $m\n";
643 print colored("Group: ", 'magenta');
644 print "'$groupname' (RID: " . $rid_of_group{$groupname} . ") has member: $m\n";
619645 }
620646 }
621647 if ($global_detailed) {
622648 foreach my $groupname (keys %rid_of_group) {
623 print "[+] Getting detailed info for group $groupname (RID: " . $rid_of_group{$groupname} . ")\n";
649 print_plus("Getting detailed info for group $groupname (RID: " . $rid_of_group{$groupname} . ")\n");
624650 get_group_details_from_rid($rid_of_group{$groupname});
625651 }
626652 }
628654
629655 sub enum_groups_unauth {
630656 print_heading("Groups on $global_target via RID cycling");
631 print "[E] INTERNAL ERROR. Not implmented yet. Maybe in the next version.\n";
657 print_error("Not implemented in this version of enum4linux.\n");
632658 }
633659
634660 sub enum_shares {
635661 # Share enumeration
636662 print_heading("Share Enumeration on $global_target");
637 print "[V] Attempting to get share list using authentication\n" if $verbose;
663 print_verbose("Attempting to get share list using authentication\n") if $verbose;
638664 # my $shares = `net rpc share -W '$global_workgroup' -I '$global_target' -U'$global_username'\%'$global_password' 2>&1`;
639665 my $command = "smbclient -W '$global_workgroup' -L //'$global_target' -U'$global_username'\%'$global_password' 2>&1";
640666 my $shares = `$command`;
641667 if (defined($shares)) {
642668 if ($shares =~ /NT_STATUS_ACCESS_DENIED/) {
643 print "[E] Can't list shares: NT_STATUS_ACCESS_DENIED\n";
669 print_error("Can't list shares: NT_STATUS_ACCESS_DENIED\n");
644670 } else {
645671 print "$shares";
646672 }
647673 }
648674
649 print "\n[+] Attempting to map shares on $global_target\n";
650 my @shares = $shares =~ /\n\s+(\S+)\s+(?:Disk|IPC|Printer)/igs;
675 print_plus("Attempting to map shares on $global_target\n");
676 my @shares = $shares =~ /^[\t ]*?([ \S]+?)[\t ]*?(?:Disk|IPC|Printer)[^\n]*/gms;
651677 foreach my $share (@shares) {
678 my ($mapping_result, $listing_result, $writing_result) = ("N/A","N/A","N/A");
679
652680 $share =~ s/'/'\\''/g;
653681 my $command = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c dir 2>&1";
654 print "[V] Attempting map to share //$global_target/$share with command: $command\n" if $verbose;
682 print_verbose("Attempting map to share //$global_target/$share with command: $command\n") if $verbose;
655683 my $output = `$command`;
684
685 if ($output =~ /NT_STATUS_ACCESS_DENIED listing/ ||
686 $output =~ /do_list:.*NT_STATUS_ACCESS_DENIED/ ) {
687 $mapping_result="OK"; $listing_result="DENIED";
688 } elsif ($output =~ /tree connect failed: NT_STATUS_ACCESS_DENIED/) {
689 $mapping_result="DENIED"; $listing_result="N/A";
690 } elsif ($output =~ /\n\s+\.\.\s+D.*\d{4}\n/) {
691 $mapping_result="OK" ; $listing_result="OK";
692 } else {
693 print_error("Can't understand response:\n");
694 print $output;
695 }
696 if ($mapping_result eq "OK") {
697 if ($aggressive) {
698 print "testing write access " . $share . "\n";
699 # check for write access
700 my @chars = ("A".."Z", "a".."z", "0".."9");
701 my $random_string;
702 $random_string .= $chars[rand @chars] for 1..8;
703
704 $command = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c 'mkdir $random_string' 2>&1";
705 print_verbose("Checking write access to share //$global_target/$share with command: $command\n") if $verbose;
706 $output = `$command` ;
707 if ($output =~ /NT_STATUS_ACCESS_DENIED making/) {
708 $writing_result="DENIED" ;
709 } elsif (length $output) {
710 # the command should not give any output, if something was output maybe it's a failure
711 my $command2 = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c dir 2>&1";
712 print_verbose("Attempting check for directory $random_string on //$global_target/$share with command: $command2\n") if $verbose;
713 my $output2 = `$command2`;
714 if ($output2 =~ /.*$random_string.*/) {
715 $writing_result="OK";
716 } else {
717 print_error("Can't understand initial response:\n");
718 print $output;
719 print_error("Can't understand second response:\n");
720 print $output2;
721 }
722 } else {
723 $writing_result="OK";
724 }
725 if ($writing_result ne "DENIED") {
726 # remove the directory we created
727 $command = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c 'rmdir $random_string' 2>&1";
728 print_verbose("Removing created directory on share //$global_target/$share with command: $command\n") if $verbose;
729 $output=`$command` ;
730 if (length $output) {
731 print_error("rmdir command returned the following:\n");
732 print $output ;
733 }
734 }
735 }
736 }
737 # print results
656738 print "//$global_target/$share\t";
657 if ($output =~ /NT_STATUS_ACCESS_DENIED listing/) {
658 print "Mapping: OK\tListing: DENIED\n";
659 } elsif ($output =~ /tree connect failed: NT_STATUS_ACCESS_DENIED/) {
660 print "Mapping: DENIED, Listing: N/A\n";
661 } elsif ($output =~ /\n\s+\.\.\s+D.*\d{4}\n/) {
662 print "Mapping: OK, Listing: OK\n";
663 } else {
664 print "[E] Can't understand response:\n";
665 print $output;
666 }
739 print colored("Mapping: ", 'magenta');
740 print $mapping_result ;
741 print colored(" Listing: ", 'magenta');
742 print $listing_result ;
743 print colored(" Writing: ", 'magenta');
744 print $writing_result ;
745 print "\n" ;
667746 }
668747 }
669748
670749 sub enum_shares_unauth {
671750 print_heading("Brute Force Share Enumeration on $global_target");
672 print "[V] Attempting to get share list using bruteforcing\n" if $verbose;
751 print_verbose("Attempting to get share list using bruteforcing\n") if $verbose;
673752 my $shares_file = $global_share_file;
674753 open SHARES, "<$shares_file" or die "[E] Can't open share list file $shares_file: $!\n";
675754 my @shares = <SHARES>;
677756
678757 foreach my $share (@shares) {
679758 # Untaint $share
680 if ($share =~ /^([a-zA-Z0-9\._\$-]+)$/) {
759 if ($share =~ /^([a-zA-Z0-9\._\$\-]+)$/) {
681760 $share = $1;
682761 } else {
683 print "ERROR: Share name $share contains some illegal characters\n";
762 print_error("Share name $share contains some illegal characters\n");
684763 exit 1;
685764 }
686765
707786 # Get SID - try other known usernames if necessary
708787 foreach my $known_username (@global_known_usernames) {
709788 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c 'lookupnames $known_username' 2>&1";
710 print "[V] Attempting to get SID from $global_target with command: $command\n" if $verbose;
711 print "[V] Assuming that user \"$known_username\" exists\n" if $verbose;
789 print_verbose("Attempting to get SID from $global_target with command: $command\n") if $verbose;
790 print_verbose("Assuming that user \"$known_username\" exists\n") if $verbose;
712791 $logon = "username '$global_username', password '$global_password'";
713792 $sid = `$command`;
714793 if ($sid =~ /NT_STATUS_ACCESS_DENIED/) {
715 print "[E] Couldn't get SID: NT_STATUS_ACCESS_DENIED. RID cycling not possible.\n";
794 print_error("Couldn't get SID: NT_STATUS_ACCESS_DENIED. RID cycling not possible.\n");
716795 last;
717796 } elsif ($sid =~ /NT_STATUS_NONE_MAPPED/) {
718 print "[V] User \"$known_username\" doesn't exist. User enumeration should be possible, but SID needed...\n" if $verbose;
797 print_verbose("User \"$known_username\" doesn't exist. User enumeration should be possible, but SID needed...\n") if $verbose;
719798 next;
720799 } elsif ($sid =~ /S-1-5-21-[\d-]+-\d+\s+/) {
721800 ($cleansid) = $sid =~ /(S-1-5-21-[\d-]+)-\d+\s+/;
722 print "[I] Found new SID: $cleansid\n" unless defined($sids{$cleansid});
801 if (defined($sids{$cleansid})) {
802 print_info("Found new SID: ");
803 print "$cleansid\n";
804 }
723805 $sids{$cleansid} = 1;
724806 next;
725807 } elsif ($sid =~ /S-1-5-[\d-]+-\d+\s+/) {
726808 ($cleansid) = $sid =~ /(S-1-5-[\d-]+)-\d+\s+/;
727 print "[I] Found new SID: $cleansid\n" unless defined($sids{$cleansid});
809 if (defined($sids{$cleansid})) {
810 print_info("Found new SID: ");
811 print "$cleansid\n";
812 }
728813 $sids{$cleansid} = 1;
729814 next;
730815 } elsif ($sid =~ /S-1-22-[\d-]+-\d+\s+/) {
731816 ($cleansid) = $sid =~ /(S-1-22-[\d-]+)-\d+\s+/;
732 print "[I] Found new SID: $cleansid\n" unless defined($sids{$cleansid});
817 if (defined($sids{$cleansid})) {
818 print_info("Found new SID: ");
819 print "$cleansid\n";
820 }
733821 $sids{$cleansid} = 1;
734822 next;
735823 } else {
739827
740828 # Get some more SIDs (hopefully)
741829 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c lsaenumsid 2>&1";
742 print "[V] Attempting to get SIDs from $global_target with command: $command\n" if $verbose;
830 print_verbose("Attempting to get SIDs from $global_target with command: $command\n") if $verbose;
743831 my $sids = `$command`;
744832 foreach my $sid ($sids =~ /(S-[0-9-]+)/g) {
745 print "[V] Processing SID $sid\n" if $verbose;
833 print_verbose("Processing SID $sid\n") if $verbose;
746834 if ($sid =~ /NT_STATUS_ACCESS_DENIED/) {
747 print "[E] Couldn't get SID: NT_STATUS_ACCESS_DENIED. RID cycling not possible.\n";
835 print_error("Couldn't get SID: NT_STATUS_ACCESS_DENIED. RID cycling not possible.\n");
748836 next;
749837 } elsif ($sid =~ /S-1-5-21-[\d-]+-\d+/) {
750838 ($cleansid) = $sid =~ /(S-1-5-21-[\d-]+)-\d+/;
751 print "[I] Found new SID: $cleansid\n" unless defined($sids{$cleansid});
839 if (defined($sids{$cleansid})) {
840 print_info("Found new SID: ");
841 print "$cleansid\n";
842 }
752843 $sids{$cleansid} = 1;
753844 next;
754845 } elsif ($sid =~ /S-1-5-[\d-]+-\d+/) {
755846 ($cleansid) = $sid =~ /(S-1-5-[\d-]+)-\d+/;
756 print "[I] Found new SID: $cleansid\n" unless defined($sids{$cleansid});
847 if (defined($sids{$cleansid})) {
848 print_info("Found new SID: ");
849 print "$cleansid\n";
850 }
757851 $sids{$cleansid} = 1;
758852 next;
759853 } elsif ($sid =~ /S-1-22-[\d-]+-\d+/) {
760854 ($cleansid) = $sid =~ /(S-1-22-[\d-]+)-\d+/;
761 print "[I] Found new SID: $cleansid\n" unless defined($sids{$cleansid});
855 if (defined($sids{$cleansid})) {
856 print_info("Found new SID: ");
857 print "$cleansid\n";
858 }
762859 $sids{$cleansid} = 1;
763860 next;
764861 } else {
768865
769866 foreach my $sid (keys %sids) {
770867 if (! defined($sid) and $global_username) {
771 print "[V] WARNING: Can\'t get SID. Maybe none of the 'known' users really exist. Try others with -k. Trying null session.\n" if $verbose;
868 print_verbose("WARNING: Can\'t get SID. Maybe none of the 'known' users really exist. Try others with -k. Trying null session.\n") if $verbose;
772869 foreach my $known_username (@global_known_usernames) {
773870 my $command = "rpcclient -W '$global_workgroup' -U% '$global_target' -c 'lookupnames $known_username' 2>&1";
774 print "[I] Assuming that user $known_username exists\n";
775 print "[V] Trying null username and password: $command\n" if $verbose;
871 print_info("Assuming that user $known_username exists\n");
872 print_verbose("Trying null username and password: $command\n") if $verbose;
776873 $sid=`$command`;
777874 if ($sid =~ /error: NT_STATUS_ACCESS_DENIED/) {
778 print "[E] Couldn't get SID: NT_STATUS_ACCESS_DENIED\n";
875 print_error("Couldn't get SID: NT_STATUS_ACCESS_DENIED\n");
779876 next;
780877 } else {
781878 last;
783880 }
784881 ($sid) = $sid =~ /(S-1-5-21-[\d-]+)-\d+\s+/;
785882 unless (defined($sid)) {
786 print "[E] Can't get SID using either a null username or the username \"$global_username\"\n";
883 print_error("Can't get SID using either a null username or the username \"$global_username\"\n");
787884 exit 1;
788885 }
789886 $logon = "username '', password ''"
790887 }
791888 unless (defined($sid)) {
792 print "[E] Couldn't find SID. Aborting RID cycling attempt.\n\n";
889 print_error("Couldn't find SID. Aborting RID cycling attempt.\n\n");
793890 return 1;
794891 }
795 print "[+] Enumerating users using SID $sid and logon $logon\n";
892 print_plus("Enumerating users using SID $sid and logon $logon\n");
796893
797894 # RID Cycle;
798895 my $last_range = 0;
843940 if ($sid_and_user =~ /-(\d+) .*\\\1 \(/) {
844941 $fail_count++;
845942 } else {
846 print "$sid_and_user\n";
943 print "$sid_and_user\n" if $sid_and_user =~ /\((Local|Domain) User\)/;
944 print "$sid_and_user\n" if $sid_and_user =~ /\((Local|Domain) Group\)/;
847945 $fail_count = 0;
848946 get_user_details_from_rid($rid) if $sid_and_user =~ /\((Local|Domain) User\)/;
849947 get_group_details_from_rid($rid) if $sid_and_user =~ /\((Local|Domain) Group\)/;
863961 sub enum_users {
864962 print_heading("Users on $global_target");
865963 my $command = "rpcclient -W '$global_workgroup' -c querydispinfo -U'$global_username'\%'$global_password' '$global_target' 2>&1";
866 print "[V] Attempting to get userlist with command: $command\n" if $verbose;
964 print_verbose("Attempting to get userlist with command: $command\n") if $verbose;
867965 my $users = `$command`;
868966 my $continue = 1;
869967 if ($users =~ /NT_STATUS_ACCESS_DENIED/) {
870 print "[E] Couldn't find users using querydispinfo: NT_STATUS_ACCESS_DENIED\n";
968 print_error("Couldn't find users using querydispinfo: NT_STATUS_ACCESS_DENIED\n");
871969 } else {
872970 ($users) = $users =~ /(index:.*)/s;
873971 print $users;
878976
879977 print "\n";
880978 $command = "rpcclient -W '$global_workgroup' -c enumdomusers -U'$global_username'\%'$global_password' '$global_target' 2>&1";
881 print "[V] Attempting to get userlist with command: $command\n" if $verbose;
979 print_verbose("Attempting to get userlist with command: $command\n") if $verbose;
882980 $users = `$command`;
883981 if ($users =~ /NT_STATUS_ACCESS_DENIED/) {
884 print "[E] Couldn't find users using enumdomusers: NT_STATUS_ACCESS_DENIED\n";
982 print_error("Couldn't find users using enumdomusers: NT_STATUS_ACCESS_DENIED\n");
885983 } else {
886984 ($users) = $users =~ /(user:.*)/s;
887985 print $users;
901999 sub get_group_details_from_rid {
9021000 my $rid = shift;
9031001 if (invalid_rid($rid)) {
904 print "[E] Invalid rid passed: $rid\n";
1002 print_error("Invalid RID passed: $rid\n");
9051003 return 0;
9061004 }
9071005 return unless $global_detailed;
9081006 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'querygroup $rid' '$global_target' 2>&1";
909 print "[V] Attempting to get detailed group info with command: $command\n" if $verbose;
1007 print_verbose("Attempting to get detailed group info with command: $command\n") if $verbose;
9101008 my $group_info = `$command`;
9111009 ($group_info) = $group_info =~ /([^\n]*Group Name.*Num Members[^\n]*)/s;
9121010 if (defined($group_info)) {
9131011 print "$group_info\n\n";
9141012 } else {
915 print "[E] No info found\n\n";
1013 print_error("No info found\n\n");
9161014 }
9171015 }
9181016
9191017 sub get_user_details_from_rid {
9201018 my $rid = shift;
9211019 if (invalid_rid($rid)) {
922 print "[E] Invalid rid passed: $rid\n";
1020 print_error("Invalid RID passed: $rid\n");
9231021 return 0;
9241022 }
9251023 return unless $global_detailed;
9261024 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'queryuser $rid' '$global_target' 2>&1";
927 print "[V] Attempting to get detailed user info with command: $command\n" if $verbose;
1025 print_verbose("Attempting to get detailed user info with command: $command\n") if $verbose;
9281026 my $user_info = `$command`;
9291027 ($user_info) = $user_info =~ /([^\n]*User Name.*logon_hrs[^\n]*)/s;
9301028 print "$user_info\n" if defined($user_info);
9881086 sub get_printer_info {
9891087 print_heading("Getting printer info for $global_target");
9901088 my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'enumprinters' '$global_target' 2>&1";
991 print "[V] Attempting to get printer info with command: $command\n" if $verbose;
1089 print_verbose("Attempting to get printer info with command: $command\n") if $verbose;
9921090 my $printer_info = `$command`;
9931091 # ($group_info) = $group_info =~ /([^\n]*Group Name.*Num Members[^\n]*)/s;
9941092 if (defined($printer_info)) {
9951093 print "$printer_info\n\n";
9961094 } else {
997 print "[E] No info found\n\n";
1095 print_error("No info found\n\n");
9981096 }
9991097
10001098 }
10331131
10341132 sub print_heading {
10351133 my $string = shift;
1036 my $output = "| $string |";
1037 my $len = length($output);
1134 my $output = "$string";
1135 my $maxlen = 100;
1136 my $len = $maxlen - length($output);
10381137 print "\n";
1039 print " " . "=" x ($len - 2) . " \n";
1040 print "$output\n";
1041 print " " . "=" x ($len - 2) . " \n";
1042 }
1138 print colored(" " . "=" x ($len / 2) . "( ", 'blue');
1139 print colored("$output", 'green');
1140 print colored(" )" . "=" x ($len / 2) . "\n\n", 'blue');
1141 }
1142
1143 sub print_verbose {
1144 my $string = shift;
1145 my $output = "$string";
1146 print colored("\n[V] ", 'yellow');
1147 print colored("$output\n", 'magenta');
1148 }
1149
1150 sub print_plus {
1151 my $string = shift;
1152 my $output = "$string";
1153 print colored("\n[+] ", 'yellow');
1154 print colored("$output\n", 'green');
1155 }
1156
1157 sub print_info {
1158 my $string = shift;
1159 my $output = "$string";
1160 print colored("\n[I] ", 'yellow');
1161 print colored("$output\n", 'cyan');
1162 }
1163
1164 sub print_error {
1165 my $string = shift;
1166 my $output = "$string";
1167 print colored("\n[E] ", 'yellow');
1168 print colored("$output\n", 'red');
1169 }