The Shelly Pro 4PM is a smart, network-connected relay used to monitor and control four electrical circuits. You’ll find it in homes, smart buildings, and light industrial environments—turning things on and off, measuring power, and playing nicely with automation platforms via its web interface and APIs. In our research, Nozomi Networks Labs discovered an impactful issue: by sending an oversized request through the device’s remote-control interface, an attacker (or even a misbehaving integration) can force the Shelly Pro 4PM to reboot. You could suddenly find your home in the dark—or in some setups be unable to open a garage or gate remotely. This denial-of-service behavior isn’t limited to a single function; it affects 30 API methods, making repeatable outages possible without special privileges. Besides the disclosure of this vulnerability, in this blogpost we’ll show all the steps we used to analyze the Shelly Pro 4PM embedded firmware, reverse engineer the RPC mechanism code and discover the attack surface exposed by this functionality. We hope that by sharing the approach we used, also other researchers can use it to explore similar targets—so we can collectively raise the security bar for connected home and building systems.
リサーチ範囲
The Shelly Pro 4PM is a four‑channel smart relay with power metering on each channel. It mounts on a DIN rail and connects by Ethernet, Wi‑Fi, or Bluetooth. Setup and day‑to‑day control are handled through a local web dashboard. After joining that local network, the device exposes a dashboard that users can use to easily manage all functionalities of the product, such as:
- Switching relays on or off
- View live power readings
- Set schedules and timers
- Configure options such as MQTT, WebSocket or cloud access
Each relay channel controls a single circuit and has an “Action on power on” setting that determines its state after a reboot (On, Off, Restore last, or Match input). If the device encounters an unexpected fault and reboots, it briefly stops responding; when it comes back online, each channel applies that setting. If a channel returns to Off (such as during a reboot), the circuits it feeds—lighting, or an outlet powering your Wi-Fi, intercom, or garage/gate controller—remain down until you restore power or toggle the relay.

Modern Shelly (Gen2+) devices also provide a Remote Procedure Call (RPC) API so that third-party software can perform the same actions over the network. The API follows JSON‑RPC 2.0 and is available over several channels: HTTP, WebSocket, and MQTT. Our research focused on the HTTP RPC channel because it is widely used and straightforward to test. Specifically, clients that need to interact with the HTTP RPCs can just send an HTTP request with a JSON as body with an id, a method, and optional params to /rpc API. For instance, if you need to list KVS (i.e., Key-Value Store) pairs, you can use the KVS.List method with the following parameters:
$ curl -X POST -d '{"id":1,"method":"KVS.List","params":{"match":"item"}}' http://${SHELLY}/rpc
If some items are found, a response like this is returned:
{
"id": 1,
"src": "shellyplus1-a8032abe54dc",
"params": {
"keys": {
"item1": {
"etag": "0DWty8HwCB"
},
"item2": {
"etag": "0DMHqWL0P0"
}
},
"rev": 2744
}
}
Within this API, we looked closely not only at the Key‑Value Store (KVS) methods—KVS.List, KVS.Get, and KVS.GetMany— but also all methods (e.g., Shelly) that process user input as string. This made them suitable for attack vectors that require verification on how the firmware handles the untrusted input.
これらの脆弱性の影響
The input‑handling weakness we identified in the Shelly Pro 4PM’s JSON‑RPC interface can present meaningful operational risks—especially where the relay controls critical loads. Below are examples of potential attack scenarios that could arise from its exploitation:
- Denial of Control (T0813). A crafted oversized request can force the device to reboot. During these events, the relay becomes temporarily unavailable, which can lead to missed schedules, loss of remote control, and service interruptions. Repeated requests can prolong downtime and increase maintenance effort.
- Denial of View (T0815). While the device is restarting, power‑metering data and status updates are not available. This creates blind spots in dashboards and alerting, reducing the ability to detect abnormal consumption or confirm that commands have taken effect.
- Loss of Safety (T0880). When the relay governs pumps, heaters, gates, lighting, or similar equipment, an outage can delay on/off actions or leave circuits in an undesired state until staff intervene. In facilities that depend on timely switching (e.g., HVAC or water circulation), this can translate into discomfort, process deviation, or equipment stress.
In summary, although this weakness does not enable data theft or code execution, its ability to repeatedly degrade availability makes it a significant reliability concern in both home and building automation contexts.
脆弱性リストと影響を受けるバージョン
Nozomi Networks identified the following issue in Shelly Pro 4PM version 1.4.4:
脆弱性スポットライト
Gathering Firmware Image
The Shelly Smart Control Android app lets you easily manage all your Shelly smart home devices from anywhere, either via the cloud or locally. Through this app, you’re able to configure and manage all functionalities of the Shelly Pro 4 PM, such as the firmware update.

Leveraging the firmware update check functionality of the Android app, we noticed in the following log message in the Diagnostic webpage:
shelly_http_client.:303 0x3ffef5ec: HTTPS GET https://updates.shelly.cloud/update/Pro4PM (CA shelly_cloud.pem,ca.pem)
Reaching this HTTP endpoint, we discovered that it shows where the last firmware update is located:
$ curl -k -v https://updates.shelly.cloud/update/Pro4PM
{
"stable": {
"version": "1.4.4",
"build_id": "20241011-114451/1.4.4-g6d2a586",
"url": "https://fwcdn.shelly.cloud/gen2/Pro4PM/76cfaf1e4bb3cb311c0f752b1c17c3d58aa217e3934361df56090ac0567ae86c"
},
"time": 1731591067
}
Having this information, we successfully downloaded the firmware image with the following command:
$ wget --no-check-certificate "https://fwcdn.shelly.cloud/gen2/Pro4PM/76cfaf1e4bb3cb311c0f752b1c17c3d58aa217e3934361df56090ac0567ae86c"
Shelly Firmware Analysis
The firmware image we downloaded is an unencrypted ZIP archive file with the following content:
$ unzip -l 76cfaf1e4bb3cb311c0f752b1c17c3d58aa217e3934361df56090ac0567ae86c.zip
Archive: 76cfaf1e4bb3cb311c0f752b1c17c3d58aa217e3934361df56090ac0567ae86c.zip
Length Date Time Name
--------- ---------- ----- ----
1598 1980-00-00 00:00 manifest.json
25280 1980-00-00 00:00 bootloader.bin
4096 1980-00-00 00:00 partition-table.bin
8192 1980-00-00 00:00 otadata.bin
1961872 1980-00-00 00:00 Pro4PM.bin
524288 1980-00-00 00:00 fs.img
--------- -------
2525326 6 files
Analyzing the manifest.json file, we discover that the target hardware architecture is ESP32 and the information of the mapping where the *.bin files are stored in the device’s flash memory:
$ head -n20 manifest.json
{
"name": "Pro4PM",
"platform": "esp32",
"version": "1.4.4",
"build_id": "20241011-114451/1.4.4-g6d2a586",
"build_timestamp": "2024-10-11T11:44:51Z",
"parts": {
"boot": {
"type": "boot",
"src": "bootloader.bin",
"addr": 4096,
"size": 25280,
"cs_sha1": "4943c9bbc147d5af910876b0b8ddcba6f072a144",
"cs_sha256": "2c3c7a476d016023b74a5975a42b39c397c360ec921cecebc3876656ba64eaa3",
"encrypt": true,
"min_version": "1.0.0"
},
Using the esp32partgen.py script (from the esp32knife GitHub project) we can extract additional information from the partition-table.bin file:
$ python3 esp32partgen.py --no-verify partition-table.bin
Parsing binary partition input...
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs ,data ,nvs ,0x9000 ,16K,
otadata ,data ,ota ,0xd000 ,8K ,encrypted
app_0 ,app ,ota_0 ,0x10000 ,2M ,encrypted
fs_0 ,data ,spiffs ,0x210000 ,512K ,encrypted
app_1 ,app ,ota_1 ,0x290000 ,2M ,encrypted
fs_1 ,data ,spiffs ,0x490000 ,512K ,encrypted
aux ,85,0 ,0x510000 ,2992K ,
shelly ,data,136 ,0x7fc000 ,16K ,encrypted
Beyond the partition table and the bootloader, it’s clear that the app_0 and app_1 sections are the two slots that hold main Pro4PM.bin executable, the core firmware image that implements all core functionalities offered by the Shelly Pro 4PM device.
Using the esptool.py from Espressif, we can retrieve information of the Shelly application:
$ python3 esptool.py -c esp32 image_info Pro4PM.bin
esptool.py v3.0-dev
Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?
Image version: 1
Entry point: 400812ac
6 segments
Segment 1: len 0x43614 load 0x3f400020 file_offs 0x00000018 include_in_checksum=True [DROM]
Segment 2: len 0x00004 load 0x3ff80000 file_offs 0x00043634 include_in_checksum=True [RTC_DRAM]
Segment 3: len 0x03b90 load 0x3ffbdb60 file_offs 0x00043640 include_in_checksum=True [BYTE_ACCESSIBLE, DRAM]
Segment 4: len 0x08e38 load 0x40080000 file_offs 0x000471d8 include_in_checksum=True [IRAM]
Segment 5: len 0x178a00 load 0x400d0020 file_offs 0x00050018 include_in_checksum=True [IROM]
Segment 6: len 0x16544 load 0x40088e38 file_offs 0x001c8a20 include_in_checksum=True [IRAM]
Checksum: b0 (valid)
Validation Hash: 6fac70b80f2abbdfaa0adefe525df09d0e87bd36b27fea40441e3112f0445b60 (valid)
Reconstructing the Flash Memory Image
Popular ESP32 reverse-engineering tools, such as esp32knife, expect a single contiguous flash image as input of the analysis. For this reason, we wrote the following Python script to build the 8MB flash memory (i.e., shelly_flash.bin binary file) starting from the individual binary files located in the firmware update ZIP archive. The location where these binary files must be placed is retrieved from the manifest.json file and the output returned after parsing the partition table. This is the pseudo code that accomplish this task:
FLASH_SIZE = 8 * 1024 * 1024
flash = bytearray(FLASH_SIZE)
place(‘bootloader.bin’, 0x1000)
place(‘partition-table.bin’, 0x8000)
place('otadata.bin', 0x00_0D000)
place('Pro4PM.bin', 0x00_10000) # app_0
place('fs.img', 0x00_210000) # fs_0
place('Pro4PM.bin', 0x00_290000) # app_1
place('fs.img', 0x00_490000) # fs_1
write('shelly_flash.bin', flash)
Finally, since the Shelly firmware binaries has sections not supported by the original esp32knife tool, we fixed it with the following changes:

After applying these patches, we were able to properly parse the reconstructed flash memory and produce the final ELF file that could be then reverse engineering with static analysis tools:
$ python3 esp32knife.py --chip esp32 load_from_file shelly_flash.bin
Prepare output directories:
- creating directory: parsed
Reading firmware from: shelly_flash.bin
Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?
Writing bootloader to: parsed/bootloader.bin
...
Adding program headers
combine section .iram0.vectors and .iram0.text in one program segment
prg_seg 0 : 3f400020 00043614 rw .flash.rodata
prg_seg 1 : 3ff80000 00000004 rw .rtc.dummy
prg_seg 2 : 3ffbdb60 00003b90 rw .dram0.data
prg_seg 3 : 40080000 0001f37c rwx .iram0.vectors
prg_seg 4 : 400d0020 00178a00 rx .flash.text
Program Headers:
Program Headers:
Type Offset VirtAddr PhysAddr FileSize MemSize Flg Align
1 0000029b 3f400020 3f400020 00043614 00043614 6 1000
1 000438af 3ff80000 3ff80000 00000004 00000004 6 1000
1 000438b3 3ffbdb60 3ffbdb60 00003b90 00003b90 6 1000
1 00047443 40080000 40080000 0001f37c 0001f37c 7 1000
1 000667bf 400d0020 400d0020 00178a00 00178a00 5 1000
Writing ELF to parsed/part.4.app_1.elf...
Recovering Information About Stripped ELF
As expected, the Pro4PM.elf (i.e., part.4.app_1.elf) executable file does not contain any symbols. To partially overcome this situation, we spotted that the firmware uses a set of logging functions to debug the application:

As we can see, some of them report the source code where the function is in, while others specify also the original function name. Thanks to these logging functions, we developed a Ghidra script that automatically renames a function.
For instance, if a function uses the following debug function:
FUN_401acb04(0,"shos_dns_sd_packets.cpp",0xc2,(byte *)"Domain parse: %zu %d ",*param_3,0);
And it’s original name was FUN_4011fa20, then the will rename it as:
shos_dns_sd_packets.cpp--FUN_4011fa20
Thanks to these steps, we were able to group unknown functions to their corresponding source code file and partially understand their usage in the firmware of the Shelly Pro 4PM.
Vulnerability Research
The next step of our research had the goal of identify how the RPC mechanism is implemented and handled by the Shelly Pro 4PM firmware. By searching for the known RPC methods, we were easily able to spot that each group of functions (e.g., KVS methods) are registered by a dedicated function. As shown in the following evidence, the shos_rpc_inst.c--FUN_40127840 function is used to map the KVS method (e.g., KVS.List) with the corresponding handler function (e.g., shelly_kvs_rpc.cpp--handle_KVS.List).

As shown in the following evidence, one of the first instruction executed by each method handler is to call the json_scanf function that is part of the open-source Frozen library made by Cesanta:
int json_scanf(const char *str, int str_len, const char *fmt, ...);

An example of usage of the json_scanf function is:
json_scanf(str, strlen(str), "{match: %Q}", &s)
where the str variable points to the "{match: AAAAA…}" string received as argument from the RPC.
Leveraging the source code of the Frozen library, we identified that the Frozen library's %Q format specifier allocates memory for string values without appropriate bounds checking: when a client sends an RPC request that contains an extremely long match value, the parser attempts to allocate enough space to store the entire string. However, on a constrained device, this exhausts heap memory and triggers a crash/reboot on the target device.

The crash is easy to reproduce via the HTTP RPC channel, for example by sending an HTTP POST request to KVS.List with an oversized match string:
$ curl -X POST -d '{"id":1,"method":"KVS.List","params":{"match":"AAAA…"}}' http://${SHELLY}/rpc
This finding is extremely important due to the following reasons:
- Pre-Authentication Vulnerability. The vulnerability occurs during the initial parsing phase, before any authentication validation or access control lists (ACLs) are checked.
- Universal Attack Vector. The vulnerability can be triggered using any RPC method name, including non-existent methods, as the parsing occurs before method validation. Additionally, the very same results can be reproduced across all the network protocols that expose the RPC mechanism, such as: HTTP, WebSocket, and MQTT.
Conclusions and Remediation
Our research showed that unexpected inputs to multiple JSON‑RPC methods on the Shelly Pro 4PM v1.4.4 can exhaust resources and trigger device reboots. While the issue does not enable code execution or data theft, it can be used to systematically cause repeatable outages—impacting automation routines and visibility in both home and building contexts.
The most effective actions to be protected against this kind of attacks are:
- Apply firmware updates. Update to 1.6.0 (or later) once available via the local web interface or the Shelly Smart Control application.
- Minimize exposure. Avoid direct internet exposure. Restrict web and /rpc access to trusted management networks or VPNs.
To help organizations promptly identify whether the vulnerable device is present in their environment—and to detect and alert on exploitation attempts before they lead to operational disruption, compromise, or further attack progression – customers can rely on the advanced capabilities of Nozomi Networks OT/IoT Security Platform. The platform provides deep visibility into network traffic and device behavior, enabling effective vulnerability and threat detection across OT and IoT networks.



This proactive monitoring empowers security teams to respond to vulnerabilities and attacks swiftly and effectively, minimizing the impact of attacks targeting critical networks. To learn more about Nozomi Networks OT/IoT Security Platform and see it in action, request a demo today.
Consistent with Nozomi Networks Labs’ mission, our goal is to equip operators and vendors with actionable insight to strengthen the reliability of connected systems. For this reason, we will continue to collaborate responsibly and share knowledge about how to make smart environments safer and more resilient from cyber security attacks.



