Summary An attacker can exploit a chain of vulnerabilities, including a Denial of Service (DoS) flaw and in-memory data storage weakness, to effectively bypass the application's brute force login protection. This makes the application susceptible to brute force attacks, compromising the security of all user accounts.
Details The issue arises from two main vulnerabilities:
1. The application crashes due to a previously described DoS vulnerability caused by unsafe array modifications in a multi-threaded environment. 2. The application saves the data of failed login attempts in-memory, without persistent storage. When the application crashes and restarts, this data is lost, resetting the brute force protections.
go // LoginAttempts is a timestamped counter for failed login attempts
type LoginAttempts struct { // Time of the last failed login LastFailed time.Time json:"lastFailed" // Number of consecutive login failures FailCount int json:"failCount"
}
By chaining these vulnerabilities, an attacker can circumvent the limitations placed on the number of login attempts.
PoC 1. Run the provided PoC script. 2. Observe that the script makes 6 login attempts, one more than the set limit of 5 failed attempts. 3. This is made possible because the script triggers a server restart via the DoS vulnerability after 5 failed attempts, thus resetting the counter for failed login attempts.
Impact This is a critical security vulnerability that allows attackers to bypass the brute force login protection mechanism. Not only can they crash the service affecting all users, but they can also make unlimited login attempts, increasing the risk of account compromise.
Summary An attacker can effectively bypass the rate limit and brute force protections by exploiting the application's weak cache-based mechanism. This loophole in security can be combined with other vulnerabilities to attack the default admin account. This flaw undermines a previously patched CVE intended to protect against brute-force attacks.
Details The application's brute force protection relies on a cache mechanism that tracks login attempts for each user. This cache is limited to a defaultMaxCacheSize of 1000 entries. An attacker can overflow this cache by bombarding it with login attempts for different users, thereby pushing out the admin account's failed attempts and effectively resetting the rate limit for that account.
The brute force protection mechanism's code: go if failed && len(failures) >= getMaximumCacheSize() { log.Warnf("Session cache size exceeds %d entries, removing random entry",
getMaximumCacheSize()) idx := rand.Intn(len(failures) - 1) var rmUser string i := 0 for key := range failures {
if i == idx { rmUser = key
delete(failures, key)
break
}
i++ }
log.Infof("Deleted entry for user %s from cache", rmUser) }
PoC 1. Set up the application environment and identify the login page. 2. Execute 4 failed login attempts for the admin account. 3. Run a Burp Intruder attack to populate the cache with login attempts for usernames ranging from 1 to 10000. 4. After 1000 attempts, start monitoring to see if the admin entries in the cache have been cleared. 5. At this point, brute-force the admin account.
In just 15 minutes, the PoC was able to perform 230 brute force attempts on the admin account. This rate allows for approximately 1000 requests per hour, effectively rendering the older CVE rate limit patches useless.
Impact This is a severe vulnerability that enables attackers to perform brute force attacks at an accelerated rate, especially targeting the default admin account.
Summary By default, the Redis database server is not password-protected. Consequently, an attacker with access to the Redis server can gain read/write access to the data in Redis. The attacker can also modify the "mfst" (manifest) key to cause ArgoCD to execute any deployment, potentially leveraging ArgoCD's high privileges to take over the cluster. Updating the "cacheEntryHash" in the manifest JSON is necessary, but since it doesn't use a private key for signing its integrity, a simple script can generate a new FNV64a hash matching the new manifest values. The repo-server, unable to verify if its cache is compromised, will read the altered "mfst" key and initiate an update process for the injected deployment.
It's also possible to edit the "app|resources-tree" key, causing the ArgoCD server to load any Kubernetes resource into the live manifest section of the app preview. This could lead to an information leak.
The fact that the cache in Redis is neither signed nor validated, combined with Redis's default lack of password protection, presents a significant security concern given ArgoCD's high-level permissions within the cluster. A security update should ensure all Redis database values are signed or encrypted.
Details We began by deploying ArgoCD on an EKS cluster. Surprisingly, we discovered that an unprivileged pod in a different namespace on the same cluster could connect to the Redis server on port 6379. This was unexpected, as we had observed network policy rules restricting access to the Redis server to only the pods application-controller, repo-server, and argocd-server. We later realized that, despite having installed the latest version of the VPC CNI plugin on the EKS cluster, it requires manual enablement through configuration to enforce network policies. This raises concerns that many clients might unknowingly have open access to their Redis servers. We also know your recommendation on this page Argo CD - Secret Management, to enable the network policy plugin. Further investigation revealed that any pod within my cluster could connect to the Redis server by resolving its address using the Kubernetes DNS server. Exploring the contents of the Redis server, we found that we could edit the 'mfst' value of the latest revision. By updating the “cacheEntryHash”, we made the repo-server accept it as a legitimate cache, leading ArgoCD to apply this configuration. These tests were conducted using the default configuration, with regular ArgoCD and ArgoCD via helm deployment. This scenario presents a viable attack path, enabling any pod with access to the cluster to potentially exploit ArgoCD's high permissions and take over the cluster. We believe there is a critical need to enhance the security of the cache and its components. Given that many clients likely use ArgoCD in a plug-and-play manner, they could be exposed to significant risk. I am willing to offer assistance or answer any questions you might have.
PoC We tested this using the latest version of ArgoCD, configured with default settings. ArgoCD was installed either by applying a YAML file or through Helm. We wrote a few Go programs to decompress the Redis values and regenerate the "cacheEntryHash", but these programs were relatively straightforward.
To modify the cluster deployment, you can alter the "mfst" key of the latest revision. For instance, add the following line:
json {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"labels":{"app.kubernetes.io/instance":"myapp1"},"name":"everything-allowed"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"everything-allowed"}},"template":{"metadata":{"labels":{"app":"everything-allowed"}},"spec":{"containers":[{"args":["while true; do sleep 30; done;"],"command":["/bin/sh","-c","--"],"image":"ubuntu","name":"everything-allowed-pod","securityContext":{"privileged":true},"volumeMounts":[{"mountPath":"/host","name":"noderoot"}]}],"hostIPC":true,"hostNetwork":true,"hostPID":true,"volumes":[{"hostPath":{"path":"/"},"name":"noderoot"}]}}}
This addition creates a highly privileged pod.
To cause the web page to load a different Kubernetes resource in the "Live Manifest", edit the "app|resources-tree" manifest. Modify one of the component's kind, namespace, and name. Upon reloading the web page and clicking on the newly created asset, an error message appears: "Unable to load data: argocd-secret not found as part of application myapp." However, the resource's description is still transmitted to the browser, as seen in this URL format:
https://127.0.0.1:8081/api/v1/applications/myapp/resource?name=argocd-secret&appNamespace=argocd&namespace=argocd&resourceName=argocd-secret&version=v1&kind=Secret&group=
This situation results in information leakage.
Impact This vulnerability could lead to Privilege Escalation to the level of cluster controller, or to information leakage, affecting anyone who does not have strict access controls on their Redis instance.
Summary An attacker can exploit a critical flaw in the application to initiate a Denial of Service (DoS) attack, rendering the application inoperable and affecting all users. The issue arises from unsafe manipulation of an array in a multi-threaded environment.
Details The vulnerability is rooted in the application's code, where an array is being modified while it is being iterated over. This is a classic programming error but becomes critically unsafe when executed in a multi-threaded environment. When two threads interact with the same array simultaneously, the application crashes.
The core issue is located in expireOldFailedAttempts function: go func expireOldFailedAttempts(maxAge time.Duration, failures map[string]LoginAttempts) int {
expiredCount := 0 for key, attempt := range failures {
if time.Since(attempt.LastFailed) > maxAgetime.Second { expiredCount += 1 delete(failures, key) // Vulnerable code
} }
return expiredCount }
The function modifies the array while iterating it which means the code will cause an error and crash the application pod, inspecting the logs just before the crash we can confirm: go goroutine 2032 [running]: github.com/argoproj/argo-cd/v2/util/session.expireOldFailedAttempts(0x12c, 0xc000adecd8)
/go/src/github.com/argoproj/argo-cd/util/session/sessionmanager.go:304 +0x7c github.com/argoproj/argo-cd/v2/util/session.(SessionManager).updateFailureCount(0xc00035 af50, {0xc001b1f578, 0x11}, 0x1)
/go/src/github.com/argoproj/argo-cd/util/session/sessionmanager.go:320 +0x7f github.com/argoproj/argo-cd/v2/util/session.(SessionManager).VerifyUsernamePassword(0xc 00035af50, {0xc001b1f578, 0x11}, {0xc000455148, 0x8}) PoC To reproduce the vulnerability, you can use the following steps:
1. Launch the application. 2. Trigger the code path that results in the expireOldFailedAttempts() function being called in multiple threads. 3. In the attached PoC script we are restarting the server in a while loop, causing the application to be unresponsive at all.
Impact This is a Denial of Service (DoS) vulnerability. Any attacker can crash the application continuously, making it impossible for legitimate users to access the service. The issue is exacerbated because it does not require authentication, widening the pool of potential attackers.
Summary The CVE allows unauthorized access to the sensitive settings exposed by /api/v1/settings endpoint without authentication.
Details Unauthenticated Access:
Endpoint: /api/v1/settings Description: This endpoint is accessible without any form of authentication as expected. All sensitive settings are hidden except passwordPattern.
Patches A patch for this vulnerability has been released in the following Argo CD versions:
v2.11.3 v2.10.12 v2.9.17
Impact Unauthenticated Access:
Type: Unauthorized Information Disclosure. Affected Parties: All users and administrators of the Argo CD instance. Potential Risks: Exposure of sensitive configuration data, including but not limited to deployment settings, security configurations, and internal network information.
Impact All versions of ArgoCD starting from v2.4 have a bug where the ArgoCD repo-server component is vulnerable to a Denial-of-Service attack vector. Specifically, it's possible to crash the repo server component through an out of memory error by pointing it to a malicious Helm registry. The loadRepoIndex() function in the ArgoCD's helm package, does not limit the size nor time while fetching the data. It fetches it and creates a byte slice from the retrieved data in one go. If the registry is implemented to push data continuously, the repo server will keep allocating memory until it runs out of it.
Patches A patch for this vulnerability has been released in the following Argo CD versions:
v2.10.5 v2.9.10 v2.8.14
For more information If you have any questions or comments about this advisory:
Open an issue in the Argo CD issue tracker or discussions Join us on Slack in channel #argo-cd
Credits This vulnerability was found & reported by Jakub Ciolek
The Argo team would like to thank these contributors for their responsible disclosure and constructive communications during the resolve of this issue
Impact
"Local sync" is an Argo CD feature that allows developers to temporarily override an Application's manifests with locally-defined manifests. Use of the feature should generally be limited to highly-trusted users, since it allows the user to bypass any merge protections in git.
An improper validation bug allows users who have create privileges but not override privileges to sync local manifests on app creation. All other restrictions, including AppProject restrictions are still enforced. The only restriction which is not enforced is that the manifests come from some approved git/Helm/OCI source.
The bug was introduced in 1.2.0-rc1 when the local manifest sync feature was added.
Patches
The bug has been patched in the following versions:
2.10.3 2.9.8 2.8.12
Workarounds
To immediately mitigate the risk of branch protection bypass, remove applications, create RBAC access. The only way to eliminate the issue without removing RBAC access is to upgrade to a patched version.
Branch protection rules and review requirements are a great way to enforce security constraints in a GitOps environment, but they should be just one layer in a multi-layered approach. Make sure your AppProject and RBAC restrictions are as thorough as possible to prevent a review bypass vulnerability from permitting excessive damage.
References
Argo CD RBAC documentation
For more information
Open an issue in the Argo CD issue tracker or discussions Join us on Slack in channel #argo-cd
Impact
I can convince the UI to let me do things with an invalid Application. 1. Admin gives me p, michael, applications, , demo/, allow, where demo can just deploy to the demo namespace 2. Admin gives me AppProject dev which reconciles from ns dev-apps 3. Admin gives me p, michael, applications, sync, dev/, allow, i.e. no updating via the UI allowed, gitops-only 4. I create an Application called pwn in dev-apps with project dev and sync the app with sources from git 5. I change the Application’s project to demo via kubectl or gitops (whichever mechanism my admins have given me, because it should be safe) 6. I use the UI to edit the resource which should only be mutable via gitops
Patches A patch for this vulnerability has been released in the following Argo CD versions:
v2.10.7 v2.9.12 v2.8.16
For more information If you have any questions or comments about this advisory:
Open an issue in the Argo CD issue tracker or discussions Join us on Slack in channel #argo-cd
Credits This vulnerability was found & reported by @crenshaw-dev (Michael Crenshaw)
The Argo team would like to thank these contributors for their responsible disclosure and constructive communications during the resolve of this issue