Command injection via file upload filename when processed by system shell
ID: security/command-injection-via-file-upload-filename
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| PHP 8.3.0 | active | — | — | — |
| Node.js 20.11.0 | active | — | — | — |
| Python 3.12.0 | active | — | — | — |
| Apache Commons IO 2.15.0 | active | — | — | — |
Root Cause
The application passes the uploaded file's filename directly to a system shell command (e.g., via exec, system, or subprocess) without sanitization, allowing an attacker to inject arbitrary commands using shell metacharacters like ;, |, or `.
generic中文
应用程序将上传文件的文件名直接传递给系统 shell 命令(例如通过 exec、system 或 subprocess),而未进行清理,允许攻击者使用 shell 元字符(如 ;、| 或 `)注入任意命令。
Official Documentation
https://owasp.org/www-community/attacks/Command_InjectionWorkarounds
-
95% success Avoid using system shell commands with user input altogether. Use language-native APIs for file operations. For example, in Node.js, use `fs.rename(uploadedFile.path, '/target/' + sanitizedFilename)` instead of `exec('mv ' + filename + ' /target/')`. Sanitize the filename by removing all non-alphanumeric characters except dots and hyphens: `filename.replace(/[^a-zA-Z0-9._-]/g, '')`.
Avoid using system shell commands with user input altogether. Use language-native APIs for file operations. For example, in Node.js, use `fs.rename(uploadedFile.path, '/target/' + sanitizedFilename)` instead of `exec('mv ' + filename + ' /target/')`. Sanitize the filename by removing all non-alphanumeric characters except dots and hyphens: `filename.replace(/[^a-zA-Z0-9._-]/g, '')`. -
90% success If a shell command is unavoidable, use a whitelist of allowed characters for the filename and reject any input that does not match. For example, in Python: `import re; if not re.match(r'^[a-zA-Z0-9._-]+$', filename): raise ValueError('Invalid filename')`. Then use `subprocess.run(['mv', filename, '/target/'], shell=False)` to avoid shell interpretation.
If a shell command is unavoidable, use a whitelist of allowed characters for the filename and reject any input that does not match. For example, in Python: `import re; if not re.match(r'^[a-zA-Z0-9._-]+$', filename): raise ValueError('Invalid filename')`. Then use `subprocess.run(['mv', filename, '/target/'], shell=False)` to avoid shell interpretation. -
88% success Use a dedicated library for safe command execution, such as Apache Commons Exec in Java, which provides `CommandLine` and `Executor` classes that avoid shell injection. Example: `CommandLine cmdLine = new CommandLine("mv"); cmdLine.addArgument(filename); cmdLine.addArgument("/target/"); DefaultExecutor executor = new DefaultExecutor(); executor.execute(cmdLine);`.
Use a dedicated library for safe command execution, such as Apache Commons Exec in Java, which provides `CommandLine` and `Executor` classes that avoid shell injection. Example: `CommandLine cmdLine = new CommandLine("mv"); cmdLine.addArgument(filename); cmdLine.addArgument("/target/"); DefaultExecutor executor = new DefaultExecutor(); executor.execute(cmdLine);`.
中文步骤
完全避免使用用户输入的系统 shell 命令。使用语言原生 API 进行文件操作。例如,在 Node.js 中,使用 `fs.rename(uploadedFile.path, '/target/' + sanitizedFilename)` 而不是 `exec('mv ' + filename + ' /target/')`。通过移除除点和连字符之外的所有非字母数字字符来清理文件名:`filename.replace(/[^a-zA-Z0-9._-]/g, '')`。如果无法避免 shell 命令,则对文件名使用白名单允许的字符,并拒绝任何不匹配的输入。例如,在 Python 中:`import re; if not re.match(r'^[a-zA-Z0-9._-]+$', filename): raise ValueError('Invalid filename')`。然后使用 `subprocess.run(['mv', filename, '/target/'], shell=False)` 避免 shell 解释。使用专用库进行安全命令执行,例如 Java 中的 Apache Commons Exec,它提供避免 shell 注入的 `CommandLine` 和 `Executor` 类。示例:`CommandLine cmdLine = new CommandLine("mv"); cmdLine.addArgument(filename); cmdLine.addArgument("/target/"); DefaultExecutor executor = new DefaultExecutor(); executor.execute(cmdLine);`。
Dead Ends
Common approaches that don't work:
-
Use escapeshellarg() in PHP to escape the filename
30% fail
escapeshellarg() escapes the argument for the shell, but if the command is constructed incorrectly (e.g., using double quotes), it may still be vulnerable. Also, it does not prevent injection if the filename is used in a context like `mv $filename /target/` without quotes.
-
Remove only semicolons and pipes from the filename
50% fail
Attackers can use other metacharacters like backticks, $(), or newlines to execute commands. Partial sanitization is insufficient.
-
Use a blacklist to block common command injection patterns
60% fail
Blacklists are easily bypassed with encoding, obfuscation, or using less common shell features. A whitelist is more secure.