2014-12-04 18:47:02 +00:00
|
|
|
#!/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'
|
2015-01-22 20:50:12 +00:00
|
|
|
require 'optparse'
|
2014-12-04 18:47:02 +00:00
|
|
|
|
|
|
|
# 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
|
2015-01-22 20:50:12 +00:00
|
|
|
def run(options = {})
|
2014-12-04 18:47:02 +00:00
|
|
|
if file.nil?
|
2014-12-06 02:30:22 +00:00
|
|
|
print_error("file path with serialized java stream required")
|
2014-12-04 18:47:02 +00:00
|
|
|
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
|
|
|
|
|
2015-01-22 20:50:12 +00:00
|
|
|
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
|
2014-12-04 18:47:02 +00:00
|
|
|
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
|
2015-01-22 20:50:12 +00:00
|
|
|
|
|
|
|
# @param [Rex::Java::Serialization::Model::NewObject] obj the object to print
|
|
|
|
# @param [Fixnum] 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 [Fixnum] 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 [Fixnum] level the indentation level when printing super classes
|
|
|
|
def print_array(arr, level = 0)
|
|
|
|
prefix = " " * level
|
2015-01-22 22:54:49 +00:00
|
|
|
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
|
2015-01-22 20:50:12 +00:00
|
|
|
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
|
2014-12-04 18:47:02 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if __FILE__ == $PROGRAM_NAME
|
2015-01-22 20:50:12 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2014-12-04 18:47:02 +00:00
|
|
|
deserializer = JavaDeserializer.new(ARGV[0])
|
2015-01-22 20:50:12 +00:00
|
|
|
deserializer.run(options)
|
2014-12-04 18:47:02 +00:00
|
|
|
end
|