home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
- ###############
-
- require 5.6.0;
- use strict;
- use vars qw{$SSL_SUPPORT};
-
- use FindBin qw{$RealBin};
- use lib "$RealBin/lib";
- use Digest::Perl::MD5 qw{md5_hex};
-
- use Getopt::Std;
- use IO::Socket;
- use IO::Select;
- use POSIX;
-
- no utf8;
- no locale;
-
- my $VERSION = '$Revision: 1.45 $';
- my $FRAMEWORK = '2.6';
- my ($REV) = $VERSION =~ m/\$Revisio.:\s+([^\s]+)/;
-
- my $UPDATE_HOST = 'metasploit.com';
- my $UPDATE_PATH = '/projects/Framework/updates/msfupdate.html';
- my $SSL_VERIFIED = 0;
- my $SSL_ISSUER = '/C=US/ST=Texas/L=San Antonio/O=The Metasploit Project/OU=Development/CN=Metasploit CA/emailAddress=cacert@metasploit.com';
- my $SSL_CERTS = "$RealBin/docs";
- my $OPSYS = $^O;
-
- select(STDOUT); $|++;
-
- # Check for buggy versions of perl with utf-8 locale set
- BrokenUTF8();
-
- # Determine if SSL support is enabled
- BEGIN
- {
- if (eval "require Net::SSLeay")
- {
- Net::SSLeay->import();
- Net::SSLeay::load_error_strings();
- Net::SSLeay::SSLeay_add_ssl_algorithms();
- Net::SSLeay::randomize();
- $SSL_SUPPORT++;
- }
- }
-
- my %opts;
- getopts('hrvusmaxfOp:', \%opts);
-
- if ($opts{v}) {
- print "Msfupdate Version: $REV\n";
- print "Framework Version: $FRAMEWORK\n";
- exit(0);
- }
-
- if ($opts{h} || (! $opts{m} && ! $opts{r} && ! $opts{u} && ! $opts{U})) {
- Usage();
- }
-
- if ($opts{O}) {
- $OPSYS = 'paranoid';
- }
-
- chdir($RealBin);
-
- my $proxy = $opts{'p'};
- my ($old_data, $now_data, $new_data);
- my ($old, $now, $new);
- my ($mod, $upx, $upd);
-
- print "\n";
- print "+ -- --=[ msfupdate v$FRAMEWORK [revision $REV]\n\n";
- print "[*] Calculating local file checksums, please wait...\n";
- $now_data = ScanFiles('.');
- print "\n";
-
- if (! -r '.current') {
- WriteFile('.current', $now_data);
- $old_data = $now_data;
- }
- else {
- $old_data = ReadFile('.current');
- }
-
- if ($opts{r}) {
- print "[*] The local file hash has been rebuilt\n";
- WriteFile('.current', $now_data);
- exit(0);
- }
-
- # Allow the user to disable SSL mode entirely
- if ($opts{f}) {
- $SSL_SUPPORT = 0;
- }
-
- # Display a big nasty warning for non-SSL unless -x is specified
- if ($opts{u} || $opts{U}) {
- if (! $opts{x} && ! $SSL_SUPPORT) {
- print "[*] WARNING: The Net::SSLeay module is not installed. The update\n";
- print " process will fall back to plain HTTP, this may allow a \n";
- print " malicious attacker to inject hostile code into your system.\n";
- print "\n";
- print "Continue anyways (yes or no) ";
-
- my $resp;
- while ($resp !~ /yes|no/i) {
- print "> ";
- $resp = <STDIN>;
- chomp($resp);
- }
- print "\n";
-
- if ($resp !~ /yes/i) {
- print "[*] The update process has been cancelled\n";
- exit(0);
- }
- }
-
- $new_data = DownloadFile($UPDATE_HOST, "$UPDATE_PATH/.current", $proxy);
- }
-
- $now = Hashit($now_data);
- $old = Hashit($old_data);
- $new = Hashit($new_data);
-
- $mod = Diff($old, $now);
- $upx = Diff($old, $new);
- $upd = Diff($now, $new);
-
- if ($opts{m}) {
- if (! keys(%{$mod})) {
- print "[*] No modifications since the last update\n";
- exit(0);
- }
-
- print "[*] Modifications since the last update:\n";
- foreach (sort keys(%{$mod})) {
- print "\t".$mod->{$_}."\t$_\n";
- }
- print "\n";
- exit(0);
- }
-
- # Strip this list down to just modifications
- foreach (keys(%{$mod})) {
- if ($mod->{$_} ne 'Mod') {
- delete($mod->{$_});
- }
- }
-
- if (! $new_data) {
- print "[*] Could not obtain the current version information from the update server\n";
- exit(0);
- }
-
- my $mver = CheckVersion();
-
- if ( ($opts{u} || $opts{U}) && ! $mver) {
- print "[*] Could not determine the current Framework version.\n";
- exit(0);
- }
-
- if ($mver && $mver ne $FRAMEWORK) {
-
- if (! $opts{U}) {
- print "[*] Version $mver of the Metasploit Framework is now available.\n";
- print " - http://metasploit.com/projects/Framework/downloads.html\n\b\n";
- }
- else {
- print "[*] Starting upgrade process for version $mver...\n\n";
- }
- }
-
- if ($opts{U} && $mver && $mver eq $FRAMEWORK) {
- print "[*] No version upgrade is necessary.\n";
- exit(0);
- }
-
- # Upgrade to a new major release version...
- if ($opts{U}) {
-
- my $backupdir = 'backup_'. $FRAMEWORK;
-
- print "[*] WARNING: You are about to upgrade to a new major version of the\n";
- print " Metasploit Framework. This process involves removing the current\n";
- print " installation and downloading the new version, one file at a time.\n";
- print " This process is slow and somewhat prone to failure. If you encounter\n";
- print " any problems during the download, you *should* be able to simply\n";
- print " restart msfupdate with the -u parameter\n\n";
-
- print " If you have modified any of the files in the Framework directory,\n";
- print " these will be saved to $backupdir. The same applies for any modules\n";
- print " that you have added to this Framework installation. After the update\n";
- print " is complete, you should be able to copy these modules from the\n";
- print " backup directory ($backupdir) into the appropriate directories\n";
- print " in the new installation.\n\n";
-
- print " If you are using FreeBSD or any other operating that maintains a\n";
- print " package for the Framework, we recommend that you use the system\n";
- print " package manager to upgrade\n\n";
-
- print " Please type 'yes' to initiate the upgrade process.\n";
- print "Continue? (yes or no) ";
-
- my $resp;
- while ($resp !~ /yes|no/i) {
- print "> ";
- $resp = <STDIN>;
- chomp($resp);
- }
- if ($resp =~ /no/i) {
- exit(0);
- }
-
- delete($now->{'./msfupdate'});
- delete($new->{'./msfupdate'});
- delete($mod->{'./msfupdate'});
- delete($upd->{'./msfupdate'});
-
- print "[*] Backing up modified files to $backupdir...\n";
-
- # Backup framework files that the user modified
- foreach my $file (keys %{$mod} ) {
- print " --- MOD $file\n";
- my $data = ReadFile($file);
- WriteFile($backupdir .'/'. $file , $data);
- }
-
- # Backup files that the user created
- foreach my $file (keys %{$upd} ) {
- next if $upd->{$file} ne 'Del';
- print " --- USR $file\n";
- my $data = ReadFile($file);
- WriteFile($backupdir .'/'. $file , $data);
- }
-
- print "[*] Removing the current installation...\n";
-
- # Do not remove these files since we need them to update...
- my %docs_save = ('./docs/7f8d5320.0' => 1, './docs/cacert.pem' => 1);
-
- # Remove all files that are part of the framework
- foreach my $file (keys %{$now}) {
- next if exists($docs_save{$file});
- unlink($file);
- }
-
- # Remove all directories but 'docs'
- foreach my $dir (split(/\n/, ScanDirs('.', 0))) {
- next if $dir eq './docs';
- system("rm", "-rf", $dir);
- }
-
- # Bump up the framework version
- $FRAMEWORK = $mver;
-
- # Download the file list for the new framework version
- $new_data = DownloadFile($UPDATE_HOST, "$UPDATE_PATH/.current", $proxy);
-
- # Rebuild the tables
- $new = Hashit($new_data);
- $now = {};
- $old = {};
-
- $mod = Diff($old, $now);
- $upx = Diff($old, $new);
- $upd = Diff($now, $new);
-
- # Configure some options
- $opts{a}++;
- }
-
-
-
-
- # They modified something, ask them what they want to do with it
- if (! $opts{a} && keys(%{$mod})) {
- print "[*] You have modified the following Framework components:\n\n";
- foreach (sort(keys(%{$mod}))) {
- print "\t".$_."\n";
- }
- print "\n";
- print " Would you like to preserve these changes? If you say no, all\n";
- print " local modifications will be overwritten by the update process.\n";
- print "\n";
- print "Preserve modifications (yes or no) ";
- my $resp;
- while ($resp !~ /yes|no/i) {
- print "> ";
- $resp = <STDIN>;
- chomp($resp);
- }
- if ($resp =~ /no/i) {
- $mod = {};
- }
- print "\n";
- }
-
- foreach (keys(%{$upd})) {
- # Do not remove user-created files
- if ($upd->{$_} eq 'Del') {
- delete($upd->{$_});
- }
-
- # Ignore updates we already have
- if ($now->{$_} eq $new->{$_}) {
- delete($upd->{$_});
- }
- }
-
-
- # Ignore locally modified files when -a is supplied
- if ($opts{a}) {
- $mod = {};
- }
-
- # After all of this stuff, this is what we have
- # - $mod is a table of anything the user wants to keep
- # - $upd is a table of everything online new or updated
-
-
- # The plan of attack is:
- # - Iterate through each item in $upd, if there is a match in
- # $mod, we print a message and ignore the download for it.
- # We will delete anything with the Del status set in this
- # table.
- # - Once we have identified an update, we try to download it.
- # If an error occurs, either because the download fails or
- # the md5 does no match.
- #
- # - After all files have been processed, we rebuild the local
- # .current file and get ready for the next update.
- #
-
- if (! keys(%{$upd})) {
- print "[*] No new updates are available\n";
- exit(0);
- }
-
- print "[*] Online Update Task Summary\n\n";
- foreach my $entry (sort keys(%{$upd})) {
- next if $opts{U};
-
- if ($mod->{$entry}) {
- print "\tIgnore: $entry\n";
- }
- else {
- print "\tUpdate: $entry\n";
- }
- }
- print "\n";
-
- exit(0) if $opts{s};
-
- if (! $opts{a}) {
- print "Continue? (yes or no) ";
- my $resp;
- while ($resp !~ /yes|no/i) {
- print "> ";
- $resp = <STDIN>;
- chomp($resp);
- }
- print "\n";
- if ($resp !~ /yes/i) {
- exit(0);
- }
- }
-
- my @tasks = sort(keys(%{$upd}));
-
- # Force msfupdate to downloaded first...
- if ($opts{U}) {
- unshift(@tasks, './msfupdate');
- unshift(@tasks, './lib/Digest/Perl/MD5.pm');
- }
-
- my $upd_tot = scalar(@tasks);
- my $upd_cur = 0;
-
- print "\n";
- print "[*] Starting online update of $upd_tot file(s)...\n\n";
- foreach my $entry (@tasks) {
- if ($mod->{$entry}) {
- next;
- }
- my $data = DownloadFile($UPDATE_HOST, "$UPDATE_PATH/$entry", $proxy);
- if ($new->{$entry} ne md5_hex($data)) {
- print "$entry: ". $new->{$entry} ." != ". md5_hex($data) ."\n";
- my $errmsg = $data ? 'checksum mismatch' : 'download failed';
- print "[*] Failed to update $entry: $errmsg\n";
- next;
- }
-
- # overwrite the local file O_o
- WriteFile($entry, $data);
-
- # set it executable since we dont track permissions
- chmod(0755, $entry);
-
- printf("[%.4d/%.4d - 0x%.6x bytes] $entry\n", ++$upd_cur, $upd_tot, length($data));
- }
-
- print "\n";
- print "[*] Regenerating local file database\n";
- $now_data = ScanFiles('.');
- WriteFile('.current', $now_data);
-
-
- sub Usage {
- print STDERR " Usage: $0 [options]>\n";
- print STDERR "Options:\n";
- print STDERR " -h You're looking at me baby\n";
- print STDERR " -v Display version information\n";
- print STDERR " -u Perform an online update via metasploit.com\n";
- print STDERR " -s Only display update tasks, do not actually download\n";
- print STDERR " -m Show any files locally modified since last update\n";
- print STDERR " -a Do not prompt, default to overwrite all files\n";
- print STDERR " -x Do not require confirmation for non-SSL updates\n";
- print STDERR " -f Disable ssl support entirely, use with -x to avoid warnings\n";
- print STDERR " -O Removes the operating system name from the user agent\n";
- print STDERR " -p Specifies a proxy: <http|socks4>:<hostname>:<port>\n";
-
- # Too buggy and dangerous to use
- # print STDERR " -U Upgrade to a new major revision of the Framework\n";
- # Developer options
- # print STDERR " -r Rebuild the local version database (internal only)\n";
- print "\n";
- exit(0);
- }
-
- sub ScanFiles {
- my $dir = shift;
- my $res;
- my $hwn;
-
- opendir ($hwn, $dir) || return;
-
- while (defined(my $entry = readdir($hwn))) {
- my $path = "$dir/$entry";
-
- # ignore all symlinks
- next if -l $path;
-
- # ignore all leading dot files
- next if $entry =~ /^\./;
-
- # ignore the backup directories
- next if $entry =~ /^backup_/;
-
- # recurse into directories
- if (-d $path) {
- $res .= ScanFiles($path);
- }
- elsif (-f $path) {
- $res .= HashFile($path)."\t$path\n";;
- }
- }
- closedir($hwn);
- return $res;
- }
-
- sub ScanDirs {
- my $dir = shift;
- my $dep = @_ ? shift() : 0;
- my $res;
- my $hwn;
-
- return if $dep == -1;
- opendir ($hwn, $dir) || return;
-
- while (defined(my $entry = readdir($hwn))) {
- my $path = "$dir/$entry";
-
- # ignore all symlinks
- next if -l $path;
-
- # ignore the backup directories
- next if $entry =~ /^backup_/;
-
- # ignore all leading dot files
- next if $entry =~ /^\./;
-
- # recurse into directories
- if (-d $path) {
- $res .= "$path\n";
- $res .= ScanDirs($path, $dep - 1);
- }
- }
- closedir($hwn);
- return $res;
- }
-
-
-
- sub CheckVersion {
- my $data = DownloadFile($UPDATE_HOST, $UPDATE_PATH, $proxy);
- my ($vers, $mtime) = split(/\s+/, $data);
- return $vers;
- }
-
- sub DownloadFile {
- my $host = shift;
- my $path = shift;
- my $prox = shift;
-
- my $port = $SSL_SUPPORT ? 443 : 80;
- my $data;
-
- $path = EscapeURI($path);
-
- my ($proxy_type, $proxy_host, $proxy_port);
- if ($prox =~ m/^(socks4|http):([^:]+):(\d+)/) {
- ($proxy_type, $proxy_host, $proxy_port) = ($1, $2, $3);
- }
-
- if ($prox && ! $proxy_host) {
- print STDERR "[*] Invalid proxy format ($prox), please use one of the following:\n";
- print STDERR "\tSOCKS: socks4:<hostname|ip address>:<port>\n";
- print STDERR "\t HTTP: http:<hostname|ip address>:<port>\n\n";
- exit(0);
- }
-
-
- my $sock = IO::Socket::INET->new
- (
- PeerAddr => $proxy_host || $host,
- PeerPort => $proxy_port || $port,
- Proto => 'tcp',
- );
-
- if (! $sock) {
- print STDERR "[*] Could not connect to $host: $!\n";
- exit(0);
- }
-
- if ($proxy_type eq 'http') {
- $sock->send("CONNECT ".$host.":".$port." HTTP/1.0\r\n\r\n");
-
- # Look for the HTTP response message from the Proxy server
- my $sel = IO::Select->new($sock);
- my $resp = '';
-
- if ($sel->can_read(5)) {
- while (my $line = <$sock>) {
- last if $line eq "\r\n";
- $resp .= $line;
- }
- } else {
- print STDERR "[*] No reply received from the HTTP proxy\n";
- exit(0);
- }
-
- if ($resp !~ /HTTP\/1\.\d\s+2/) {
- foreach my $line (split(/\r\n/, $resp)) {
- $line =~ s/\r|\n//g;
- $line =~ s/\e//g;
- print STDERR "[*] Proxy: " . $line . "\n";
- }
- print STDERR "[*] Connection failed\n";
- exit(0);
- }
- }
-
- if ($proxy_type eq 'socks4') {
- $sock->send("\x04\x01".pack('n',$port).gethostbyname($host)."\x00");
- $sock->recv(my $res, 8);
-
- if (! $res || ($res && ord(substr($res,1,1)) != 90)) {
- print STDERR "[*] Failed to established connection through socks4 proxy $proxy_host\n";
- if (length($res)) {
- print STDERR "[*] Response: ". unpack("H*", $res) ."\n";
- }
- exit(0);
- }
- }
-
- my $req =
- "GET $path HTTP/1.0\r\n".
- "Host: ".$host.":".$port."\r\n".
- "User-Agent: msfupdate/$REV $FRAMEWORK ($OPSYS)\r\n\r\n";
-
- if ($SSL_SUPPORT) {
-
- # Reset the verified flag
- $SSL_VERIFIED = 0;
-
- # Create SSL Context
- my $ctx = Net::SSLeay::CTX_new();
-
- # Tell SSL to use the certificate in the docs directory
- Net::SSLeay::CTX_load_verify_locations($ctx, '', $SSL_CERTS);
-
- # Configure the SSL call-back to prevent MiTM
- Net::SSLeay::CTX_set_verify($ctx, &Net::SSLeay::VERIFY_PEER, \&SSLVerify);
-
- # Configure session for maximum interoperability
- Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL);
-
- # Create a new SSL object with context
- my $ssl = Net::SSLeay::new($ctx);
-
- # Bind the SSL descriptor to the socket
- Net::SSLeay::set_fd($ssl, $sock->fileno);
-
- # Negotiate connection
- my $sslConn = Net::SSLeay::connect($ssl);
-
- if ($sslConn <= 0) {
- print STDERR "[*] SSL error:". Net::SSLeay::print_errs()."\n";
- $sock->close;
- return;
- }
-
- Net::SSLeay::ssl_write_all($ssl, $req);
-
- my $cert = Net::SSLeay::get_peer_certificate($ssl);
-
- $data = Net::SSLeay::ssl_read_all($ssl);
-
- Net::SSLeay::free ($ssl);
- Net::SSLeay::CTX_free ($ctx);
- $sock->close;
-
- $data = ProcessHTTP($data);
- return $data;
- }
-
- $sock->send($req);
- $sock->shutdown(1);
- while (<$sock>) { $data .= $_ }
- close ($sock);
-
- $data = ProcessHTTP($data);
- return $data;
- }
-
- # Prevent MiTM attacks on the update downloads when run over SSL
- sub SSLVerify {
- my ($ok, $x509_store_ctx) = @_;
- my $cert = Net::SSLeay::X509_STORE_CTX_get_current_cert($x509_store_ctx);
-
- if ($cert) {
- my $x509_issuer = Net::SSLeay::X509_get_issuer_name($cert);
- my $issuer = Net::SSLeay::X509_NAME_oneline($x509_issuer);
-
- # differences between openssl 0.9.6 vs 0.9.7 (thanks par!)
- $issuer =~ s/Email/emailAddress/g;
-
- if ($ok && $issuer eq $SSL_ISSUER) {
- $SSL_VERIFIED++;
- return 1;
- }
- }
-
- return;
- }
-
- sub ProcessHTTP {
- my $http = shift;
- my $idx = index($http, "\r\n\r\n");
- return if -1 == $idx;
- my $head = substr($http, 0, $idx);
- my $body = substr($http, $idx + 4);
- return if $head !~ /HTTP\/1..\s+2/;
- return $body;
- }
-
- sub WriteFile {
- my $file = shift;
- my $data = shift;
-
- my @path = split(/\//, $file);
- pop(@path);
-
- my $cdir = ".";
- foreach (@path) {
- $cdir .= "/".$_;
- if (! -d $cdir) {
- mkdir($cdir, 0755);
- }
- }
-
- open (my $out, ">$file") || return;
- print $out $data;
- close ($out);
- }
-
- sub ReadFile {
- my $path = shift;
- my $data;
- my $inp;
-
- open($inp, "<$path") || return;
- while (<$inp>) { $data .= $_ }
- close($inp);
-
- return $data;
- }
-
- sub HashFile {
- my $path = shift;
- my $data = ReadFile($path);
- return md5_hex($data);
- }
-
- sub Hashit {
- my $data = shift;
- my $hash = {};
- foreach my $line (split(/\n/, $data)) {
- my ($md5, $path) = $line =~ m/^([^\s]+)\s+(.*)/g;
- $hash->{$path} = $md5;
- }
- return $hash;
- }
-
- sub Diff {
- my $setA = shift;
- my $setB = shift;
- my $res;
-
- foreach (keys(%{$setA})) {
- if (exists($setB->{$_})) {
- if ($setA->{$_} ne $setB->{$_}) {
- $res->{$_} = 'Mod';
- }
- }
- else {
- $res->{$_} = 'Del';
- }
- }
-
- foreach (keys(%{$setB})) {
- if (! exists($setA->{$_})) {
- $res->{$_} = 'New';
- }
- }
-
- return $res;
- }
-
- sub EscapeURI {
- my $path = shift;
- my %escapes = ();
- for (0..255) { $escapes{chr($_)} = sprintf("%%%02X", $_) }
- $path =~ s/([^A-Za-z0-9\-_.!~*'()\/])/$escapes{$1}/eg;
- return $path;
- }
-
- sub BrokenUTF8 {
- if ( $] >= 5.008 && $] < 5.008002 )
- {
- my $badver;
-
- # Check LANG first
- $badver = ($ENV{'LANG'} =~ /utf/i) ? 1 : 0;
-
- # LC_ALL overrides LANG if its set
- if (defined($ENV{'LC_ALL'})) {
- $badver = ($ENV{'LC_ALL'} =~ /utf/i) ? 1 : 0;
- }
-
- return if ! $badver;
-
- print STDERR qq|
- [*] This version of Perl ($]) contains a buggy utf-8 implementation. If you
- would like to use this version with the Metasploit Framework, you must
- set the LC_ALL environment variable to 'C'. For example:
-
- \$ export LC_ALL=C; ./msfconsole
-
- |;
- exit(0);
- }
- }
-