T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/macos_launchctl.sh:228
- Finding
- Persistent Code Execution Through Unescaped plist and AppleScript Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/macos_launchctl.sh`, lines 228–242 and 292–302 **Vulnerability Type**: XML injection and AppleScript source injection **Risk Level**: High ### Vulnerable Code ```bash cat <<PLISTEOF <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>${label}</string> <key>ProgramArguments</key> <array> <string>/usr/bin/open</string> <string>-a</string> <string>${app_path}</string> ${extra_args:+ <string>${extra_args}</string>} ``` The stop-task generator similarly inserts the application name directly into AppleScript source: ```bash <dict> <key>Label</key> <string>${label}</string> <key>ProgramArguments</key> <array> <string>/usr/bin/osascript</string> <string>-e</string> <string>tell application "${app_name}" to quit</string> </array> ``` ### Technical Analysis The values `label`, `app_path`, `extra_args`, and `app_name` are inserted into XML without XML escaping. Characters such as `<`, `>`, `&`, and quotes can invalidate the generated plist or alter its structure. The stop-task generator introduces an additional source-injection boundary. `app_name` is derived from the basename of a user-supplied application path and is interpolated inside a quoted AppleScript expression. An application bundle whose filename contains AppleScript syntax and quote characters can terminate the intended application string and add attacker-controlled AppleScript statements. Because the resulting plist is saved in `~/Library/LaunchAgents` and loaded through `launchctl`, successful injection can be executed repeatedly according to the configured schedule. The ordinary user confirmation reduces accidental exploitation but does not sanitize the payload, and the `--yes` option bypasses confirmation ...[truncated 1539 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. XML-escape every dynamic value before inserting it into a plist, including `label`, `app_path`, `extra_args`, `app_name`, and log paths. 2. Prefer generating plists through a structured API such as Python's `plistlib` rather than constructing XML with a shell heredoc. 3. Reject control characters and malformed Unicode in all values written to a plist. 4. Do not generate AppleScript by interpolating an application name into source text. Prefer a validated bundle identifier and a mechanism that passes data separately from executable source. 5. If AppleScript remains necessary, apply an AppleScript-specific quoting routine and strictly constrain the accepted application name. 6. Run `plutil -lint` on every generated plist before moving it into `~/Library/LaunchAgents`. 7. Parse the completed plist and verify that `ProgramArguments` exactly matches an approved template before calling `launchctl`. 8. Write to a securely created temporary file first, validate it, set restrictive permissions, and then atomically move it to its final location. ]]>
