T09 · Insecure Skill Coding Practices
- Location
SKILL.md:434- Finding
Arbitrary Code Execution Through Unsafe Terraform Filename Interpolation
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 434-463
Vulnerability Type: Command and Python code injection through an untrusted filename
Risk Level: HighVulnerable Code
bash # Find Terraform resource blocks missing tags rg -l 'resource "aws_' . -g '*.tf' | while read tf_file; do python3 -c " import re with open('$tf_file') as f: content = f.read() blocks = re.finditer(r'resource\s+\"(aws_\w+)\"\s+\"(\w+)\"\s*\{', content) for match in blocks: rtype, rname = match.group(1), match.group(2) # Find the block end by counting braces start = match.end() depth = 1 pos = start while depth > 0 and pos < len(content): if content[pos] == '{': depth += 1 elif content[pos] == '}': depth -= 1 pos += 1 block = content[start:pos] if 'tags' not in block: print(f'MISSING tags in $tf_file: {rtype}.{rname}') " doneTechnical Analysis
The filename returned by
rgis assigned to the shell variabletf_fileand then interpolated directly into the source code passed topython3 -c. The interpolation occurs inside a shell double-quoted argument and inside a Python single-quoted string:python with open('$tf_file') as f:A repository controls its filenames. A crafted Terraform filename containing quote characters, line breaks, shell substitutions, or Python syntax can therefore terminate or modify the intended Python string. This allows the filename to change the Python program executed during the scan.
The loop also uses
while read tf_filewithout null-delimited paths orIFS= read -r, making handling of backslashes, whitespace, and newline characters unsafe.Attack Path
- An attacker prepares a repository containing an
.tffile whose contents matchresource "aws_, ensuring thatrgreturns its filename. - The attacker gives the file a specially crafted name containing ...[truncated 1171 chars]
- An attacker prepares a repository containing an
- Remediation
View remediation
Remediation Suggestions
Never interpolate a repository-controlled path into shell or Python source code. Pass the filename as a positional argument and use null-delimited path handling:
bash rg -l -0 'resource "aws_' . -g '*.tf' | while IFS= read -r -d '' tf_file; do python3 - "$tf_file" <<'PY' import re import sys file_path = sys.argv[1] with open(file_path, encoding="utf-8") as f: content = f.read() blocks = re.finditer( r'resource\s+"(aws_\w+)"\s+"(\w+)"\s*\{', content, ) for match in blocks: rtype, rname = match.group(1), match.group(2) start = match.end() depth = 1 pos = start while depth > 0 and pos < len(content): if content[pos] == "{": depth += 1 elif content[pos] == "}": depth -= 1 pos += 1 block = content[start:pos] if "tags" not in block: print(f"MISSING tags in {file_path}: {rtype}.{rname}") PY doneAdditional hardening should include running repository scans in a sandbox without production credentials, testing the scanner against filenames containing quotes and newlines, and avoiding source-code generation from untrusted values.
