Feb 6, 2026·5 min read·64 visits
Gogs failed to verify ownership of 2FA recovery codes. An attacker with your password can log in to your account using *their* recovery codes. Patch to 0.13.4 immediately.
A critical logic error in the Gogs self-hosted Git service allows attackers to bypass Two-Factor Authentication (2FA) by using recovery codes belonging to a different account. If an attacker possesses a victim's primary credentials (username/password), they can successfully authenticate as the victim by supplying a recovery code generated from an account they control. This effectively neutralizes the 2FA protection layer, turning a credential leak into a full account takeover.
We often treat Two-Factor Authentication (2FA) like a magic shield. You lose your password in a breach? No problem, the hacker doesn't have your phone or your YubiKey. You're safe. But in software engineering, security features are only as good as the logic implementing them. In the case of Gogs, a popular self-hosted Go Git service, that logic was flawed in the most comedic way possible.
Imagine a bank vault that requires a key to open. You show up, claiming to be the owner of Vault 101. The guard asks for your key. You hand them the key to your own house. The guard looks at the key, checks a list, and says, "Yep, that's a valid key that exists in the world," and opens Vault 101 for you. That is exactly what is happening here.
CVE-2025-64175 isn't a complex memory corruption bug or a mathematical break in cryptography. It's a fundamental logic error where the application forgot to ask whose key was being used.
The root cause resides in the authentication flow for recovery codes. When a user loses access to their TOTP app (like Google Authenticator), they can use a one-time recovery code. The backend needs to verify two things: 1) Is this code valid and unused? 2) Does this code belong to the user trying to log in?
Gogs nailed the first part and completely ghosted the second. In the internal/db/two_factor.go file (or models in older structures), the function responsible for validating the code accepted a userID, but—and this is the kicker—it explicitly ignored it.
The database query was constructed to search the entire two_factor_recovery_code table for a matching string. If it found any row in the table that matched the code provided by the attacker, it returned success. It didn't matter if that code belonged to the admin, a random user, or the attacker themselves.
Let's look at the Go code that caused this mess. In Go, the underscore _ is used as a blank identifier to ignore values. Usually, this is fine for unused return values. Here, it was used on the input argument userID. That should have been the first red flag during code review.
Vulnerable Code:
// UseRecoveryCode validates recovery code of given user and marks it is used if valid.
func UseRecoveryCode(_ int64, code string) error {
recoveryCode := new(TwoFactorRecoveryCode)
// CRITICAL BUG: No filter for user_id!
// It only checks if the code string exists and hasn't been used yet.
has, err := x.Where("code = ?", code).And("is_used = ?", false).Get(recoveryCode)
if err != nil {
return fmt.Errorf("get unused code: %v", err)
} else if !has {
return ErrTwoFactorRecoveryCodeNotFound{Code: code}
}
// ... marks code as used and returns success
}See the _ int64? That's the function saying, "I'm taking a User ID, but I promise not to look at it." The ORM query x.Where("code = ?", code) scans the global table. Since recovery codes are often random strings, collisions are rare, but that's irrelevant here. The attacker doesn't need to guess your code; they just need to provide a valid code that the database knows about.
To exploit this, an attacker needs two things: the victim's password (easy enough via phishing, credential stuffing, or reuse) and an account on the same Gogs instance (also easy if registration is open, or if the attacker is a rogue insider/contractor).
Here is the kill chain:
deadbeef-1337.deadbeef-1337 (their own code).deadbeef-1337 a valid, unused code?" The DB replies "Yes". The system logs the attacker into the victim's session.The attacker has now bypassed the 2FA protection entirely. The system marks the attacker's recovery code as used, which is a small price to pay for full admin access.
This is a "High" severity issue for a reason. Gogs is used to host source code. If an attacker compromises a developer's account, they don't just steal data; they can modify it.
With this bypass, an attacker can:
Since this bypass requires the primary password, it effectively downgrades the security of 2FA-enabled accounts to that of 1FA accounts. If your password leaked, your 2FA saves you. With CVE-2025-64175, it doesn't.
The remediation is straightforward: actually use the userID that was passed to the function. The developers patched this in version 0.13.4 (and 0.14.0+dev) by adding a constraint to the query.
Patched Code:
func (db *twoFactors) UseRecoveryCode(ctx context.Context, userID int64, code string) error {
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var recoveryCode TwoFactorRecoveryCode
// FIX: Added "user_id = ?"
err := tx.Where("user_id = ? AND code = ? AND is_used = ?", userID, code, false).First(&recoveryCode).Error
// ...
})
}If you are running a self-hosted Gogs instance, you must upgrade immediately. If you cannot upgrade, disable open registration to prevent attackers from easily creating accounts to generate recovery codes, though this won't stop insider threats.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Gogs Gogs | < 0.13.4 | 0.13.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 (Improper Authentication) |
| CVSS v4.0 | 7.7 (High) |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | Low (Any valid account on instance) |
| Impact | Full Account Takeover |
An uncontrolled resource consumption vulnerability exists in the Docling document conversion library. Maliciously structured HTML, JATS, ODS, or BoxNote inputs containing table cells with excessively large 'rowspan' or 'colspan' attribute values trigger algorithmic complexity conditions. This allows unauthenticated remote attackers to initiate resource exhaustion states, crashing or hanging the target document processing pipeline while bypassing configured timeouts.
A Local File Inclusion (LFI) and Arbitrary File Disclosure vulnerability exists in Docling and Docling Slim versions >= 2.16.0 up to 2.131.0. When parsing serialized DoclingDocument structures using the JSON input format, the backend fails to restrict image URI schemes, allowing remote attackers to retrieve local files and verify path existence on the host system during embedded document export.
Docling, a tool for parsing and processing diverse document formats, is vulnerable to arbitrary file read, arbitrary file write, and potential remote code execution (RCE) in versions 2.94.0 through 2.131.0. The vulnerability occurs when applications configure Docling to use the Tectonic engine for rendering TikZ diagrams into images. Because the compilation did not restrict hazardous TeX primitives or sandbox the environment, an attacker can supply crafted documents containing malicious TikZ definitions to access or modify local files and execute arbitrary commands under the privileges of the processing application.
An SSRF guard bypass vulnerability in the Docling document conversion engine allows unauthenticated attackers to bypass internal IP access controls. The vulnerability exists due to a DNS rebinding Time-of-Check Time-of-Use (TOCTOU) condition, URL authority parsing inconsistencies, and unvalidated network requests triggered during headless browser page rendering.
A technical analysis of CVE-2026-105742 (GHSA-p3fw-7699-7926), a sensitive information disclosure vulnerability in the Docling document processing library. Vulnerable versions of Docling indiscriminately forward custom HTTP headers, such as authentication tokens, to arbitrary third-party origins and during cross-origin redirects while fetching remote image assets from untrusted HTML and EPUB documents.
CVE-2026-106121 is a Denial of Service (DoS) vulnerability in the RabbitMQ Java Client library (amqp-client) affecting versions prior to 5.37.0. The vulnerability resides in the legacy, custom JSON-RPC parsing class com.rabbitmq.tools.json.JSONReader. When parsing malformed or truncated payloads ending within a quoted string or single-line comment, the parser's scanner enters an infinite loop. This occurs because the loop lacks an exit condition for the end-of-input sentinel character returned by the iterator, leading to either CPU exhaustion or a JVM crash from an OutOfMemoryError.