Back to skill

Security audit

embedded-engineer

Security checks for vulnerabilities and agentic risk

Overview

This embedded engineering skill is coherent, but its example code includes unsafe IoT and industrial control patterns that users could copy without adequate warnings.

Review carefully before installing or using as a coding reference. Treat the included IoT and industrial examples as demonstration sketches only, and require secure OTA signing, TLS, authenticated command channels, scoped network binding, access control, and explicit warnings before adapting this material for real devices.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
references/examples.md:114
Finding
Unauthenticated Remote Firmware Retrieval and Execution<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md`, lines 114–159 and 554–558 **Vulnerability Type**: Insecure over-the-air firmware update mechanism **Risk Level**: Critical ### Vulnerable Code ```cpp void checkForUpdate() { if (updateInProgress) return; HTTPClient http; http.begin("http://update.server.com/firmware/latest.json"); int httpCode = http.GET(); if (httpCode == HTTP_CODE_OK) { DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, http.getStream()); if (!error) { const char* latestVersion = doc["version"]; const char* updateUrl = doc["url"]; if (strcmp(latestVersion, FIRMWARE_VERSION) > 0) { performUpdate(updateUrl); } } } http.end(); } void performUpdate(const char* url) { updateInProgress = true; WiFiClient client; t_httpUpdate_return ret = httpUpdate.update(client, url); switch(ret) { case HTTP_UPDATE_FAILED: Serial.printf("Update failed: %s\n", httpUpdate.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: Serial.println("No updates available"); break; case HTTP_UPDATE_OK: Serial.println("Update successful, restarting..."); ESP.restart(); break; } updateInProgress = false; } ``` The update check is also invoked automatically: ```cpp // Periodic OTA check (once per hour) static unsigned long lastOTACheck = 0; if (millis() - lastOTACheck > 3600000) { otaHandler.checkForUpdate(); lastOTACheck = millis(); } ``` ### Technical Analysis Firmware metadata is retrieved over plaintext HTTP. The `url` field from that unauthenticated response is passed directly to `httpUpdate.update()` using a plaintext `WiFiClient`. The implementation does not e ...[truncated 1725 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Retrieve update manifests and images exclusively over HTTPS with strict certificate and hostname validation. - Consider certificate or public-key pinning for dedicated update infrastructure. - Reject update URLs whose scheme or host is not explicitly allowlisted. - Sign update manifests and firmware images with an offline-controlled signing key. - Embed only the trusted verification public key in the device and verify signatures before writing firmware. - Bind the signed manifest to the device model, hardware revision, firmware version, image length, and cryptographic digest. - Implement monotonic versioning and secure anti-rollback protection. - Stage updates in a non-active partition and validate them before changing the boot partition. - Retain a known-good recovery image and implement rollback after boot validation failure. - Fail closed on network, parsing, certificate, signature, version, or integrity errors. - Avoid accepting firmware locations directly from an unsigned network response. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/examples.md:326
Finding
Unauthenticated Commands over a Plaintext Public MQTT Channel<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md`, lines 32–33 and 326–400 **Vulnerability Type**: Missing transport security, client authentication, and command authorization **Risk Level**: High ### Vulnerable Code ```cpp const char* mqtt_server = "broker.hivemq.com"; const int mqtt_port = 1883; ``` ```cpp void reconnectMQTT() { if (mqtt.connect(DEVICE_ID)) { Serial.println("MQTT connected"); // Subscribe to command topics mqtt.subscribe("iot/devices/ESP32_SENSOR_001/commands"); mqtt.subscribe("iot/devices/ESP32_SENSOR_001/config"); mqtt.subscribe("iot/broadcast/firmware"); // Publish online status StaticJsonDocument<256> doc; doc["device_id"] = DEVICE_ID; doc["status"] = "online"; doc["firmware"] = FIRMWARE_VERSION; doc["ip"] = WiFi.localIP().toString(); char buffer[256]; serializeJson(doc, buffer); mqtt.publish("iot/devices/ESP32_SENSOR_001/status", buffer, true); } } ``` ```cpp void mqttCallback(char* topic, byte* payload, unsigned int length) { StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, payload, length); if (error) { Serial.print("JSON parse error: "); Serial.println(error.c_str()); return; } if (strcmp(topic, "iot/devices/ESP32_SENSOR_001/commands") == 0) { const char* command = doc["command"]; if (strcmp(command, "restart") == 0) { ESP.restart(); } else if (strcmp(command, "update") == 0) { otaHandler.checkForUpdate(); } else if (strcmp(command, "calibrate") == 0) { calibrateSensors(); } } } ``` ### Technical Analysis The ESP32 connects to `broker.hivemq.com` on the standard plaintext MQTT port, 1883, using `WiFiClient`. The call to `mqtt.connect(DEVICE_ID)` supplies no username, password, client cer ...[truncated 1804 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use MQTT over TLS, normally port 8883, through `WiFiClientSecure`. - Validate the broker certificate and hostname; do not disable certificate verification. - Provision a unique client certificate or unique high-entropy credential for every device. - Configure broker ACLs so each device can subscribe and publish only to its authorized topics. - Avoid public shared brokers for production command and telemetry channels. - Use unique, provisioned device identities rather than a fixed example identifier. - Cryptographically sign command envelopes and validate the signature on the device. - Include a timestamp, expiration, nonce, and monotonically increasing sequence number to prevent replay. - Apply explicit authorization per command and separate high-risk administration commands from ordinary telemetry. - Rate-limit restart, update, and calibration commands. - Validate the JSON schema, field presence, type, length, and allowed values before use. - Log accepted and rejected administrative commands without disclosing secrets. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/examples.md:30
Finding
Hard-Coded Wi-Fi Credential in ESP32 Source<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md`, lines 30–31 **Vulnerability Type**: Hard-coded plaintext credential **Risk Level**: Medium ### Vulnerable Code ```cpp const char* ssid = "IoT_Network"; const char* password = "SecurePassword123"; ``` ### Technical Analysis The Wi-Fi network name and password are embedded directly in source code. If developers copy the example without replacing the credential-management design, the password becomes available to everyone with source access and is also likely to be recoverable from compiled firmware through static analysis or memory extraction. A single static password also prevents per-device revocation and encourages credential reuse across an entire device fleet. Although the value may be illustrative, it is presented in executable example code rather than as an unmistakable non-secret placeholder or secure provisioning mechanism. ### Attack Path 1. An attacker obtains the source code, firmware image, development artifact, or physical access to a device. 2. The attacker searches strings or firmware data sections for the Wi-Fi network name and password. 3. The attacker moves within range of a deployment using the unchanged or reused credential. 4. The attacker authenticates to the Wi-Fi network. 5. The attacker accesses reachable embedded devices or network services and may then exploit the plaintext MQTT and OTA weaknesses. ### Impact Assessment If the example credential is retained or reused, an attacker may gain unauthorized access to the deployment network. The resulting scope depends on network segmentation but can include device discovery, traffic interception, command injection, lateral movement, and access to other systems sharing the network. The credential itself does not directly grant firmware-level privileges. However, network access materially facilitates exploitation of the insecure MQTT command channel and unauthenticated firmware update mechanism. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Remove operational credentials from source code and documentation examples. - Use explicit placeholders such as `WIFI_PASSWORD_FROM_SECURE_PROVISIONING`. - Provision unique credentials per device through a secure onboarding process. - Store provisioned credentials in protected nonvolatile storage supported by the target platform. - Protect credentials at rest using hardware-backed encryption where available. - Prevent credentials from being committed through secret scanning and repository controls. - Support credential rotation and per-device revocation. - Avoid fleet-wide shared passwords. - Segment IoT devices onto a restricted network even when strong credentials are used. - Rotate the password immediately if the displayed value has ever been used in a real environment. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/examples.md:811
Finding
Unauthenticated Writable OPC UA Service Exposed on All Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `references/examples.md`, lines 811–840 **Vulnerability Type**: Insecure industrial service exposure and missing access control **Risk Level**: High ### Vulnerable Code ```python class OPCUAHandler: """OPC UA protocol handler""" def __init__(self): self.server = Server() self.server.set_endpoint("opc.tcp://0.0.0.0:4840") self.server.set_server_name("Industrial IoT Gateway") # Setup namespace uri = "http://industrial.iot.gateway" self.idx = self.server.register_namespace(uri) # Create objects self.objects = self.server.get_objects_node() self.device = self.objects.add_object(self.idx, "Gateway") async def start(self): """Start OPC UA server""" self.server.start() logger.info("OPC UA server started") async def add_variable(self, name: str, value: Any) -> opcua.Node: """Add a variable to the server""" var = self.device.add_variable(self.idx, name, value) var.set_writable() return var async def update_variable(self, node: opcua.Node, value: Any): """Update variable value""" node.set_value(value) ``` ### Technical Analysis The OPC UA server binds to `0.0.0.0:4840`, exposing it on every available network interface. Variables created through `add_variable()` are explicitly marked writable. The example does not configure OPC UA security policies, message encryption, server certificates, client certificate trust, user authentication, role-based permissions, or node-level authorization. As a result, reachable clients may be able to establish an anonymous or otherwise insufficiently protected session and modify exposed variables. The risk is elevated because OPC UA commonly carries industrial process information and control values. Writable nodes should never rely only on network location as their security b ...[truncated 1250 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Bind the service only to the dedicated industrial interface or management address that requires OPC UA access. - Enforce host firewall rules and network segmentation around TCP port 4840. - Disable anonymous access. - Configure a strong OPC UA security policy with message signing and encryption. - Provision a unique server certificate and establish an explicit trust list for authorized client certificates. - Require authenticated user or application identities. - Implement role-based and node-level authorization using least privilege. - Keep variables read-only unless remote writes are explicitly required. - For writable nodes, enforce type checks, safe ranges, state-dependent constraints, and rate limits. - Separate monitoring nodes from control and configuration nodes. - Record security-relevant session, browse, and write events in tamper-resistant audit logs. - Reject insecure endpoints and deprecated security policies in production. ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The ESP32 example includes insecure OTA behavior and destructive reset functionality without warnings or safeguards: it fetches update metadata over plain HTTP, performs firmware updates from a URL without visible authenticity verification, accepts remote MQTT-triggered update/restart commands, and exposes a factory reset path that erases storage. In an embedded/IoT skill, this is more dangerous because readers may reuse the example in production firmware, leading to remote code execution through malicious firmware delivery or device wipe/denial of service.

Intent-Code Divergence

Low
Confidence
91% confidence
Finding
At L1403-L1405, the doc comment says the communication task "handles UART, CAN, and network protocols." However, the implemented body at L1431-L1456 processes UART traffic, CAN messages, and a local Modbus RTU handler, with no actual network stack or network protocol handling shown. This is an active documentation-to-code mismatch rather than a mere omission.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/examples.md:31