SECURE_NODE // ATHENS_GR
← BACK TO RESEARCH Technique · API Security · Broken Access Control

Unauthenticated API Endpoint Exposing Password Hashes

10 JUN 2026| ≈ 3 MIN READ| BrokenAccessControlAPIPasswordHashing
SANITIZED ▸ Methodology only. All hostnames, endpoint paths, usernames, hash values, and organizational details are lab placeholders. No client name, real endpoint, real hash, or production system identifier appears here. Assessment was authorized and scoped in writing.

API endpoint enumeration with a common wordlist surfaces paths that the application doesn't link to from its UI but serves without authentication. This engagement found /api/users/datatable — a backend data-table endpoint that returns the full user list in a JSON envelope. The response included password hashes, password salts, the hashing algorithm identifier, and the role description for every user in the system. No authentication header was required.

FINAL SEVERITY
Critical
admin hash exposed, no auth
SCOPE
Grey-box
web app, partial API docs
ALGORITHM
SHA-256 unsalted
fast hash, crackable offline

The endpoint

An API enumeration pass against the API root with a common wordlist returned a 200 for /api/users/datatable. The response was not paginated and returned all users. The admin account record included the full credential material:

# api enumeration hit
[api-fuzzer] -u https://app.acme-lab.test/api/FUZZ -w api-common.txt

/api/users/datatable  [200]  [Content-Type: application/json]

# response (admin user record — lab values)
GET /api/users/datatable HTTP/1.1
Host: app.acme-lab.test

HTTP/1.1 200 OK
{
  "users": [
    {
      "id": 1,
      "username": "admin",
      "email": "admin@acme-lab.test",
      "passwordHash": "<lab-hash>",
      "passwordSalt": "lab_salt_example",
      "hashAlgorithm": "SHA-256",
      "roleId": 1,
      "roleDescription": "Full system access"
    }
  ]
}

# SHA-256 with a static salt — crackable offline with rockyou.txt
# no authentication required to retrieve this response

SHA-256 is a fast general-purpose hash function. With GPU acceleration, billions of SHA-256 operations per second are achievable on commodity hardware. A password matching rockyou.txt at SHA-256 speed cracks in seconds. The hash here corresponds to a common default — "admin" — and the static salt does not prevent dictionary attacks, it only requires that the salt be included in the input, which is known from the response.

The RBAC bypass companion

A second finding from the same engagement: an RBAC bypass on the administrative panel where role assignment checks were enforced in the UI but not the API, allowing any authenticated user to escalate to administrator role by calling the user-update endpoint directly.

The test timeline

DAY 1 — API ENUMERATION

ffuf pass against the API root. Common API wordlist. /api/users/datatable returns 200 with no authentication header present.

DAY 1 — HASH CONFIRMED

Admin password hash, salt, and algorithm returned unauthenticated. SHA-256 with static salt. roleDescription: "Full system access" for user ID 1.

DAY 1 — RBAC BYPASS

Role escalation via direct API call. UI enforces role check; API endpoint does not. Any authenticated user can assign themselves administrator role via the user-update API.

DAY 1 — HASH SCOPE

All user hashes assessed. Full user list returned — all password hashes exposed. Admin hash corresponds to a dictionary word. Offline crack confirmed in lab environment.

DAY 2 — REPORT

Both findings documented. Offline crack path captured with evidence. SHA-256 upgrade path recommended alongside access control fix.

Closing the exposure

  • Require server-side authentication on every API endpoint, including internal and admin-facing paths. The presence of a path in a wordlist does not mean it must remain accessible. Add authentication middleware to the datatable route. Verify every API endpoint has an authentication check — not just the ones linked from the UI.
  • Separate credential storage from user-listing responses. The user datatable endpoint should return identifiers, usernames, and roles — not hashes, salts, or algorithm identifiers. Credential fields must never appear in any response payload regardless of the requester's authentication state.
  • Migrate password storage to Argon2id or bcrypt. SHA-256 is a fast hash designed for throughput, not password storage. Argon2id (the recommended choice from OWASP) and bcrypt are designed to be computationally expensive, making GPU-accelerated offline cracking orders of magnitude slower. Migrate on next login or in a scheduled background job.
  • Run an API enumeration pass before every production deploy. Tools like ffuf with a common API wordlist take minutes to run and surface exactly this class of finding. Adding it to the deployment pipeline or pre-release checklist would have caught this endpoint before it reached production.

The takeaway

The endpoint was not hidden — it was in a common wordlist, returning 200, with no authentication. Admin credentials in the response body, no auth required, fast hash. Discovery: one ffuf pass. Crack: seconds.