CVE-2020-28043: Server-Side Request Forgery (SSRF) (CWE-918)
value: MISP through 2.4.133 allows SSRF in the REST client via the use_full_path parameter with an arbitrary URL.
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVSS v3 base score: 7.5
Published: 2020-11-02T21:15Z
Repository: https://github.com/MISP/MISP
Language: PHP
Files changed: app/Controller/ServersController.php, app/Model/Server.php
Commit date: 2020-10-31 08:49:01 +0100
Commit message: fix: [security] SSRF fixed in the rest client

- by using the full path parameter in the rest client, users could issue queries to any server
- this becomes especially problematic when the MISP server is able to query other internal servers,
  as external users could trigger those

- new server setting added that allows enabling the full path option, this is now disabled by default
- new server setting added to add an override baseurl for the rest client, removing the need for the full
  path option in the first place (for example for the training VM with its port forwarding)

- Thanks to Heitor Gouvêa for reporting this vulnerability

Diff: diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php
index f870a4276ad..f5911d64b77 100644
--- a/app/Controller/ServersController.php
+++ b/app/Controller/ServersController.php
@@ -2000,21 +2000,21 @@ class ServersController extends AppController
             'user_id' => $this->Auth->user('id'),
             'headers' => $request['header'],
             'body' => empty($request['body']) ? '' : $request['body'],
             'url' => $request['url'],
             'http_method' => $request['method'],
-            'use_full_path' => $request['use_full_path'],
+            'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
             'show_result' => $request['show_result'],
             'skip_ssl' => $request['skip_ssl_validation'],
             'bookmark' => $request['bookmark'],
             'bookmark_name' => $request['name'],
             'timestamp' => $date->getTimestamp()
         );
         if (!empty($request['url'])) {
-            if (empty($request['use_full_path'])) {
+            if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
                 $path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
-                $url = Configure::read('MISP.baseurl') . $path;
+                $url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
                 unset($request['url']);
             } else {
                 $url = $request['url'];
             }
         } else {
@@ -2080,10 +2080,11 @@ class ServersController extends AppController
         } else {
             return false;
         }
         $view_data['duration'] = microtime(true) - $start;
         $view_data['duration'] = round($view_data['duration'] * 1000, 2) . 'ms';
+        $view_data['url'] = $url;
         $view_data['code'] =  $response->code;
         $view_data['headers'] = $response->headers;
         if (!empty($request['show_result'])) {
             $view_data['data'] = $response->body;
         } else {
diff --git a/app/Model/Server.php b/app/Model/Server.php
index 17eef984191..cb15a4f192d 100644
--- a/app/Model/Server.php
+++ b/app/Model/Server.php
@@ -1277,10 +1277,27 @@ class Server extends AppModel
                                 'test' => 'testSalt',
                                 'type' => 'string',
                                 'editable' => false,
                                 'redacted' => true
                         ),
+                        'rest_client_enable_arbitrary_urls' => array(
+                            'level' => 0,
+                            'description' => __('Enable this setting if you wish for users to be able to query any arbitrary URL via the rest client. Keep in mind that queries are executed by the MISP server, so internal IPs in your MISP\'s network may be reachable.'),
+                            'value' => false,
+                            'errorMessage' => '',
+                            'test' => 'testBool',
+                            'type' => 'boolean',
+                            'null' => true
+                        ),
+                        'rest_client_baseurl' => array(
+                            'level' => 1,
+                            'description' => __('If left empty, the baseurl of your MISP is used. However, in some instances (such as port-forwarded VM installations) this will not work. You can override the baseurl with a url through which your MISP can reach itself (typically https://127.0.0.1 would work).'),
+                            'value' => false,
+                            'errorMessage' => '',
+                            'test' => null,
+                            'type' => 'string',
+                        ),
                         'syslog' => array(
                             'level' => 0,
                             'description' => __('Enable this setting to pass all audit log entries directly to syslog. Keep in mind, this is verbose and will include user, organisation, event data.'),
                             'value' => false,
                             'errorMessage' => '',

Vulnerable code:             'use_full_path' => $request['use_full_path'],
            if (empty($request['use_full_path'])) {
                $url = Configure::read('MISP.baseurl') . $path;
Fixed code:             'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
            if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
                $url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
        $view_data['url'] = $url;
                        'rest_client_enable_arbitrary_urls' => array(
                            'level' => 0,
                            'description' => __('Enable this setting if you wish for users to be able to query any arbitrary URL via the rest client. Keep in mind that queries are executed by the MISP server, so internal IPs in your MISP\'s network may be reachable.'),
                            'value' => false,
                            'errorMessage' => '',
                            'test' => 'testBool',
                            'type' => 'boolean',
                            'null' => true
                        ),
                        'rest_client_baseurl' => array(
                            'level' => 1,
                            'description' => __('If left empty, the baseurl of your MISP is used. However, in some instances (such as port-forwarded VM installations) this will not work. You can override the baseurl with a url through which your MISP can reach itself (typically https://127.0.0.1 would work).'),
                            'value' => false,
                            'errorMessage' => '',
                            'test' => null,
                            'type' => 'string',
                        ),