T09 · Insecure Skill Coding Practices
- Location
dnscheck.py:8- Finding
Unvalidated dig Arguments Permit Native Option Injection in the Root Script
- Content
View full analysis
Vulnerability Details
File Location:
dnscheck.py:8-11(input originates atdnscheck.py:38-39)
Vulnerability Type: Native command argument injection
Risk Level: MediumVulnerable Code
python def dig(domain, rtype="A"): """Use system dig if available, fall back to socket.""" try: out = subprocess.run( ["dig", "+short", "+timeout=5", rtype, domain], capture_output=True, text=True, timeout=10 )The relevant command-line input is accepted without validation:
python p.add_argument("domains", nargs="+", help="Domains to look up") p.add_argument("-t", "--type", nargs="+", default=None, help="Record types (A, AAAA, MX, NS, TXT, CNAME, SOA)")Technical Analysis
Both
rtypeanddomainare controlled by command-line arguments and are passed directly todig. The use of an argument list withshell=Falseprevents shell metacharacter injection, but it does not prevent native argument injection into the invoked program.digrecognizes arguments beginning with characters such as-,+, and@as control arguments. Consequently, a purported record type such as@attacker-controlled-resolver.examplemay be interpreted as a resolver selection directive rather than as a DNS record type. Other native options can modify query behavior or activate batch-file processing.The advertised record types are documented but not enforced. No allowlist restricts
rtypetoA,AAAA,MX,NS,TXT,CNAME, andSOA, and domain values are not validated before reaching the process boundary.Attack Path
- An attacker or untrusted caller supplies a crafted value through
--type, such as adigcontrol argument beginning with@,+, or-. argparsestores the value without checking it against the documented record-type set.dig()inserts the value directly into the command argument list before the domain.
...[truncated 1038 chars]
- An attacker or untrusted caller supplies a crafted value through
- Remediation
View remediation
Remediation Suggestions
-
Enforce a case-insensitive allowlist before invoking
dig:python ALLOWED_TYPES = {"A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA"} def validate_rtype(value): value = value.upper() if value not in ALLOWED_TYPES: raise argparse.ArgumentTypeError(f"Unsupported record type: {value}") return valueApply the validator through
argparse:python p.add_argument( "-t", "--type", nargs="+", type=validate_rtype, default=None, help="Record types (A, AAAA, MX, NS, TXT, CNAME, SOA)" ) -
Validate each domain as a DNS hostname or IP literal. Reject control prefixes such as
-,+, and@, empty labels, invalid characters, and names exceeding DNS length limits. -
Construct the
diginvocation so fixed query components are unambiguous. Do not rely solely on shell avoidance as protection against application-level option parsing. -
Add tests confirming that values such as
@resolver,-f,+trace, and unsupported record types are rejected beforesubprocess.runis called. -
Keep the duplicate root and
scripts/implementations synchronized, or consolidate them into one implementation to prevent security fixes from being applied to only one copy.
-
