metasploit-framework/tools/exploit/java_deserializer.rb

169 lines
4.7 KiB
Ruby
Executable File

#!/usr/bin/env ruby
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
msf_base = __FILE__
while File.symlink?(msf_base)
msf_base = File.expand_path(File.readlink(msf_base), File.dirname(msf_base))
end
$:.unshift(File.expand_path(File.join(File.dirname(msf_base), '..', '..', 'lib')))
require 'rex/java/serialization'
require 'pp'
require 'optparse'
# This class allows to deserialize Java Streams from
# files
class JavaDeserializer
# @!attribute file
# @return [String] the file's path to deserialize
attr_accessor :file
# @param file [String] the file's path to deserialize
def initialize(file = nil)
self.file = file
end
# Deserializes a Java stream from a file and prints the result.
#
# @return [Rex::Java::Serialization::Model::Stream] if succeeds
# @return [nil] if error
def run(options = {})
if file.nil?
print_error("file path with serialized java stream required")
return
end
print_status("Deserializing...")
print_line
begin
f = File.new(file, 'rb')
stream = Rex::Java::Serialization::Model::Stream.decode(f)
f.close
rescue ::Exception => e
print_exception(e)
return
end
if options[:array]
print_array(stream.contents[options[:array].to_i])
elsif options[:object]
print_object(stream.contents[options[:object].to_i])
else
puts stream
end
end
private
# @param [String] string to print as status
def print_status(msg='')
$stdout.puts "[*] #{msg}"
end
# @param [String] string to print as error
def print_error(msg='')
$stdout.puts "[-] #{msg}"
end
# @param [Exception] exception to print
def print_exception(e)
print_error(e.message)
e.backtrace.each do |line|
$stdout.puts("\t#{line}")
end
end
def print_line
$stdout.puts("\n")
end
# @param [Rex::Java::Serialization::Model::NewObject] obj the object to print
# @param [Integer] level the indentation level when printing super classes
def print_object(obj, level = 0)
prefix = " " * level
if obj.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc
puts "#{prefix}Object Class Description:"
print_class(obj.class_desc.description, level + 1)
else
puts "#{prefix}Object Class Description: #{obj.class_desc.description}"
end
puts "#{prefix}Object Data: #{obj.class_data}"
end
# @param [Rex::Java::Serialization::Model::NewClassDesc] c the class to print
# @param [Integer] level the indentation level when printing super classes
def print_class(c, level = 0)
prefix = " " * level
puts "#{prefix}Class Name: #{c.class_name}"
puts "#{prefix}Serial Version: #{c.serial_version}"
puts "#{prefix}Flags: #{c.flags}"
puts "#{prefix}Fields ##{c.fields.length}"
c.fields.each do |f|
puts "#{prefix}Field: #{f}"
end
puts "#{prefix}Class Annotations ##{c.class_annotation.contents.length}"
c.class_annotation.contents.each do |c|
puts "#{prefix}Annotation: #{c}"
end
puts "#{prefix}Super Class: #{c.super_class}"
if c.super_class.description.class == Rex::Java::Serialization::Model::NewClassDesc
print_class(c.super_class.description, level + 1)
end
end
# @param [Rex::Java::Serialization::Model::NewArray] arr the array to print
# @param [Integer] level the indentation level when printing super classes
def print_array(arr, level = 0)
prefix = " " * level
if arr.array_description.description.class == Rex::Java::Serialization::Model::NewClassDesc
puts "#{prefix}Array Description"
print_class(arr.array_description.description, 1)
else
puts "#{prefix}Array Description: #{arr.array_description.description}"
end
puts "#{prefix}Array Type: #{arr.type}"
puts "#{prefix}Array Values ##{arr.values.length}"
arr.values.each do |v|
puts "Array value: #{prefix}#{v} (#{v.class})"
if v.class == Rex::Java::Serialization::Model::NewObject
print_object(v, level + 1)
end
end
end
end
if __FILE__ == $PROGRAM_NAME
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: java_deserializer.rb <file> [option]"
opts.on("-aID", "--array=ID", "Print detailed information about content array") do |id|
options[:array] = id
end
opts.on("-oID", "--object=ID", "Print detailed information about content object") do |id|
options[:object] = id
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end.parse!
if options.length > 1
$stdout.puts "[-] Don't provide more than one option"
exit
end
deserializer = JavaDeserializer.new(ARGV[0])
deserializer.run(options)
end