Stealing the keys from the octopus: Exfiltrate Git Credentials in Argocd

Intro

Hello world, hello hackers! We're Future Sight - your soon-to-be favorite research group. Never heard of us? Don't worry, nobody has. This is literally our first drop.

Today, we will make some red teamers happy with a new technique we have discovered that allows an authenticated user in ArgoCD to steal the powerful GitHub credentials, further compromising Git accounts and more. For those folks who like Kubernetes security, you will enjoy even more!! Let's dive into the world of ArgoCD, Kubernetes, and Git and use the goddamn octopus against itself!

Motivation

It's the year 2025, and exfiltration of information is a buzz theme in cybersecurity, especially in those poor password managers. We want to contribute to that chaos a little bit and raise awareness among everyone in the cybersecurity field by demonstrating a way to exfiltrate git credentials in ArgoCD and Kubernetes.

motivation

Argocd is considered by most to be the best tool in GitOps continuous delivery for Kubernetes, and that's why we targeted it. In the CNCF landscape, it's in first place in the field of "Continuous Integration & Delivery".

Argocd might not be alone in this; similar tools with the same features might also allow these types of attacks. So buckle up kids; this is going to be an interesting ride!

WTF is Kubernetes?

Note: If you're already a black-belt at destroying Kubernetes clusters, accidentally nuking namespaces, and spending hours wondering why your pod refuses to receive traffic (spoiler: it was a missing Service all along)... You can safely skip this section. For the rest of you who have a life, enjoy this introduction!

Kubernetes is a container orchestration framework that helps to manage containers by taking care of scaling and failover. It facilitates the deployment and management of services using a declarative configuration and an automation approach. From the features that Kubernetes has, below are the ones that give Kubernetes its popularity:

Basic Concepts of Kubernetes

The largest unit in Kubernetes is a cluster, which comprises two primary components. On one side, you have the control plane that manages everything in the Kubernetes infrastructure, and on the other side, you have the node, which is where pods, the smallest unit in Kubernetes, are going to run. The Nodes are typically referred to as worker nodes, and the control plane is referred to as the master node. A Kubernetes cluster consists of one control plane and one or more worker nodes.

Below is an overview of the infrastructure:

kubernetes

The control plane is divided into 4 main components:

Each worker node is divided into 3 main components:

Kubernetes is composed of resources; below are the fundamental ones for this research:

Overview detailing how the resources interact with each other:

kubernetes_resources

There are several implementations of the Kubernetes framework, the most notable of which are k3s, kubeadm, minikube, and microk8s. In the majority of these frameworks, besides the components we have already presented, there is another service that comes pre-installed, which is CoreDNS.

CoreDNS

Note: Attention kids this part is very important!

CoreDNS is an authoritative DNS server running in the kube-system namespace, used by Kubernetes clusters to provide service discovery and name resolution. This enables resources, such as pods, to discover other pods by name rather than IP address. When we create a service or a pod, Kubernetes automatically creates an A record for that service and pod in the CoreDNS.

The services can be called by other pods:

Pods typically can be called by <pod-ipv4-address>.<service-name>.<my-namespace>.svc.<cluster-domain.example> however they can have a hostname and subdomain fields too, allowing them to be resolved as:

By default, when a pod resolves a DNS name, it first checks if the Kubernetes DNS has the record; if not, it forwards the request to an external DNS server. Each pod will have a /etc/resolv.conf, which will be set up by the kubelet. If you view the resolv.conf file, you will see that it lists the IP address of kube-dns as the nameserver for resolving DNS names.

coredns

If you want more insight about how DNS works in Kubernetes the following is a good article.

And WTF is Argocd?

Now you have and overview of what Kubernetes is, let's introduce our new friend, Argocd. Argocd is a GitOps declarative continuous integration tool for Kubernetes. And you might ask, what the hell is GitOps? GitOps is a set of practices that use Git as a source of truth to manage and deploy applications. Essentially, Argocd will take a Git repo with Kubernetes manifests as input and apply them to the cluster.

Besides the UI and CLI, ArgoCD has three main components:

Overview architecture:

argocd

Features

As a service that connects to both Git servers and Kubernetes clusters, Argocd presents us with multiple features to support that:

Argocd Known Attacks

The initial focus of this article was to demonstrate the possible attacks that an authenticated attacker can perform in ArgoCD regarding the permissions of the user they compromise. Most known attacks do the following:

However, they are kinda lame and well-known, so we don't want them here.

There are talks that demonstrate some of these attacks: The Hidden Dangers of Defaults and Attacking Argo CD with Argo CD.

Without access to those resources, an attacker can do little more than attempt to probe for accessible services and identify potential permissions to exploit. This was the reason we started digging deeper to find another way to leverage a session that an attacker had obtained.

known_attacks

Game Plan

In ArgoCD, to retrieve manifests from a Git repository, you can configure a connection that stores the credentials for that repository. When called by an application, we don't need to re-enter the same credentials.

However, ArgoCD, for security reasons, hides the credentials once the repository is created, not even allowing the person who created the repository to view the password.

hidden_creds

When we saw that, we immediately started to think of ways to leak the credentials, and if you are reading this, it's because we found a way!

Our first try was to find a way to set up a proxy in the repository. However, the proxy configuration is only available at the moment of creation of the repository, so we can't use that.

proxy

But what if we can create our own proxy and deceive argocd into thinking that it is connecting to a git server when in fact it's connecting to an attacker service...

The next thing that came to mind was Kubernetes DNS. As explained before, by default, Kubernetes creates DNS records for services and pods. And guess what is the first place that a pod is going to resolve a domain? If you guess the Kubernetes DNS, you are right!

What does that mean? Well, for example, if you can create a github.com DNS record pointing to an attacker-controlled service inside Kubernetes, the service receives the connection and does not go to the actual github.com(Well... we will proxy the requests, so in fact the connection goes to the actual github.com domain, but it will go through us first >:D).

To create a DNS record, an attacker has two paths:

Note: We will not cover the coredns path because kube-system is a highly critical namespace, and we assume that it would be protected and monitored.

dns

Okay, assuming the repository server is connected to our malicious service... What about our friend TLS? We need a valid certificate from a trusted CA to actually receive HTTPS connections and look at the content of the HTTP request. Using HTTPS typically stops these attacks, but not this time!

For our convenience, ArgoCD has a feature that allows us to add custom certificates for a domain. This functionality is useful for implementing TLS internally, but an attacker can leverage this to put the certificate used by the service in argocd. Now he can view the HTTP content, as Argocd trusts that certificate.

What is actually happening

Below is an overview of the attack:

attack

Requisites to perform this technique

It seems like all the stars have aligned... but the attacker still needs some prerequisites in the user account they compromise.

Must have policies:

Note: Optional means that is not required for the technique to be sucessful but is a quality of life for the attacker

Having those policies, an attacker can now take advantage of the little octopus and start exfiltrating git credentials! Let's see what we have built for him(the attacker obviously hehe).

Here is a script to verify if a compromise user has permissions.

Argexfil

Having put our plan together, we just need to create the service that ArgoCD will connect to, instead of the actual Git service. For that service, we combined the words 'Argocd' and 'exfiltration' to create Argexfil.

Types of Connections

In Argocd, there are four types of connection methods to connect to git repos:

We will only take into consideration http/https and github app connections.

The SSH connection we can never steal the private key even when we are between the connection, since it only sends the public key and the signature giving proof that argocd has the private key.

The Google Cloud connection is already deprecated, and it's no longer possible to use it for new customers starting on June 17, 2024, which is already in the past. We focus on the future.

For the HTTP and HTTPS connections, we can choose git or helm, but both are pretty easy to get the contents. For HTTP we don't need anything, just the DNS resolving to argexfil, and for HTTPS, we need an extra step, which is to put the certificate used in argexfil in the argocd.

For the GitHub app connections, it is used only in GitHub and GitHub Enterprise, and it works as follows:

Here we can do two types of attacks, and both simultaneously:

Git Credentials

Each type of connection has its own way of sending the GitHub credentials. For the http/https, both git and helm will send the credentials in the authorization header, encoding the username and password in base64, so it's pretty easy to extract it.

Example: Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3N3b3JkCg== -> testuser:testpassword

Note: In some cases, developers use classic Person Access Tokens instead of passwords; however, these typically begin with ghp_, which distinguishes them.

For the GitHub app:

tokens

Features

Besides capturing the requests made by argocd and logging the request information to the output, we decided to add some features. Below are some of the features:

To add to that, Argocd lets us delete an application without cascading, meaning that the application is deleted from the ui and from Argocd entirely, but every resource created in that application is not deleted. The service will still run in Kubernetes and still receive ArgoCD connections. This can hide the attackers trace from the argocd side and only be visible to kubernetes side.

We can access the argexfil code here.

POC

Conclusion

In this research, we have identified a technique that enables an authenticated attacker to leak Git credentials in ArgoCD using Kubernetes DNS. This technique has never been mentioned before, and therefore, it has a lot of work to be done and possibly a greater impact. It has its limitations, but if we exfiltrate even one GitHub credential, we can potentially take control of a GitHub account and its repositories, as well as the organizations the user is a part of.

Fun Fact: We have approached the ArgoCD team and discussed this technique. They told us that they had never seen this technique before; however, they did not consider it a vulnerability because they believed it was a problem with Kubernetes default configurations, not Argocd. Additionally, they informed us that the TLS would mitigate this issue, but as demonstrated, if an attacker has a certificate "create" policy, they can still perform the attack. (Once we reported this issue, they were quick to discuss it, so kudos to them.)

Mitigations that can be in place to prevent this technique:

Future work:

Well thats it kids, stay safe! Until next time!