GitOops

Challenge Lab (Medium) - by Andrew Aiken

The following post by 0xb0b is licensed under CC BY 4.0arrow-up-right


Scenario

Objective / Scope

Hack Smarter been retained to conduct a targeted penetration test against a critical asset within the client's internal network. The scope is limited to a specific high-value endpoint identified as a primary development server (and any cloud assets identified on the host).

Due to the sensitive nature of the intellectual property and proprietary source code likely resident on this machine, the client requires a comprehensive assessment of its security posture to prevent potential supply chain compromise.

Initial Access

You have been provided with VPN access to their internal environment, but no other information.

Note From Tyler

This lab was originally created for vmGoat which is an open-source project created by Andrew. If you'd like to learn more about his project, you can check out the Github repo - vmGoatarrow-up-right.

Summary

chevron-rightSummaryhashtag

In this GitOops we begin with only VPN access to an internal development server and no prior credentials. Enumeration reveals a Linux-based DevOps environment hosting a Gitea instance at gitoops.local, an additional Git-over-SSH service on port 2222, and an Atlantis server at atlantis.gitoops.local used to automate Terraform workflows. Inspecting the publicly accessible gitCorp/public repository exposes Terraform configuration files that define AWS infrastructure, including an S3 backend storing the Terraform state file. Accessing the referenced S3 bucket directly discloses terraform.tfstate, which contains sensitive data including the private SSH key for the EC2 instance backing the Git server. After reconstructing the key format, we successfully authenticate via SSH as alexis.

On the host, process enumeration reveals Atlantis running with a Gitea token and a repo allowlist for gitCorp/private. Recognizing that Atlantis automatically executes terraform init, plan, and apply on pull request comments, we pivot to supply chain compromise. Using the recovered SSH key, we clone the private repository over Git SSH, create a malicious Terraform configuration leveraging a null_resource with a local-exec provisioner to execute a reverse shell, and push it to a new branch. We then create a pull request via the Gitea API and trigger execution by commenting atlantis apply. Atlantis processes the workflow and executes our injected Terraform code on the server, resulting in a root shell. With full system compromise achieved through infrastructure-as-code abuse and cloud misconfiguration, we retrieve the final flag from /root/flag.txt.

Recon

We use rustscan -b 500 -a 10.0.18.73 -- -sC -sV -Pn to enumerate all TCP ports on the target machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

The target machine appears to be a Linux-based Git server environment. It exposes multiple SSH services on port 22 and 2222, an HTTP service on port 80 redirecting to HTTPS, and an HTTPS service on port 443 hosting a Gitea instance.

Port 22 is running OpenSSH 9.6p1 (Ubuntu).

Port 80 runs nginx 1.24.0 Ubuntu and redirects traffic to HTTPS, enforcing encrypted communication. Port 443 serves Gitea, confirmed by the HTTP title and TLS certificate. The SSL certificate is self-signed for the internal domain gitoops.local, with SAN entries for gitoops.local and *.gitoops.local, indicating this system is intended to operate within a private/internal DevOps environment.

Port 2222 is also running SSH, but the banner SSH-2.0-Go suggests a Go-based SSH service, which may be tied to the Git platform itself - commonly used for Git-over-SSH operations.

From our Nmap result, we extract the SAN entries and create an entry in our /etc/hosts file.

Since other subdomains or vhosts may exist, we perform a vhost scan using FFuF and find atlantis.gitoops.local.

gitoops.local

We'll start by enumerating the Gitea instance at gitoops.local. At explore, we see the publicly accessible repositories and find the public repository of gitCorp.

In this directory, we find the following 5 terraform files:

Terraform is an Infrastructure-as-Code (IaC) tool created by HashiCorp that allows to define cloud infrastructure using code instead of manually setting up the infrastructure in a dashboard.

For example, Terraform can be used to create virtual machines such as EC2 instances in AWS. It can also provision storage resources like S3 buckets. Networking components such as Virtual Private Clouds (VPCs), subnets, route tables, and security groups can all be defined and deployed through Terraform. Additionally, it can configure DNS records using services like Route53, create Identity and Access Management (IAM) roles and policies, generate SSH keys, and handle many other cloud resources.

We may find valuable loot in these files for our initial access, or even a way to customize them later and use them to our advantage to compromise the system.

We pull the repository and inspect the files.

The following configuration data.tf does not create new infrastructure, but instead looks up an existing VPC and subnet tagged “vmGoat,” retrieves the most recent official Ubuntu 22.04 AMI from Amazon, and fetches a public Route53 DNS zone defined by a variable so those existing resources can be used when provisioning other components.

The ec2.tf configuration creates a security group that allows anyone to access the server over HTTP 80, HTTPS 443, and SSH 22. It provisions a public Ubuntu EC2 instance named gitoops in the specified subnet with a fixed private IP address, a public IP address, and the previously defined SSH key attached for remote access.

The main configuraiton creates a public DNS A record that points the domain to the EC2 instance' public IP address, generates a new 4096-bit RSA private key, and uploads its public portion to AWS as an EC2 key pair so that the instance can be accessed via SSH using the generated private key.

The settings.tf now finally holds something interesting. The configuration specifies the required Terraform and provider versions, stores the infrastructure state file in an S3 bucket named gitoops-4ulyqvxd8nn6hlsc with the key gitcorp/terraform.tfstate in the us-east-1 region, and configures the AWS provider to operate in that region while automatically tagging all created resources as part of the gitoops project and managed by Terraform.

An S3 bucket is a cloud-based object storage container in Amazon Web Services. It is used to store files, called objects which can include anything from backups and documents to application assets and configuration files.

Access to S3 buckets is controlled through permissions, and if those permissions are misconfigured, the contents can become publicly accessible.

In this case, the S3 bucket is being used as a Terraform backend, meaning it stores the terraform.tfstate file. The Terraform state file is extremely sensitive because it contains a detailed record of all provisioned infrastructure and often includes secrets in plaintext.

A Terraform state file can contain:

  • SSH private keys

  • Cloud access keys and tokens

  • IAM role references

  • Internal IP addresses

  • Resource IDs

  • Database connection strings

  • Passwords or API tokens (if defined in configuration)

  • Full infrastructure topology

This could mean the following and open the way for us to gain an initial foothold:

Cloud Misconfiguration → Sensitive Data Exposure → Infrastructure Compromise

The following configuration just defines a string input named dns_zone_domain with the default value gitoops.local. that specifies the Route53 DNS domain to be used when creating or referencing DNS records.

We have identified a possible attack vector via the terafiles. In the activity feed, we can still see which users are involved in the project. These user names could be useful later on. We write them down.

atlantis.gitoops.local

Before we proceed with our potential identified attack vector, let's take a look at the service at atlantis.gitoops.local.

According to ChatGPT, Atlantis is an open-source tool that automates Terraform workflows in pull requests:

Atlantis

Atlantis is an open-source tool that automates Terraform workflows in pull requests. It listens for GitHub/GitLab/Bitbucket webhooks and runs terraform plan and terraform apply when users comment on a PR, helping teams safely review and manage infrastructure changes as code. It’s commonly used with Terraform to manage cloud infrastructure such as AWS in a collaborative, audited way.

References

That's really interesting. If we are able to trigger workflows via pull requests and could create a workflow ourselves, we could also use this to compromise the underlying infrastructure by injecting malicious Terraform code that Atlantis would automatically execute.

Shell as alexis

We don't have access to git repository yet, so we'll continue with the findings from the repo and take a closer look at the S3 bucket.

Recalling the settings.tf file:

We try to access the S3 Bucket an request the contents of the key gitcorp/terraform.tfstate.

We actuially retrieved the Terraform state file terraform.tfstate, including sensitive material with the SSH private key for the EC2 instance being publicly exposed.

Looking at the instance "key_name": "gitoops" and "public_ip": "1.1.1.1" so it is actually the EC2 instance we have infront of us.

We copy openssh key

We need to fix the ssh key with replacing the newlines with actual ones.

Edit the correct permissions to the key...

... and use the key to log in. We try with the users ubuntu and alexis (from our Gitea enumeration) and are able to log in as alexis via SSH using the private key.

Shell as root

As alexis, we find the config repo.yaml for the repo inside the /opt/atlantis folder. Nothing exciting at first glance.

We scan again with linpeas to make sure we haven't overlooked anything.

We see some problematic capabilities set for snap-confie, but we can't exploit them. A false-positive.

If we look at the processes running in the background,...

... we find the Atlantis server running.

Recalling our idea from initial enumeration, that we could use the mechanics of atlantis to inject malicious execution via triggering workflows via pull requests.

If we look closeley we find the gitea-token used to access a private repository of gitCorp.

So we know from research that Atlantis executes:

  • terraform init

  • terraform plan

  • terraform apply

That means if wecan modify Terraform code in the allowed repo:

we control what Terraform executes.

Next things to do:

  • Confirm we can push to the used repository.

  • Add malicious Terraform.

  • Trigger atlantis apply.

  • Get shell.

  • Check privileges.

  • Escalate if needed.

circle-info

Unfortunately, the steps using tokens are not effective, but are shown here for the sake of completeness.

chevron-rightHinthashtag

We could also use the SSH key we already gathered.

We can clone the repo as follows:

We read the README.md and it confirms that this repo is used for Terraform code.

We set local username and email.

Next, we will prepare a terraform configuration that executes a reverse shell when loaded.

Next, we create a new branch...

... and the file:

But our push fails. We do not have write permission using the token.

The atlantis token user does NOT have write permission to the repository

We could have checked that beforehand using the Gitea api.

Instead of the token, we'll try using the OPENSSH key from the S3 bucket. We start over, delete the folder and repeat our steps. We clone the repo using the SSH key and are successful.

Prepare the pwn.tf reverse shell configuration.

Create a branch.

... and the file:

Next, we push the changes to tha branch and are successful:

We can also confirm that the code is pushed using the api:

Next, we need to perform a pull request, but cannot use the GUI of Gitea, since we are missing the credentials of alexis. But we can also use the Gitea API for this.

The job pops up at atlantis.gitoops.local:

Apply the pull request like depicted in our enumeration, we need to comment atlantis apply in the pull request.

We prepare a listener using Penelope.

Next, we make the comment.

We then should receive a connection back. We are root and find the final flag at /root/flag.txt.

Recommendation

Don't miss out on Andrew Aiken's official write-up:

Last updated

Was this helpful?