#!/usr/bin/perl
$|=1;
$TIMEOUT=10; #connect timeout
$RECEIVE_LEN=1024; #receive buffer
if ($#ARGV < 0){help();exit}
use Getopt::Std;
getopts('R:H:P:p:hvao:O:');
if ($opt_h || !$opt_R || !$opt_H || !$opt_P){help();exit}
$wwwhost=$opt_H;
$wwwport=$opt_P;
$verbose=$opt_v;
$WAITALL=$opt_a;# MSG_WAITALL recv flag
$use_proxy=$opt_p;
$hlogfile=$opt_o;
$mlogfile=$opt_O;
@rules=loadrules($opt_R);
if ($use_proxy){
    @plist=loadproxy($use_proxy);
    $pnum=@plist;
    $pcur=0;
    if ($verbose){print STDERR "$pnum proxies loaded\n"}
}
use Socket;
$SIG{PIPE}='IGNORE';
$FBUGS=0;
for $rule (@rules){
    my ($rflags, $data, $method, $uri, $explain)=parserule($rule);
    my $crlf=0;
    my $req=undef;
    my $res=undef;
    if ($uri=~/\\r\\n/){
	$uri=~s/\\r\\n/\r\n/g;
	$crlf=1;
    }
    if ($use_proxy){
	my $pfail=0;
	if (!($uri=~/^\//)){$uri=sprintf("/%s", $uri)}
	if ($crlf==1){
	    $uri=~s/\r\n\r\n/\r\nHost: $wwwhost:$wwwport\r\n\r\n/g;
	    $req=sprintf("%s http://%s:%s%s",$method,$wwwhost,$wwwport,$uri);
	}else{
	    $req=sprintf("%s http://%s:%s%s HTTP/1.1\r\nHost: %s:%s\r\n\r\n",$method,$wwwhost,$wwwport,$uri,$wwwhost,$wwwport);
	}
	while(!$res){
	    if ($pcur >= $pnum){$pcur=0;}
	    my ($proxyip, $proxyport) = split(/:/, $plist[$pcur++]);
	    print STDERR "trying $proxyip:$proxyport ... ";
	    $res=sendraw($proxyip, $proxyport, $req, $rflags);
	    if (!$res){$pfail++;}
	    if ($pfail >= $pnum){
		print STDERR "all proxies are dead\n";
		exit;
	    }
	}
	$pfail=0;
    }else{
	if ($crlf==1){
	    $uri=~s/\r\n\r\n/\r\nHost: $wwwhost:$wwwport\r\n\r\n/g;
	    $req=sprintf("%s %s",$method,$uri);
	}else{
	    $req=sprintf("%s %s HTTP/1.1\r\nHost: %s:%s\r\n\r\n",$method,$uri,$wwwhost,$wwwport);
	}
	$res=sendraw($wwwhost, $wwwport, $req, $rflags);
    }
    # print out results
    if ($res=~/$data/){
        $FBUGS++;
	my $tmp=$explain;
	$tmp=~s/(\\n\\t|\;)/\n\t/g;
	my $str="BUG FOUND($wwwhost:$wwwport):\n uri:\t$uri\n expl:\t$tmp\n";
        print $str;
	if ($hlogfile){plog($hlogfile, $str)}
	if ($mlogfile){plog($mlogfile, "BUG^$wwwhost:$wwwport^$uri^$explain\n")}
        if ($verbose){
	    $str = " expc:\t$data\n";
    	    print $str;
	    if ($hlogfile){plog($hlogfile, $str)}
    	    $res=~s/\r\n/\r\n\t/g;
	    $str=" resp:\t$res\n*********************************************\n";
    	    print $str;
	    if ($hlogfile){plog($hlogfile, $str)}
        }else{
	    $str="*********************************************\n";
    	    print $str;
	    if ($hlogfile){plog($hlogfile, $str)}
	}
    }

}
print STDERR "$FBUGS possible holes found total\n";
exit;

# subroutines
# send-receive data using sockets
sub sendraw {
    my ($host,$port,$str,$rflags)=@_;
    my ($so,$res);
    socket($so,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0)
	|| die("socket:$!\n");
    eval {
	local $SIG{ALRM} = sub { die "alarm\n" };
        alarm $TIMEOUT;
        if(!connect($so,pack "SnA4x8",2,$port,inet_aton($host))){
	    print STDERR "can't connect:$!\n";
	    return undef;
	}
        alarm 0;
    };
    if($@ eq "alarm\n"){
	print STDERR "connect to $host:$port timeout\n";
	close($so);
	return undef;
    }
    send($so,$str,0);
    #while(read($so,$buf,512)){$res.=$buf}
    if ($WAITALL){$rflags=MSG_WAITALL();}
    recv($so,$res,$RECEIVE_LEN,$rflags);
    return $res;
}
# show help
sub help {
    print <<HELP
=== ZWWWSCAN :: Based on arirang and its rules
=== (http://monkey.org/~pilot/arirang/)
=== by z3r0Fi11<cyberwin\@mail.ru> :: bom shankar!

Usage: zwwwcan.pl -R <rulefile> -H <host> -P <port> [-p proxyfile] [-a] [-v] [-o logfile] [-O logfile]
	-R 'rulefile'	: load arirang rule file 'rulefile'
	-H 'host'	: target host 'host'
	-P 'port'	: target port 'port'
	-p 'proxyfile'	: use proxy list 'proxyfile' for scaning
	-h 		: this help
	-v		: verbose output(recommended)
	-a		: force MSG_WAITALL receive flag
	-o 'logfile'	: save results in 'logfile' in human readable format
	-O 'logfile'	: save results in 'logfile' in machine parseable format

Note: Log files will be appended.
=== have a good day
HELP
;
}
# load, strip proxylist
sub loadproxy {
    my ($file)=@_;
    my $fd;
    my @pl;
    open($fd, $file) || die("open:$file:$!\n");
    while(<$fd>){
	s/[\r\n]//g;s/\s//g;
	if ($_){
	    push(@pl, $_);
	}
    }
    close($fd);
    return @pl;
}
# load arirang rules
sub loadrules {
    my ($file)=@_;
    my $fd;
    my @rl;
    open($fd, $file) || die("open:$file:$!\n");
    while(<$fd>){
	s/[\r\n]//g;
	s/^\s+//;
	if (/^#/){next}
	if (!$_){next}
	push(@rl, $_);
    }
    close($fd);
    return @rl;
}
# parse rules string (based on manual scanrule/rule.uxe from arirang)
sub parserule {
    my ($str)=@_;
    my ($tmp,$rflags,$rdata,$rmethod,$ruri);
    if ($str=~/^\(([^\)]*)\)\s/){
	($rflags, $tmp)=split(/\)\s/, $str, 2);
	$rflags=~s/\(//;
	if ($rflags eq "OOB"){$rflags=MSG_OOB();}
	elsif ($rflags eq "PEEK"){$rflags=MSG_PEEK();}
	elsif ($rflags eq "ALL"){$rflags=MSG_WAITALL();}
	else{$rflags=0}
	($rdata, $tmp)=split(/\-\>\s/, $tmp, 2);
    }else{
	$rflags=0;
	($rdata, $tmp)=split(/\-\>\s/, $str, 2);
    }
    ($rmethod, $tmp)=split(/\s:/, $tmp, 2);
    ($ruri, $tmp)=split(/\^/, $tmp, 2);
    return ($rflags,$rdata,$rmethod,$ruri,$tmp);
}
sub plog {
    my ($file, $str)=@_;
    my $f;
    open($f,">>$file") or die "open:$file:$!\n";
    print $f "$str";
    close($f);
}
