Unauthenticated Remote Code Execution in Narrowcasting client

An unauthenticated remote code execution (RCE) vulnerability was found in a narrowcasting product. Now this exploit is very dear to my hearth because it’s my first ‘cool’ exploit. Narrowcasting systems are the information screens you see in big venues or reception halls. There are a lot of vendors creating this kind of software and I won’t disclose this vendor.

How does it work?

An admin logs into a management server and prepares content for the screens. This server then pushes the content to all the narrowcasting clients (the tv on the wall). My employer uses a narrowcasting system and I always wondered how the communication worked. My personal goal was to get my own information on the screen. I downloaded a copy of this software and ran it on a virtual machine to take look how the communication works.

An XMLrpc service was identified running on port 1113. Querying the XMLrpc service for its functions returned a nice list. Sadly the server was not cooperating when doing requests to this service:

On port 1115 a raw TCP socket was found. Functions exposed by the XMLrpc service could also be used on this port. This port is exposed on the network and doesn’t require any authentication or encryption. The list of found functions was nice but not that useful:

Now our vendor created the client using a Python 2.4 executable. The python code is compiled to an executable using Py2Exe, this process can be reversed using a bit of software called uncompyle. After decompiling the python file responsible for port 1115 some additional functions were found:

  • Reboot
  • downloadURL(url, dst)
  • createdir(dir)
  • upload(file, dst)
  • move(src, dst)
  • SYSEXEC

With these functions we should be able to get our own information on the screen. But.. wait.. SYSEXEC? All functions were pretty well defined and explained in the source but SYSEXEC was muffled away behind 2 includes (they knew!). SYSEXEC does a call to a custom class called SystemHandler.

The class is pretty empty but based on the parameters given: a python file is dynamically included and executed. The included python file has to be in the ‘system’ folder of the application. By default, the folder does not even exist. But for example, the command:

SYSEXEC|execute

Will import ‘system/execute.py’, from the imported file the class TSYS() is called, then a function called proces() is executed. I suspect they use this to do maintenance and then remove the functions afterwards. But we have a create directory and upload file function on port 1115. Can we create and upload our own files and execute SYSEXEC?

Weaponizing our findings

We need a python file that can be called from the class SystemHandler. The following code takes a command from the arguments, runs it though cmd.exe and returns the output:

Proof of Concept:

  • Connect to port 1115
  • Create a system directory: createdir|system
  • Upload our file: upload|system/cmd.py <line break><content of file> (just disconnect when upload is done)
  • Call our newly created function over port 1115: SYSEXEC|cmd|whoami

Time Line:

  • 02/12/2019: Initial disclosure to vendor
  • 03/12/2019: Vendor acknowledges the problem but classified as won’t fix
  • 04/12/2019: Informed vendor I’ll disclose the vulnerability in 3 months
  • 07/12/2019: Vendor acknowledges the problem (fix will take approximately 3 months)
  • 05/02/2020: Fix has been deployed