ELF Modified UPX — Breakglass Intelligence Report
TLP: WHITE Date: 2026-04-03 Analyst: GHOST (Breakglass Intelligence) Classification: FALSE POSITIVE — Legitimate Industrial Firmware Source: @TuringAlex (Twitter/X)
Executive Summary
An ELF binary (MD5: da2e396baf23de1881d06dd3377f84a6) flagged as suspicious due to modified UPX packing was investigated. After unpacking by restoring the modified OPS! magic bytes to standard UPX!, full static analysis of the 1.2MB unpacked binary reveals this is legitimate OpenPLC Editor firmware compiled for an Arduino MKR Zero (ARM Cortex-M0+, SAMD21) running a traffic light controller over Modbus TCP/Ethernet. The binary is not malware. It is an industrial control system (ICS) firmware image that was packed with a modified UPX variant, triggering multiple anti-unpacking YARA rules and resulting in false positive classifications by 13/38 AV engines.
Key Findings
- NOT MALWARE: Binary is legitimate OpenPLC firmware for Arduino MKR Zero hardware
- Developer identified: Windows user
sergiowith OpenPLC Editor installed at\Users\sergio\OpenPLC_Editor - Purpose: Traffic light PLC controller with Modbus TCP slave functionality over W5100 Ethernet
- Modified UPX: Magic bytes changed from
UPX!toOPS!at three locations (offsets 0x78, 0x728D2, 0x728DC) - Network config: Device IP 192.168.1.195, Gateway 192.168.1.1, Subnet 255.255.255.0
- Embedded hash: MD5
558d148d2b08815643ddd5ae90d8a221(likely firmware debug checksum) - Compiler: GCC 7.2.1 (GNU Tools for ARM Embedded Processors 7-2017-q4-major)
- AV false positives: 13/38 engines flagged as suspicious/malicious (ReversingLabs:
Linux.Trojan.Generic)
What Was Found vs. What Was Suspected
| Aspect | Initial Suspicion | Our Findings |
|---|---|---|
| File type | Malicious ELF binary | Legitimate ICS/SCADA firmware |
| UPX modification | Anti-analysis evasion | Unknown reason; possibly OpenPLC build toolchain artifact |
| Architecture | IoT botnet (ARM) | Arduino MKR Zero (ARM Cortex-M0+ SAMD21) |
| Purpose | C2 beacon / DDoS bot | Traffic light controller with Modbus TCP |
| Network activity | C2 communication | Modbus TCP slave on LAN (192.168.1.0/24) |
| IPv6 YARA hit | IPv6-based C2 | Ethernet library IPv6 socket support structures |
| Origin (Hungary) | Attacker infrastructure | Likely developer/researcher upload location |
Sample Details
| Field | Value |
|---|---|
| MD5 | da2e396baf23de1881d06dd3377f84a6 |
| SHA256 | 6439834bec1cc530b12b1d821a509561efdd43048ecfb183939fe00a11a3c7dd |
| SHA1 | 8e1bf4a68904dec5ebd92d1f07621a52464ef6e7 |
| SSDEEP | 12288:18Z3vxICUzUWBMH79C2EwdvAFhhvX2k4HQ:q5Ir7BMbwVyAFhhukmQ |
| TLSH | T174A4237E146B7157DA9262A6C95CC17B22BFBE809B46432DE732504336005ECCEFEB64 |
| TELFhash | t1f490045c1dd03100371c0044535dc40ff3001503f50f55030d00c5d3c3c04430cc1c10 |
| File type | ELF 32-bit LSB executable, ARM, EABI5, statically linked |
| Packed size | 469,248 bytes |
| Unpacked size | 1,197,456 bytes (39.19% compression ratio) |
| UPX version | 4.22 (with modified magic bytes) |
| Original filename | dulime_tl |
| MalwareBazaar first seen | 2026-03-12 |
| ReversingLabs first seen | 2025-08-22 |
| Kaspersky first seen | 2025-10-27 |
Modified UPX Analysis
Magic Byte Modification
The standard UPX packer uses the magic bytes UPX! (hex 55 50 58 21) in three locations within packed ELF binaries:
- l_info structure (offset 0x78): Immediately after ELF header + program headers. Contains the loader checksum, magic, size, version, and format fields.
- Packed data trailer (offset 0x728D2): End-of-packed-data marker.
- Packed data trailer (offset 0x728DC): Secondary marker within 10 bytes of the first.
In this sample, all three instances were changed to OPS! (hex 4F 50 53 21). This single-character substitution (U->O, P->P, X->S) prevents the standard upx -d decompressor from recognizing the file, but the underlying NRV2E compression and ELF loader stub remain functional.
l_info Structure at Offset 0x74
Offset 0x74: d2 80 af b6 (checksum)
Offset 0x78: 4f 50 53 21 (magic: "OPS!" — should be "UPX!")
Offset 0x7c: 8c 0a (l_lsize: 2700)
Offset 0x7e: 0e (l_version: 14)
Offset 0x7f: 17 (l_format: 23 — ELF/ARM)
Unpacking Method
Restoring the three OPS! instances to UPX! allows standard UPX 4.2.4 to decompress the binary successfully:
upx -d patched.elf -o unpacked.elf
1197456 <- 469248 39.19% linux/arm unpacked.elf
Why Modified UPX?
This is not necessarily malicious. Possible explanations:
- OpenPLC build toolchain: The OpenPLC Editor Arduino build pipeline may use a customized UPX variant for firmware deployment
- Size optimization: Arduino MKR Zero has limited flash; UPX reduces firmware size by 60%
- Accidental: Developer may have used a forked UPX with modified constants
- Deliberate obfuscation: Unlikely given the benign nature of the code, but cannot be ruled out
Firmware Analysis
Architecture & Platform
- Target: Arduino MKR Zero (Atmel SAMD21 — ARM Cortex-M0+)
- ABI: EABI5, soft-float
- Entry point: 0x2001791C (RAM execution — typical for Arduino bootloader chain)
- Linker: Statically linked (all libraries embedded)
- Sections: 19 sections including full debug info (
.debug_info,.debug_str,.debug_line, etc.) - Symbols: 1,503 symbols (NOT stripped — unusual for production firmware)
Source Files (from debug symbols)
| File | Purpose |
|---|---|
Baremetal.ino.cpp | Main Arduino sketch (bare-metal PLC runtime) |
ModbusSlave.cpp | Modbus TCP slave implementation |
Config0.c | PLC configuration (OpenPLC generated) |
POUS.c | Program Organization Units (IEC 61131-3) |
Res0.c | PLC resource definition |
arduino.cpp | OpenPLC Arduino runtime layer |
debug.c | Debug/diagnostic functions |
glueVars.c | I/O variable mapping (PLC <-> hardware) |
Ethernet.cpp, EthernetClient.cpp, etc. | W5100 Ethernet library |
SPI.cpp | SPI bus (W5100 communication) |
w5100.cpp | WIZnet W5100 Ethernet chip driver |
Uart.cpp, CDC.cpp, USBCore.cpp | Serial/USB communication |
Key Functions
| Function | Size (bytes) | Purpose |
|---|---|---|
TRAFFICLIGHT_body__ | 4,318 | Main traffic light control logic (PLC scan cycle) |
TRAFFICLIGHT_init__ | 1,040 | Traffic light state initialization |
modbusTask() | 240 | Modbus TCP communication handler |
plcCycleTask() | 32 | PLC scan cycle scheduler |
handle_serial() | 304 | Serial debug interface |
glueVars() | 144 | I/O variable synchronization |
mbconfig_ethernet() | 120 | Modbus Ethernet configuration |
setup() | 160 | Arduino initialization |
loop() | 60 | Main loop (calls scheduler) |
Modbus Configuration
The firmware implements a Modbus TCP Slave supporting:
MB_FC_READ_COILS— Read discrete outputsMB_FC_READ_INPUT_STAT— Read discrete inputsMB_FC_READ_REGS— Read holding registersMB_FC_READ_INPUT_REGS— Read input registersMB_FC_WRITE_COIL— Write single coilMB_FC_WRITE_COILS— Write multiple coilsMB_FC_WRITE_REG— Write single registerMB_FC_WRITE_REGS— Write multiple registers
Error handling: MB_EX_ILLEGAL_ADDRESS, MB_EX_SLAVE_FAILURE
Embedded Network Configuration
Located at offset 0xB7CA in the unpacked binary:
| Field | Value | Offset |
|---|---|---|
| Debug MAC pattern | DE:AD:BE:EF:DE:AD | 0xB7CC |
| Device IP | 192.168.1.195 | 0xB7D2 |
| Gateway | 192.168.1.1 | 0xB7D6 |
| Subnet mask | 255.255.255.0 | 0xB7DA |
| Debug MD5 hash | 558d148d2b08815643ddd5ae90d8a221 | 0xB7DE |
The MAC address DE:AD:BE:EF:DE:AD is a well-known placeholder/test value, confirming this is development/test firmware.
I/O Pin Mapping
The glueVars.c module maps PLC I/O variables to Arduino pins:
- Digital inputs:
__IX0_0through__QX1_2(traffic light sensor inputs) - Digital outputs:
__QX0_0through__QX0_7(traffic light signal outputs) - Pin masks: DOUT pins 07-0C, AIN pins 10-15
Developer Attribution
| Field | Value | Confidence |
|---|---|---|
| Username | sergio | DEFINITIVE — embedded in 20+ debug paths |
| OS | Windows | DEFINITIVE — backslash paths throughout |
| IDE | OpenPLC Editor + Arduino IDE | DEFINITIVE — build paths |
| Arduino BSP | SAMD 1.8.13 | DEFINITIVE — full path in debug strings |
| GCC version | 7.2.1 20170904 (ARM embedded 7-2017-q4-major) | DEFINITIVE — .comment section |
| Project path | \Users\sergio\OpenPLC_Editor\editor\arduino\examples\Baremetal | DEFINITIVE |
| Arduino libs | \Users\sergio\Documents\Arduino\libraries\Ethernet\ | DEFINITIVE |
| Sketch temp | arduino-sketch-84CE77EEE7210676AFE890442798934C | DEFINITIVE |
The developer "sergio" is using OpenPLC Editor on Windows with Arduino SAMD board support package 1.8.13 installed via Arduino Board Manager. The Ethernet library is installed in the standard Arduino user libraries folder.
Note: "sergio" is likely associated with the OpenPLC project itself. The OpenPLC Editor project on GitHub is maintained by Thiago Alves, but the Baremetal examples and Arduino runtime may have multiple contributors. The path structure \OpenPLC_Editor\editor\arduino\examples\Baremetal matches the standard OpenPLC Editor repository layout.
YARA Rule Analysis (Why It Triggered)
| Rule | Author | Why It Matched | Verdict |
|---|---|---|---|
SUSP_ELF_LNX_UPX_Compressed_File | Florian Roth (Nextron) | UPX-packed ELF binary | TRUE POSITIVE for UPX packing, but packing ≠ malicious |
upx_antiunpack_elf32 | JPCERT/CC | Modified UPX magic (OPS! instead of UPX!) | TRUE POSITIVE — magic bytes ARE modified |
upx_packed_elf_v1 | RandomMalware | Generic UPX-packed ELF detection | TRUE POSITIVE for UPX, FALSE POSITIVE for malware |
linux_generic_ipv6_catcher | @_lubiedo | IPv6 socket structures in Ethernet library | FALSE POSITIVE — benign IPv6 library code |
The JPCERT/CC rule upx_antiunpack_elf32 is the most significant — it specifically detects UPX-packed ELF32 binaries where the magic bytes have been altered, which is a known technique used by Linux botnets (Mirai, Gafgyt, Tsunami). However, in this case, the modification is associated with legitimate firmware rather than malware.
AV Detection Assessment
13 of 38 AV engines flag this binary as suspicious or malicious:
| Vendor | Detection | Assessment |
|---|---|---|
| ReversingLabs | Linux.Trojan.Generic | FALSE POSITIVE |
| Kaspersky | Malware (verdict) | FALSE POSITIVE |
| FileScan.IO | LIKELY_MALICIOUS (0.75 threat level) | FALSE POSITIVE |
| Spamhaus HBL | suspicious | FALSE POSITIVE |
Root cause of false positives:
- ARM ELF + modified UPX = strong heuristic match for IoT botnet patterns
- Statically linked binary with no section headers (packed state) = common in Mirai variants
- Modbus/Ethernet networking code = network communication capability
- Traffic light state machine = complex control flow that heuristic engines flag
MITRE ATT&CK Mapping
Not applicable — this is not malware. The binary does not implement any ATT&CK techniques.
IOC Assessment
No IOCs to report. This binary is legitimate firmware. The following indicators are documented for reference only and should NOT be added to blocklists:
- SHA256:
6439834bec1cc530b12b1d821a509561efdd43048ecfb183939fe00a11a3c7dd— BENIGN - IP 192.168.1.195 — Private/LAN address, device configuration
- IP 192.168.1.1 — Private/LAN gateway
ICS/SCADA Security Observations
While this binary is not malicious, the investigation reveals several ICS security concerns:
- Debug firmware in the wild: Full debug symbols, unstripped binary with developer paths — this should never leave the development environment
- Default/test MAC address:
DE:AD:BE:EF:DE:ADsuggests this is test firmware, not production - No authentication on Modbus: Standard Modbus TCP has no built-in authentication — any device on the 192.168.1.0/24 network can read/write PLC registers
- Hardcoded network config: IP address and gateway are compiled into the firmware rather than configured dynamically
- OpenPLC on real hardware: If deployed in production, an OpenPLC-based traffic light controller with Modbus TCP and no network segmentation would be trivially exploitable
Recommended Actions
For AV Vendors
- Submit false positive reports for SHA256
6439834bec1cc530b12b1d821a509561efdd43048ecfb183939fe00a11a3c7dd - Improve heuristics to differentiate between IoT botnet UPX modification and legitimate firmware packing
- Consider whitelisting OpenPLC firmware signatures
For the OpenPLC Community
- Investigate why the build toolchain produces modified UPX magic bytes
- Strip debug symbols from release firmware to prevent developer information leakage
- Document the UPX packing step in the build pipeline
For ICS/SCADA Defenders
- Do not expose Modbus TCP devices directly to untrusted networks
- Implement network segmentation for PLC-controlled infrastructure
- Monitor for modified UPX-packed binaries in ICS environments — while this sample is benign, the same technique is used by real ICS-targeting malware
Conclusion
This investigation conclusively demonstrates that the sample is legitimate OpenPLC industrial control firmware for an Arduino MKR Zero, implementing a traffic light controller over Modbus TCP. The modified UPX packing (OPS! magic instead of UPX!) caused widespread false positive detections across AV engines and YARA rules designed to catch Linux botnets. The binary contains no malicious code, no C2 infrastructure, no exploitation capabilities, and no data exfiltration mechanisms.
The investigation highlights a persistent problem in threat intelligence: heuristic overlap between IoT botnet packing techniques and legitimate embedded firmware distribution. Modified UPX is a strong signal for malware in the Linux/ARM space, but it produces false positives when applied to the growing ecosystem of ARM-based ICS/IoT devices that use UPX for flash storage optimization.
GHOST — Breakglass Intelligence "One indicator. Total infrastructure. Even when the infrastructure is a traffic light."