T09 · Insecure Skill Coding Practices
- Location
scripts/web_screenshot.py:13- Finding
OS Command Injection Through Web Screenshot Arguments
- Content
View full analysis
Vulnerability Details
File Location:
scripts/web_screenshot.py, lines 13–21, 36–37, and 50–51
Vulnerability Type: OS command injection through shell command construction
Risk Level: HighVulnerable Code:
python def run_command(cmd, timeout=120): """Run a command and return its result.""" try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=timeout )python cmd = f'agent-browser open "{url}" --timeout {timeout}' success, stdout, stderr = run_command(cmd, timeout=timeout//1000 + 10)python full_arg = "--full" if full_page else "" cmd = f'agent-browser screenshot "{output}" {full_arg}' success, stdout, stderr = run_command(cmd, timeout=60)Technical Analysis
The
--urland--outputcommand-line arguments are inserted directly into shell command strings. The resulting strings are executed withsubprocess.run(..., shell=True).Placing an argument inside double quotes does not make shell execution safe. An attacker can supply a value containing a closing double quote followed by shell metacharacters and an additional command. The shell parses this syntax before invoking
agent-browser, allowing the additional command to execute independently.Both the URL passed to
agent-browser openand the output path passed toagent-browser screenshotare affected. Numeric arguments are parsed byargparseas integers and are not the primary injection vector.Attack Path
- An attacker gains control over the
--urlor--outputvalue supplied to the script. - The attacker includes a closing quote and shell control syntax in that value.
screenshot_webpage()interpolates the malicious value into thecmdstring.run_command()invokes the generated string withshell=True.- The operating-system shell interprets th ...[truncated 644 chars]
- An attacker gains control over the
- Remediation
View remediation
Remediation Suggestions
Remove shell interpretation and pass each command argument as a separate list element:
python def run_command(cmd, timeout=120): try: result = subprocess.run( cmd, shell=False, capture_output=True, text=True, timeout=timeout, check=False, ) return result.returncode == 0, result.stdout, result.stderr except subprocess.TimeoutExpired: return False, "", "Command timed out"python open_cmd = [ "agent-browser", "open", url, "--timeout", str(timeout), ]python screenshot_cmd = ["agent-browser", "screenshot", output] if full_page: screenshot_cmd.append("--full")Additionally:
- Restrict URLs to explicitly supported schemes, preferably
httpandhttps. - If requests originate from untrusted users, apply destination controls to prevent access to loopback, link-local, private, or otherwise sensitive network resources.
- Resolve output paths and restrict them to an approved output directory.
- Reject negative or unreasonably large timeout and wait values.
- Close the browser in a
finallyblock so failure paths do not leave browser processes running.
- Restrict URLs to explicitly supported schemes, preferably
