Full Disclosure mailing list archives
Whistlelink: Site-access password exposed in web server access logs via GET query string
From: Red Nanaki via Fulldisclosure <fulldisclosure () seclists org>
Date: Sun, 21 Jun 2026 09:44:34 +0000
Whistlelink: Site-access password exposed in web server access logs via GET
query string
Severity: CRITICAL
SUMMARY
The Whistlelink reporting portal protects optionally-enabled, password-gated
whistleblowing sites with a site-access password. When a visitor unlocks such a
site, the client validates the password by issuing an HTTP GET request that
carries the password as a URL query-string parameter:
GET /sys/api/v1.0/wl/publish_site/check_password?password=
The value is only Base64-encoded (js-base64), which is a reversible transport
encoding, not encryption or hashing. Because the secret travels in the request
line / URI, it is written verbatim into the front-end web server's access log
(the deployment fronts the application with nginx, which logs the full request
URI by default) on every unlock attempt, and is similarly retained by any
reverse proxy, load balancer, WAF, CDN, observability / log-aggregation
pipeline, log backup, and the user's browser history.
Anyone able to read those logs can trivially Base64-decode the value and recover
the cleartext site-access password, defeating the access control for the entire
protected whistleblowing site. For a whistleblowing platform - where the
confidentiality of who may reach the reporting channel is a primary protection
goal - this is treated as CRITICAL.
CVSS
CVSS v3.1 Base: 7.7 (High)
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
CVSS v3.1 Environmental (whistleblowing asset, Confidentiality Requirement
High): 9.8 (Critical)
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N/CR:H
CVSS v4.0: High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N
The operative rating for this product class is the environmental score: the
protected asset is a whistleblowing channel whose access confidentiality is
paramount (potential whistleblower-safety consequences), justifying CR:H, which
raises the score to 9.8 / Critical and the CRITICAL tag.
Metric justification (v3.1 Base):
- Attack Vector: Network. Access logs are routinely centralized to
network-reachable systems (log/observability platforms, off-host backups,
object storage). If logs are only local files, AV:L applies and the Base
score drops to ~6.
- Attack Complexity: Low. The secret is present, in recoverable form, in every
check_password log line; decoding Base64 is trivial.
- Privileges Required: Low. Reading the logs requires some access to logging /
operations infrastructure - a materially lower and broader-trust bar than
knowing the site password itself. If logs are exposed to an unauthenticated
party (e.g. a misconfigured log bucket), PR:N applies and Base ~ 8.6.
- User Interaction: None. No victim interaction needed to read the stored value.
- Scope: Changed. The vulnerable component (the web application) writes the
secret into a separate security authority (the host / log subsystem), and the
impact lands on the protected site and its users.
- Confidentiality: High. Full disclosure of the site-access credential (and,
where reused, the underlying password), granting access to the protected
whistleblowing site.
- Integrity / Availability: None. The flaw discloses data; it does not directly
modify or deny service.
Score derivation (v3.1):
- ISCBase = 1 - (1 - 0.56) = 0.56
- Impact (Scope Changed) = 7.52 x (0.56 - 0.029) - 3.25 x (0.56 - 0.02)^15 ~
3.99
- Exploitability = 8.22 x 0.85 (AV) x 0.77 (AC) x 0.68 (PR, scope-changed) x
0.85 (UI) ~ 3.11
- Base = Roundup(min(1.08 x (3.99 + 3.11), 10)) = Roundup(7.67) = 7.7 (High)
- Environmental with CR:H: MISC = min(1 - (1 - 0.56 x 1.5), 0.915) = 0.84
-> Modified Impact ~ 5.93
-> Roundup(min(1.08 x (5.93 + 3.11), 10)) = 9.8 (Critical)
COMPONENT
backend (API endpoint design) / packaging (web server logging configuration)
FOUND BY
Passive HAR traffic audit + static analysis of the published client bundle.
AFFECTED ENDPOINT
GET /sys/api/v1.0/wl/publish_site/check_password?password=
Client request construction (published JS bundle, de-minified):
// password validation service
checkPassword: function (t) {
return C.a.get("".concat(T, "/check_password"), { params: { password: t } });
}
// caller - submit handler of the password gate
submit: function () {
// ...
cs.checkPassword(ds["a"].encode(this.password)).then(function (e) {
if (e.data.data) {
localStorage["authtoken"] = ds["a"].encode(this.password); // reversible Base64,
also reused as the Auth header
// route into the protected site
}
});
}
ds is js-base64 v3.7.8 (standard alphabet A-Za-z0-9+/=), so
ds.a.encode(password)
is plain, reversible Base64 - not a hash and not encryption.
DETAILS
Two distinct defects combine:
1. CWE-598 - Sensitive information transmitted in a GET request query string.
The site-access password is sent as the "password" query parameter rather than
in a request body. URLs / query strings are persisted far more widely than
request bodies: front-end web server access logs (nginx logs the full request
URI by default), reverse proxies, load balancers, WAFs, CDNs,
log-aggregation / observability pipelines, log backups, and the local browser
history. Even over TLS, the value is recorded in cleartext at these endpoints.
2. CWE-261 / CWE-312 - Weak (reversible) encoding of a credential.
The value is only Base64-encoded. Base64 provides no confidentiality; anyone
reading a log line recovers the cleartext password by decoding it. The same
reversible token is also placed in localStorage and replayed as the Auth header
on every request, broadening exposure.
The combined result (CWE-532 - Insertion of Sensitive Information into Log
File):
the cleartext site-access password is durably stored, in recoverable form, in
web
server access logs on every unlock attempt. The password is a single shared
secret
gating the whole protected site, so a single log read defeats the access control
for all visitors.
IMPACT
- Disclosure of the site-access credential to any party with read access to web
server / proxy / aggregated logs or their backups - a broader and lower-trust
population than those authorized to access the protected site (operations
staff, log-platform users, backup holders, third-party log processors, or an
attacker who obtains logs via a separate flaw).
- Complete bypass of the password gate for the protected whistleblowing site,
exposing the existence, configuration, and reporting channel that the password
was meant to keep confidential.
- Credential-reuse blast radius: if the configured site password is reused
elsewhere (common for shared, human-chosen site passwords), the impact extends
beyond the platform.
- Long-lived exposure: log retention and backups mean the secret remains
recoverable long after any password rotation, until all historical logs are
purged.
ATTACK SCENARIO
1. A whistleblowing site is configured with site-access password protection.
2. Visitors (and the operator during setup / testing) unlock the site; each
unlock emits GET .../check_password?password=, logged by nginx and
downstream systems.
3. A log-platform user, backup holder, third-party log processor, or an attacker
who has obtained the access logs greps for "check_password", Base64-decodes
the captured value, and recovers the cleartext password.
4. The actor unlocks the protected whistleblowing site at will; rotating the
password does not remediate historical log copies.
REMEDIATION
1. Stop sending the password in the URL. Validate via POST with the credential
in the request body (TLS-encrypted, not logged in the request line). This is
the primary fix.
2. Never log the secret. Ensure the body-based credential is excluded from
access / error logs and from any observability / WAF capture; scrub historical
logs and backups that contain check_password?password=.
3. Do not rely on Base64 for confidentiality. Treat the site password as a
secret
end-to-end; do not persist a reversible copy in localStorage / the Auth header
- derive a short-lived, server-issued session token instead (as already done
for case authentication).
4. Rotate any site passwords that may have been logged, and treat reused
passwords as compromised.
5. Server-side enforcement: confirm the password gate is enforced on the
protected data endpoints server-side, not only via the client-side route guard.
6. Throttle check_password and avoid a plain boolean oracle to limit brute
force.
REFERENCES
- CWE-598: Use of GET Request Method With Sensitive Query Strings
- CWE-532: Insertion of Sensitive Information into Log File
- CWE-312: Cleartext Storage of Sensitive Information
- CWE-261: Weak Encoding for Password
- OWASP ASVS v4.0.3 7.1.1 (no sensitive data in logs), 8.3.1 (sensitive data not
sent in URL parameters)
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/
Current thread:
- Whistlelink: Site-access password exposed in web server access logs via GET query string Red Nanaki via Fulldisclosure (Jul 02)
