go-ansible/docs/security-attack-vector-mapping.md

19 KiB

Security Attack Vector Mapping

Scope: "external input" here means CLI arguments, controller-side files, environment variables, playbook/inventory/task YAML, inventory-derived SSH settings, remote SSH stdout/stderr/facts, and remote/network responses consumed by modules.

Notes:

  • All task module arguments originate in YAML via (*Task).UnmarshalYAML and are templated in (*Executor).templateArgs before handler dispatch.
  • Unless noted otherwise, module handlers only check required-field presence and coerce values with getStringArg/getBoolArg (modules.go:1095, modules.go:1105). They do not enforce allow-lists, path containment, protocol restrictions, or sink-specific sanitisation.
  • This document records current behaviour only. It does not propose fixes.

Controller, CLI, Parsing, and Templating Boundaries

Function File:Line Input source Flows into Current validation Potential attack vector
runAnsible cmd/ansible/ansible.go:29 Positional CLI playbook path filepath.Abs, coreio.Local.Exists, Executor.Run, then YAML parsing and task execution Existence check only; no allow-list or base-path restriction Arbitrary controller-side file selection; execution of attacker-controlled playbook content
runAnsible cmd/ansible/ansible.go:29 CLI --extra-vars, --limit, --tags, --skip-tags Executor.SetVar, host filtering, tag filtering, later templating and module dispatch Comma splitting only; no schema or escaping Variable poisoning into commands/paths/URLs; host-scope expansion; task-selection bypass
runAnsible cmd/ansible/ansible.go:29 CLI --inventory path Executor.SetInventory -> Parser.ParseInventory Abs path resolution, existence check, simple directory probing for fixed filenames Arbitrary inventory load; malicious SSH target/credential redirection
runAnsible cmd/ansible/ansible.go:29 Play names, task names, task results, remote stdout/stderr fmt.Printf in callbacks No output escaping or redaction beyond verbosity gates Terminal-control-sequence injection, log injection, secret disclosure in verbose mode
runAnsibleTest cmd/ansible/ansible.go:163 CLI host, user, password, key, port NewSSHClient, Connect, remote fact probe commands Defaults only Outbound SSH to arbitrary hosts, credential misuse, internal-host probing
runAnsibleTest cmd/ansible/ansible.go:163 Remote fact probe output Printed directly to terminal No output escaping Terminal-control-sequence injection or misleading operator output from hostile host
Parser.ParsePlaybook parser.go:31 Controller-side playbook file path and YAML content coreio.Local.Read, yaml.Unmarshal, processPlay File read and YAML parse only Arbitrary local file read by API caller; attacker-controlled task graph and vars
Parser.ParseInventory parser.go:68 Controller-side inventory path and YAML content coreio.Local.Read, yaml.Unmarshal File read and YAML parse only Inventory poisoning of hosts, ports, users, passwords, key paths, become settings
Parser.ParseTasks parser.go:83 Controller-side task include path and YAML content coreio.Local.Read, yaml.Unmarshal, extractModule File read and YAML parse only Included-task poisoning; arbitrary local file read if caller controls path
Parser.ParseRole parser.go:119 Role name and tasks_from from playbook YAML filepath.Join/Clean, search-path traversal, reads role tasks/defaults/vars Existence check only; no containment within a roles root Path traversal out of intended roles tree; arbitrary controller-side YAML/vars load
Task.UnmarshalYAML parser.go:239 Arbitrary task keys and module argument values from YAML task.Module, task.Args, include/import fields, loop fields Known-key skip list only; any dotted key is accepted as a module Module confusion, unexpected dispatch, arbitrary argument injection into later sinks
Executor.runIncludeTasks executor.go:420 include_tasks / import_tasks path, after templating templateString, Parser.ParseTasks No path canonicalisation or base-path enforcement Variable-driven arbitrary task-file include and controller-side file read
Executor.runIncludeRole executor.go:444 include_role / import_role name, tasks_from, vars runRole -> Parser.ParseRole No validation beyond downstream role lookup Variable-driven role traversal and role var poisoning
Executor.templateString / resolveExpr executor.go:746 / executor.go:757 Templated strings from playbook vars, task vars, host vars, facts, and registered results Module args, include paths, command fragments, URLs, file paths Regex substitution only; unresolved expressions are left in place Data-to-command/path/URL injection once templated values reach sink functions
Executor.handleLookup executor.go:870 Template lookup('env', ...) and lookup('file', ...) expressions os.Getenv, coreio.Local.Read, then back into templated args/content Regex parse only; no path or variable allow-list Controller secret exfiltration via environment variables or arbitrary local file reads
Executor.TemplateFile executor.go:973 Template src path and template file content Local file read, Go template parse/execute, later upload to remote host src must exist; no path restrictions; parse failure falls back to string replacement Arbitrary controller-side file read; template-driven secret interpolation into remote files
Executor.evaluateWhen / evalCondition executor.go:620 / executor.go:652 when: and assertion conditions from YAML, plus templated values Task/role gating logic Minimal parsing; unknown conditions default to true Conditional-bypass and unexpected task execution if conditions are malformed or attacker-shaped
Executor.getHosts / GetHosts executor.go:468 / parser.go:331 Play hosts and CLI --limit patterns Inventory host selection Exact/group lookup plus strings.Contains fallback for Limit Substring-based host-scope expansion to unintended inventory entries
Executor.getClient executor.go:499 Inventory host vars such as ansible_host, ansible_port, ansible_user, passwords, key file, become password SSHConfig, NewSSHClient, later network dial and sudo use Type assertions only Host redirection, credential injection, arbitrary key-path selection, password reuse for sudo
Executor.gatherFacts executor.go:565 Remote command output from target host e.facts, later templating and condition evaluation strings.TrimSpace and simple field parsing only Fact poisoning from hostile hosts into later command, file, and conditional paths

SSH and Remote Transport Boundaries

Function File:Line Input source Flows into Current validation Potential attack vector
SSHClient.Connect ssh.go:77 SSH config from CLI/inventory: host, port, user, password, key file Controller-side key reads, known_hosts, outbound TCP dial, SSH handshake Defaults plus known_hosts verification; no host or key-path restrictions Arbitrary key-file read on controller; outbound SSH to internal or attacker hosts; credential use against unintended targets
SSHClient.Run ssh.go:200 Remote command string built by executor/modules session.Run, optionally wrapped in sudo ... bash -c Only escapes single quotes when become is active; otherwise trusts caller Shell injection whenever a module builds a command from untrusted data; privileged execution under reused passwords
SSHClient.RunScript ssh.go:276 Raw script text from task/module logic Heredoc-fed bash on remote host No sanitisation; static heredoc delimiter Arbitrary remote script execution; delimiter collision if script embeds ANSIBLE_SCRIPT_EOF
SSHClient.Upload ssh.go:283 Uploaded bytes, remote path, mode, become state Remote mkdir, cat >, chmod, optional sudo Remote path quoted; mode rendered numerically Arbitrary remote file write/overwrite, including privileged paths when become is enabled
SSHClient.Download ssh.go:377 Remote file path Remote cat, returned bytes Remote path quoted only Arbitrary remote file read and exfiltration back to controller
SSHClient.FileExists ssh.go:396 Remote file path Remote test -e probe Remote path quoted only Remote path probing and presence disclosure
SSHClient.Stat ssh.go:410 Remote file path Inline remote shell script and parsed booleans Remote path quoted only Remote path probing and metadata disclosure

Module Dispatch and Command/Control Modules

Function File:Line Input source Flows into Current validation Potential attack vector
executeModule modules.go:17 task.Module and templated task.Args from YAML Module switch dispatch; unknown/empty modules with spaces fall back to moduleShell Module normalisation only Malformed or spoofed module names can degrade into shell execution
moduleShell modules.go:178 shell: / cmd: / _raw_params, optional chdir SSHClient.RunScript Non-empty command required; chdir quoted Arbitrary remote shell execution by design
moduleCommand modules.go:206 command: / cmd: / _raw_params, optional chdir SSHClient.Run Non-empty command required; chdir quoted Remote command injection because the whole string is passed to the remote shell
moduleRaw modules.go:234 Raw task command string SSHClient.Run Non-empty string required Arbitrary remote command execution by design
moduleScript modules.go:253 Controller-side script path from task args Local file read, then SSHClient.RunScript Script path must exist Arbitrary controller file read and remote execution of attacker-selected script
moduleApt modules.go:550 Package name, state, update_cache apt-get command strings Required-field check is weak; package name inserted raw Shell injection via package name; attacker-controlled package installation/removal
moduleAptKey modules.go:584 Key URL, optional keyring path, state Remote `curl gpg --dearmororcurl apt-key add -`
moduleAptRepository modules.go:615 Repository line, optional filename, update_cache Writes /etc/apt/sources.list.d/*.list, then apt-get update Repo required; filename only lightly normalised Repository poisoning and package-source redirection
modulePackage modules.go:652 Package args plus remote which output Delegates to moduleApt No extra validation Same package-command risks as moduleApt; trusts remote detection output
modulePip modules.go:665 Package name, state, executable Remote pip/custom executable command No allow-list; values inserted raw Shell injection; arbitrary executable selection on remote host
moduleService modules.go:690 Unit name, state, enabled systemctl command strings name required; values inserted raw Shell injection and unauthorised service lifecycle manipulation
moduleSystemd modules.go:732 daemon_reload plus service args systemctl daemon-reload, then moduleService No extra validation Same service-control and command-injection risks as moduleService
moduleUser modules.go:743 Username plus uid/group/groups/home/shell/system flags useradd, usermod, userdel command strings name required; most fields inserted raw Shell injection and privileged account creation/modification/deletion
moduleGroup modules.go:800 Group name, gid, system flag groupadd, groupdel commands name required; values inserted raw Shell injection and privileged group manipulation
moduleURI modules.go:835 URL, method, headers, body, expected status Remote curl, stdout/stderr parsing into task results URL required; headers/body are concatenated into command options SSRF/egress from remote host, shell injection through headers/body, response-driven output injection
moduleAssert modules.go:915 that conditions and fail_msg evalCondition, task result Only checks that that exists Guard bypass because condition evaluator is permissive; attacker-controlled failure messages
moduleSetFact modules.go:932 Arbitrary key/value pairs from playbook Global executor vars used by later templating and conditions Ignores only cacheable Variable poisoning that changes later commands, paths, URLs, and condition outcomes
modulePause modules.go:941 seconds Sleep loop gated by context Numeric parse only Denial of service through long pauses or deliberately stalled runs
moduleWaitFor modules.go:989 Target host, port, state, timeout Remote timeout ... bash -c 'until nc -z ...' Port/timeout integer checks only; host inserted raw Shell injection through host; remote internal-port scanning / SSRF-like reachability probing
moduleGit modules.go:1013 Repo URL/path, destination, version git clone, git fetch, git checkout --force on remote host repo and dest required; shell quoting used Supply-chain/code provenance risk from attacker repo/version; forced checkout can destroy remote working tree state
moduleHostname modules.go:1120 Hostname string hostnamectl / hostname, then sed -i on /etc/hosts name required; quoted in first command only /etc/hosts corruption or sed injection via hostname content
moduleSysctl modules.go:1139 Sysctl name, value, state sysctl -w, grep, sed, config append name required; values inserted raw Shell injection and persistent kernel-parameter tampering
moduleCron modules.go:1172 Cron name, job, user, schedule fields, state crontab pipelines with grep and echo No allow-list; several fields inserted raw Shell injection and persistence via cron jobs
moduleReboot modules.go:1288 Reboot delay and message shutdown -r now 'msg' Delay integer only; message is single-quoted without escaping Shell injection via reboot message; high-impact denial of service
moduleUFW modules.go:1306 Firewall rule, port, protocol, state ufw commands No allow-list; port/proto inserted raw Shell injection and unauthorised firewall reconfiguration
moduleDockerCompose modules.go:1408 project_src, state docker compose in selected directory project_src required; path quoted Execution of attacker-controlled compose project, image pull/deploy supply-chain risk

File, Transfer, and Data-Exposure Modules

Function File:Line Input source Flows into Current validation Potential attack vector
moduleCopy modules.go:281 Local src or inline content, remote dest, mode, owner, group Controller file read or inline content -> SSHClient.Upload -> chown/chgrp dest required; mode parse is best-effort; owner/group inserted raw Arbitrary controller file read, remote file overwrite, ownership-command injection
moduleTemplate modules.go:324 Template src, remote dest, mode, templated vars/facts TemplateFile -> SSHClient.Upload src and dest required Arbitrary controller template read, secret interpolation into remote files, remote overwrite
moduleFile modules.go:352 Remote path/dest, state, mode, src, owner, group, recurse mkdir, chmod, rm -rf, touch, ln -sf, chown, chgrp Path required; mode/owner/group inserted raw Destructive deletion, symlink manipulation, permission/ownership command injection
moduleLineinfile modules.go:420 Remote path, line text, regexp, state sed, grep, echo on remote host Path required; regexp/line not sanitised for sed Regex/sed injection, file corruption, uncontrolled line insertion/removal
moduleStat modules.go:463 Remote path SSHClient.Stat, then result data Path required Remote path probing and metadata disclosure
moduleSlurp modules.go:480 Remote path/src SSHClient.Download, then base64-encoded result Path required Arbitrary remote file exfiltration into task results/registered vars
moduleFetch modules.go:502 Remote src, controller dest SSHClient.Download, EnsureDir, coreio.Local.Write Both paths required; no destination root restriction Arbitrary controller-side file overwrite from remote-controlled content
moduleGetURL modules.go:526 URL, remote dest, mode Remote curl/wget, optional chmod URL and dest required; mode inserted raw Remote SSRF/egress, arbitrary file write, permission command injection
moduleUnarchive modules.go:1042 Local or remote archive src, remote dest, remote_src Optional controller file read/upload, then remote tar/unzip src and dest required; archive type guessed from suffix Arbitrary controller file read, extraction of attacker archives, path-traversal/symlink effects during extraction
moduleBlockinfile modules.go:1209 Remote path, block text, marker, state, create flag Remote sed and heredoc append script Path required; marker only partly escaped; block inserted into heredoc script sed injection, file corruption, persistent content insertion
moduleIncludeVars modules.go:1262 File path or raw parameter from task args Currently only returned in result message No validation Information disclosure/log injection via reflected path; function is a stub, not a parser
moduleDebug modules.go:895 msg or variable name/value Task result message, later terminal output No redaction or escaping Secret leakage and terminal/log injection when untrusted values are displayed
moduleFail modules.go:907 Failure message Task result message, later terminal output No escaping Log/terminal injection via reflected failure text
moduleMeta modules.go:1277 Meta action args from playbook No-op result Args ignored No direct sink today; low immediate impact unless future behaviour is added without review
moduleSetup modules.go:1283 Module invocation from playbook Currently returns a fixed result without using remote input No task-arg handling and no remote call in current implementation No direct sink today; future fact-gathering work would need a fresh boundary review
moduleAuthorizedKey modules.go:1357 User name, SSH public key, state Remote getent, mkdir, chown, grep, echo, sed on authorized_keys user and key required; key matching uses only first 40 chars; user inserted raw Command injection through user, persistent SSH access, key-prefix collisions or incorrect key removal