Feb 19, 2026·6 min read·27 visits
Appsmith <= 1.92 allowed attackers to hijack user accounts by manipulating the HTTP 'Origin' header during password reset requests. This caused the server to generate and email reset links pointing to an attacker-controlled domain. Clicking the link leaked the reset token to the attacker, granting full account access. Fixed in version 1.93.
In the world of web security, there is a golden rule that is broken so often it should be printed on the back of every developer's eyelid: **Never trust client input.** Yet, here we are again with CVE-2026-22794, a critical Account Takeover (ATO) vulnerability in Appsmith, the darling of the open-source low-code world. The vulnerability is a classic case of 'Origin Validation Error' (CWE-346). By blindly trusting the HTTP `Origin` header to construct sensitive password reset links, the Appsmith server allowed attackers to redirect the password reset flow to their own domains. This isn't just a minor glitch; it's a mechanism that turns the application's own email service into a weapon, handing over administrative keys to anyone with `curl` and a lack of morals. The fix involves finally forcing the server to know its own name via a hardcoded configuration rather than asking the stranger knocking at the door.
Appsmith is a fantastic tool. It allows developers to whip up internal dashboards, admin panels, and CRUD apps in minutes. It connects to everything—PostgreSQL, MongoDB, AWS S3, you name it. And that is exactly why this vulnerability is terrifying.
Usually, when we talk about Account Takeover (ATO), we're worried about someone reading your emails or posting embarrassing tweets. But with Appsmith, an ATO doesn't just give you access to a user profile; it gives you the keys to the kingdom's backend. You aren't just compromising a user; you are compromising the database credentials and API keys stored within that user's projects.
Prior to version 1.93, Appsmith suffered from a severe identity crisis. It didn't inherently know where it lived. Instead of having a strict, server-side concept of "Home," it relied on the incoming HTTP request to tell it where to send users. It's the digital equivalent of a bank asking a robber, "Which vault would you like me to open for you?" and then politely holding the door.
The root cause here is a failure to sanitize the Origin header (or sometimes the Host header, depending on the specific proxy setup). In a modern web stack, the Origin header is sent by the browser to indicate where a request is coming from. It is client-controlled data. It is untrusted. It is dirty.
When a user requests a password reset, the server needs to generate a link like https://appsmith.company.com/reset?token=XYZ. To build this string, the code needs three things: the protocol (https), the domain (appsmith.company.com), and the token (XYZ).
The developers of Appsmith made a fatal assumption: they assumed that the Origin header in the request matched the actual domain of the application. They wrote code that essentially said: "Take whatever the user claims is the base URL, append the token, and email it to the victim."
This is a logic flaw known as CWE-346: Origin Validation Error. By failing to validate this input against a whitelist or a static configuration variable, the application became a proxy for phishing attacks, signed and delivered by its own SMTP server.
Let's look at the "smoking gun" in UserServiceCEImpl.java. This is where the magic (and the tragedy) happened.
The Vulnerable Code (Conceptual):
Before the patch, the code logic looked something like this:
// Inside forgotPasswordTokenGenerate
public Mono<Boolean> forgotPasswordTokenGenerate(ResetUserPasswordDTO resetUserP) {
// DANGER: relying on the DTO which was populated from the HTTP headers
String baseUrl = resetUserP.getBaseUrl();
String token = UUID.randomUUID().toString();
// Constructing the link using the attacker-supplied base URL
String resetLink = String.format("%s/user/resetPassword?token=%s", baseUrl, token);
// The server happily sends this link to the victim
return emailService.sendForgotPasswordEmail(email, resetLink);
}The Fix (Commit 6f9ee622...):
The remediation was to stop trusting the request and start trusting the configuration. They introduced a check against APPSMITH_BASE_URL.
@Value("${APPSMITH_BASE_URL:}")
private String appsmithBaseUrl;
private Mono<Void> validateBaseUrl(String providedBaseUrl) {
// If the admin hasn't set the config, we are still vulnerable (backward compat)
if (!StringUtils.hasText(appsmithBaseUrl)) {
return Mono.empty();
}
// The validation logic
if (!appsmithBaseUrl.equals(providedBaseUrl)) {
return Mono.error(new AppsmithException(
AppsmithError.GENERIC_BAD_REQUEST,
"Origin header does not match APPSMITH_BASE_URL configuration."));
}
return Mono.empty();
}> [!NOTE]
> Notice the check !StringUtils.hasText(appsmithBaseUrl). Even with the patch, if the administrator does not explicitly set the APPSMITH_BASE_URL environment variable, the check is skipped to maintain backward compatibility. This means patching alone isn't enough; you must configure the environment correctly.
Exploiting this requires no authentication and can be automated. Here is how an attacker turns a standard password reset feature into an account hijacking machine.
admin@company.com).POST request to the password reset endpoint, but injects a malicious Origin header.POST /api/v1/users/forgotPassword HTTP/1.1
Host: appsmith.target-corp.com
Origin: https://attacker-logger.com
Content-Type: application/json
{"email": "admin@company.com"}123-ABC) and constructs the link using the malicious origin: https://attacker-logger.com/user/resetPassword?token=123-ABC.no-reply@appsmith.target-corp.com. Since they might use Appsmith daily, they click the link to reset their password.attacker-logger.com. The attacker's server logs the request path, capturing the token parameter.This vulnerability scores a CVSS 9.6 for a reason. In a corporate environment, Appsmith is rarely used for trivial tasks. It is the glue code for internal operations.
If an attacker compromises an Appsmith admin account, they likely gain:
This is not just a user losing their account; it is a potential full-compromise of the organization's data layer.
Remediation is a two-step process. Do not skip step 2.
Step 1: Upgrade Update your Appsmith instance to version 1.93 or later immediately. This introduces the code capable of validating the header.
Step 2: Configure
You must define the APPSMITH_BASE_URL environment variable in your docker-compose.yml or Kubernetes deployment. This tells Appsmith exactly what its public URL is, so it stops guessing based on attacker input.
services:
appsmith:
environment:
- APPSMITH_BASE_URL=https://appsmith.yourcompany.comIf you fail to set this variable, the patch code we analyzed earlier will simply return Mono.empty() and bypass the validation check, leaving you just as vulnerable as before. Don't be that person.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Appsmith Appsmith | <= 1.92 | 1.93 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-22794 |
| CVSS Score | 9.6 (Critical) |
| CWE | CWE-346 (Origin Validation Error) |
| Attack Vector | Network (AV:N) |
| EPSS Score | 0.00012 |
| Exploit Status | PoC Available / Active Exploitation |
The application does not properly verify that the source of data or communication is valid.