Back to skill

Security audit

ESP-IDF Helper

Security checks for vulnerabilities and agentic risk

Overview

The skill is openly for ESP-IDF flashing and packaging, but its helper scripts have unsafe input and path handling that could run unintended commands or include unintended local files.

Install only if you trust the source and are comfortable reviewing or fixing the helper scripts first. Avoid using this with untrusted build directories or flash_args files, avoid the permanent bashrc PATH change unless necessary, and do not run usbipd or flashing workflows with elevated privileges unless you have verified the exact commands and target devices.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/capture_idf_help.sh:14
Finding
Shell Command Injection Through IDF_PATH<![CDATA[ ## Vulnerability Details **File Location**: `scripts/capture_idf_help.sh`, line 14 **Vulnerability Type**: Shell command injection through an environment variable **Risk Level**: High ### Vulnerable Code ```bash if [[ -n "${IDF_PATH:-}" && -f "$ESPIDF_ROOT/export.sh" ]]; then bash -lc "set -euo pipefail; source \"$ESPIDF_ROOT/export.sh\" >/dev/null; idf.py --help" > "$OUT_REL" elif command -v idf.py >/dev/null 2>&1; then idf.py --help > "$OUT_REL" fi ``` ### Technical Analysis `IDF_PATH` is copied into `ESPIDF_ROOT` and then interpolated into a command string passed to `bash -lc`. Although double quotes are added around the intended path, embedded quotes, command substitutions, shell operators, or other shell syntax in the environment-variable value can escape the intended `source` argument. The preliminary `-f "$ESPIDF_ROOT/export.sh"` check does not make interpolation into a second shell safe. A specially named directory can satisfy the file check while its path remains syntactically dangerous when incorporated into the nested command string. ### Attack Path 1. An attacker influences the `IDF_PATH` environment variable or persuades the user to configure ESP-IDF under a specially crafted directory name. 2. The crafted directory contains an `export.sh` file, allowing the file-existence check to pass. 3. The user executes `scripts/capture_idf_help.sh`. 4. The path is embedded into the argument supplied to `bash -lc`. 5. The nested shell parses attacker-controlled syntax as commands rather than treating the entire value as a literal path. 6. Those commands execute with the privileges of the user running the script. ### Impact Assessment Successful exploitation provides arbitrary local command execution under the invoking user's account. The attacker could read or modify files accessible to that user, alter source code or firmware artifacts, tamper with the generated reference file, or execute additional locally available programs. If the scr ...[truncated 92 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Avoid constructing a command string for `bash -lc`. Source the script directly in the current shell while preserving normal shell argument boundaries: ```bash if [[ -n "${IDF_PATH:-}" && -f "$ESPIDF_ROOT/export.sh" ]]; then source "$ESPIDF_ROOT/export.sh" >/dev/null idf.py --help > "$OUT_REL" elif command -v idf.py >/dev/null 2>&1; then idf.py --help > "$OUT_REL" else echo "ERROR: idf.py not found." >&2 exit 1 fi ``` Additionally: 1. Canonicalize `IDF_PATH` with `realpath`. 2. Require `export.sh` to be a regular file in an expected, trusted ESP-IDF checkout. 3. Reject paths containing control characters. 4. Avoid evaluating any environment-derived value as shell source code. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/usbipd_attach_serial.sh:134
Finding
PowerShell Command Injection Through BUSID and DISTRO Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/usbipd_attach_serial.sh`, lines 134–175 **Vulnerability Type**: PowerShell command injection **Risk Level**: High ### Vulnerable Code ```bash process_device() { local busid="$1" local cmd="usbipd attach --wsl --busid=$busid" if [[ -n "$DISTRO" ]]; then cmd+=" --distribution $DISTRO" fi echo "" echo "--- Processing BUSID: $busid ---" echo "Attach command: $cmd" # Bind the device first (admin may be required for first-time bind) local bind_cmd="usbipd bind --busid=$busid" echo "Bind command: $bind_cmd" if [[ "$DRY_RUN" == "1" ]]; then echo "[dry-run] not executing bind/attach" return 0 fi set +e local bind_out bind_out=$(powershell.exe -NoProfile -Command "$bind_cmd" 2>&1) local bind_rc=$? set -e # ... set +e local attach_out attach_out=$(powershell.exe -NoProfile -Command "$cmd" 2>&1) local attach_rc=$? set -e } ``` ### Technical Analysis The values accepted through `--busid` and `--distro` are concatenated directly into PowerShell command text. The resulting strings are passed to `powershell.exe -Command`, which parses them as PowerShell source code. Bash quoting protects the value only while Bash constructs the argument. It does not prevent PowerShell from interpreting separators, expressions, quoting characters, or other PowerShell syntax contained in that argument. Neither value is validated against the expected syntax before execution. Values extracted from `usbipd list` also cross a command-interpreter boundary and should not be assumed safe solely because they originated from command output. ### Attack Path 1. An attacker supplies a malicious `--busid` or `--distro` value, directly or through an untrusted wrapper invoking the script. 2. The script appends that value to `bind_cmd` or `cmd`. 3. The complete string is passed to `powershell.exe -Command`. 4. PowerShell parses the attacker-controlled portion as command syntax ...[truncated 632 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Strictly validate bus IDs before use: ```bash if [[ ! "$busid" =~ ^[0-9]+-[0-9]+$ ]]; then echo "ERROR: Invalid BUSID" >&2 return 2 fi ``` 2. Obtain valid WSL distribution names from `wsl.exe --list --quiet` and require an exact match. 3. Do not concatenate arguments into a PowerShell program string. 4. Pass untrusted values as positional arguments to a fixed PowerShell script block and invoke the target executable with PowerShell's argument operator. 5. Add `-NonInteractive` and use an explicit executable path where practical. 6. Revalidate bus IDs parsed from `usbipd list` before processing them. 7. Keep binding and attachment operations unprivileged whenever possible and clearly separate any action that truly requires elevation. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pack_firmware.sh:39
Finding
Generated Firmware Flash Script Injection via flash_args<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pack_firmware.sh`, lines 39–49 **Additional Affected Locations**: lines 54–151 and 180–298 **Vulnerability Type**: Generated Bash and Windows batch command injection **Risk Level**: High ### Vulnerable Code ```bash FLASH_PARAMS=$(head -1 "${FLASH_ARGS_FILE}") FIRMWARE_LIST="" WIN_FIRMWARE_ARGS="" while read -r line; do [ -z "$line" ] && continue addr=$(echo "$line" | awk '{print $1}') file=$(echo "$line" | awk '{print $2}') filename=$(basename "$file") FIRMWARE_LIST="${FIRMWARE_LIST} ${addr} ${filename} \\\n" WIN_FIRMWARE_ARGS="${WIN_FIRMWARE_ARGS} ${addr} ${filename}" done < <(tail -n +2 "${FLASH_ARGS_FILE}") ``` The untrusted values are subsequently embedded into generated scripts: ```bash if ${ESPTOOL} --chip ${CHIP} -p ${PORT} -b ${BAUD} \ --before=default_reset --after=no_reset --no-stub write_flash \ ${FIRMWARE_LIST} > "${LOG_FILE}" 2>&1; then ``` ```batch set "FLASH_PARAMS=${FLASH_PARAMS}" "%ESPTOOL%" --chip %CHIP% -p %PORT% -b %BAUD% --before=default_reset --after=no_reset --no-stub write_flash %FLASH_PARAMS%${WIN_FIRMWARE_ARGS} ``` ### Technical Analysis The project-controlled `flash_args` file is treated as trusted script source. Its first line, address fields, and filename fields are inserted into Bash and Windows batch scripts without syntax validation or interpreter-specific escaping. A crafted field can contain shell or batch metacharacters, quotes, variable-expansion syntax, command separators, or control characters. Those characters become part of the generated executable scripts. The scripts are then included in a distributable firmware ZIP and are expected to be executed by production operators. This creates a supply-chain execution path: the dangerous code is not necessarily executed while packaging, but is embedded into apparently legitimate flashing utilities and executes later on another machine. ### Attack Path 1. An ...[truncated 1025 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat `flash_args` as untrusted structured data. 2. Permit addresses only when they match the required hexadecimal format, such as `^0x[0-9A-Fa-f]+$`. 3. Reject empty fields, extra fields, control characters, newlines, shell metacharacters, batch metacharacters, and variable-expansion characters. 4. Normalize every firmware path and restrict it to a safe relative filename or a verified path beneath the build directory. 5. In generated Bash code, use an argument array rather than interpolated command text: ```bash FLASH_FILES=( "0x1000" "bootloader.bin" "0x8000" "partition-table.bin" ) "$ESPTOOL" --chip "$CHIP" -p "$PORT" -b "$BAUD" \ --before=default_reset --after=no_reset --no-stub \ write_flash "${FLASH_FILES[@]}" ``` 6. For Windows, generate a fixed launcher that reads a validated manifest rather than inserting values into batch source code. 7. Fail packaging on malformed input instead of issuing a warning or producing partially valid scripts. 8. Add regression tests with spaces, quotes, separators, percent signs, exclamation marks, and newline-containing input. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pack_firmware.sh:311
Finding
Arbitrary File Inclusion in Firmware Package Through Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pack_firmware.sh`, lines 311–321 **Vulnerability Type**: Path traversal and unintended local file disclosure **Risk Level**: High ### Vulnerable Code ```bash echo "Copying firmware files..." while read -r line; do [ -z "$line" ] && continue file=$(echo "$line" | awk '{print $2}') src_file="${BUILD_DIR}/${file}" if [ -f "${src_file}" ]; then cp "${src_file}" "${OUTPUT_DIR}/" echo " Copied: $(basename "$file")" else echo " Warning: file not found: ${src_file}" fi done < <(tail -n +2 "${FLASH_ARGS_FILE}") ``` ### Technical Analysis The second field of each `flash_args` line is appended directly to `BUILD_DIR`. The script does not reject absolute paths, `..` path components, or symbolic links resolving outside the build directory. Consequently, a value such as a parent-relative path can resolve to any regular file readable by the invoking user. The file is copied into `firmware_package` and then included in the ZIP. Checking `-f` confirms only that the resolved target is a regular file; it does not ensure that the target is inside `BUILD_DIR`. ### Attack Path 1. An attacker supplies or modifies a build directory containing a crafted `flash_args`. 2. A firmware entry points outside the build directory using traversal components or an in-tree symbolic link. 3. The user runs `scripts/pack_firmware.sh`. 4. `src_file` resolves to a local file outside the intended build tree. 5. The regular-file check succeeds and the file is copied into `firmware_package`. 6. The packaging step includes that copied file in the distributable ZIP. 7. Anyone receiving the package can access the unintentionally included file. ### Impact Assessment The vulnerability can disclose any regular file readable by the packaging user, subject to the attacker's ability to predict or select its path. Potentially exposed material includes project configuration, source files, local cr ...[truncated 207 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Canonicalize and verify every source path before copying: ```bash build_root=$(realpath -- "$BUILD_DIR") candidate=$(realpath -- "${BUILD_DIR}/${file}") || { echo "ERROR: Invalid firmware path: $file" >&2 exit 1 } case "$candidate" in "$build_root"/*) ;; *) echo "ERROR: Firmware path escapes build directory: $file" >&2 exit 1 ;; esac ``` Also: 1. Reject absolute paths and any input containing `..` before resolution. 2. Decide whether symbolic links are allowed; if not, explicitly reject them. 3. Permit only expected firmware extensions and filename patterns. 4. Detect destination basename collisions. 5. Stop packaging on invalid or missing firmware files instead of continuing with warnings. 6. Build the archive from a newly created private staging directory containing only explicitly validated files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pack_firmware.sh:69
Finding
Predictable Temporary Log Files Enable Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pack_firmware.sh`, lines 69–76 and 112–122 **Vulnerability Type**: Insecure predictable temporary files **Risk Level**: Medium ### Vulnerable Code The generated `flash.sh` contains predictable paths under the shared `/tmp` directory: ```bash record_mac() { local PORT=$1 local DEVICE_NAME=$(basename "$PORT") local MAC_LOG="/tmp/read_mac_${DEVICE_NAME}.log" local MAC_CLEAN if ! ${ESPTOOL} --chip ${CHIP} -p ${PORT} -b ${BAUD} read_mac > "${MAC_LOG}" 2>&1; then echo "[${DEVICE_NAME}] Warning: failed to read MAC" >&2 return 1 fi } ``` ```bash flash_single() { local PORT=$1 local DEVICE_NAME=$(basename "$PORT") echo "[${DEVICE_NAME}] Flashing..." if [ ! -e "${PORT}" ]; then echo "[${DEVICE_NAME}] ERROR: Port not found: ${PORT}" return 1 fi local LOG_FILE="/tmp/flash_${DEVICE_NAME}.log" local RETRY=0 local MAX_RETRY=3 ``` Later, output redirection writes to the predictable log path: ```bash > "${LOG_FILE}" 2>&1 ``` ### Technical Analysis The generated script creates log filenames deterministically from the serial device basename. Shared temporary directories such as `/tmp` are writable by other local users. An attacker can predict names such as `flash_ttyUSB0.log` and pre-create those paths as symbolic links to files writable by the victim. When the flashing script redirects command output, the shell follows the symbolic link and opens the target for truncation and writing. The script neither creates the file atomically nor verifies file ownership and type. Parallel operations may also reuse predictable names, increasing collision and log-integrity risks. ### Attack Path 1. A local attacker determines the likely serial-port name used by the operator. 2. The attacker creates the corresponding predictable path in `/tmp` as a symbolic link to a file writable by the operator. 3. The operator runs the generated `f ...[truncated 573 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Create a private temporary directory atomically and remove it on exit: ```bash TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/esp-flash.XXXXXXXX") || exit 1 chmod 700 "$TMP_DIR" trap 'rm -rf -- "$TMP_DIR"' EXIT HUP INT TERM MAC_LOG=$(mktemp "$TMP_DIR/read-mac.XXXXXXXX.log") || exit 1 LOG_FILE=$(mktemp "$TMP_DIR/flash.XXXXXXXX.log") || exit 1 ``` Additionally: 1. Do not derive temporary filenames solely from user-controlled or predictable device names. 2. Use `mktemp` for each parallel worker. 3. Set a restrictive `umask`, such as `umask 077`. 4. Keep logs in the private directory and print their final secure location when troubleshooting is required. 5. Avoid running firmware flashing scripts with administrative privileges unless strictly necessary. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose is development/build/debug help, but the content also describes packaging redistributable firmware, generating standalone flashing scripts, recording MAC addresses, and bundling executables. That broader behavior materially increases supply-chain, privacy, and operational risk because users and reviewers may not expect production-line distribution and device-data collection from this skill.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
oting: powershell.exe not found

If you encounter `powershell.exe: command not found`, the Windows paths are not in your PATH environment variable.

**Quick Fix (current session only):**
```bash
export PATH="$PATH:/mnt/c/Windows/System32:/mnt/c/Windows/System32/WindowsPowerShell/v1.0"
~/skills/esp-idf-helper/scripts/usbipd_attach_serial.sh --list
```

**Permanent Fix (add to ~/.bashrc):**
```bash
echo 'export PATH="$PATH:/mnt/c/Windows/System32:/mnt/c/Windows/System32/WindowsPowerShell/v1.0:/mnt/c/Windows/SysWOW64"' >> ~/.bashrc
source ~/.bashrc
```

## Firmware Packaging

Pack ESP-IDF build output into a distributable firmware package with cross-platform flash scripts.

### Usage
```bash
scripts/pack_firmware.sh <build_directory>
```

### Example
```bash
# After building your project
idf.py build

# Create firmware package
scripts/pack_firmware.sh ./build

# Output: build/firmware_package/ and build/esp_firmware_YYYYMMDD_HHMMSS.zip
```

### Generated Package Contents
| File | Descript
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill clearly instructs shell-command usage and references local scripts, but it does not declare any explicit tool scope or allowed-tools boundary. That weakens sandboxing and reviewability, making it easier for an agent to execute shell actions beyond the user's expected ESP-IDF workflow.

Session Persistence

Medium
Category
Rogue Agent
Content
~/skills/esp-idf-helper/scripts/usbipd_attach_serial.sh --list
```

**Permanent Fix (add to ~/.bashrc):**
```bash
echo 'export PATH="$PATH:/mnt/c/Windows/System32:/mnt/c/Windows/System32/WindowsPowerShell/v1.0:/mnt/c/Windows/SysWOW64"' >> ~/.bashrc
source ~/.bashrc
Confidence
90% confidence
Finding
The skill instructs users to append to ~/.bashrc, creating persistent environment changes across future sessions. Persistence is security-relevant because it alters execution behavior long-term and could normalize PATH-based trust in Windows binaries from WSL, increasing exposure if those paths or binaries are abused later.

Vague Triggers

Medium
Confidence
93% confidence
Finding
This help text states that commands not recognized by idf.py will be attempted as build system targets. In a plain-text reference file, that creates an ambiguous and overly broad trigger scope because it does not clearly constrain what inputs should or should not activate this behavior.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The help text includes commands such as "erase-flash", "erase-otadata", "clean", and "fullclean" that delete flash contents or build artifacts, but it does not warn that these actions may be destructive or irreversible. For markdown and similar user-facing documentation, safety-affecting behavior should be disclosed clearly.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This script generates `flash.sh` and `flash_one.bat` that execute `write_flash`, which overwrites device firmware and is a destructive operation. Although the generated scripts print progress messages, the included `README.txt` only shows usage examples and does not warn users that running these commands will reflash attached devices and may affect device state or data.

Static analysis

No suspicious patterns detected.