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 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.
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