CWE-288, Authentication Bypass Using an Alternate Path or Channel, is a product that requires authentication but has an alternate path or channel that does not.
The login works. People just never have to go through it. It affects web applications, network devices, operating systems, and installers, which matches the platforms MITRE lists: language and technology independent, plus web based.
How it happens
Authentication is usually attached to one route: the main page, the main port, the main prompt. A resource can often be reached by other routes that were added later, left in for debugging, or created by the platform itself. Each unguarded route is a bypass.
MITRE's examples span decades and layers:
- CVE-2000-1179 (Gold): a router allows remote attackers to read system logs without authentication by connecting directly to the login screen and typing certain control characters.
- CVE-1999-1454 (Gold): attackers with physical access may bypass the password prompt by pressing the Escape key.
- CVE-1999-1077 (Gold): an OS allows local attackers to bypass the password protection of idle sessions through a keyboard sequence that brings up a debugger.
- CVE-2003-0304 (Gold): a direct request of an installation file lets an attacker create administrator accounts.
The last one is the pattern most web teams will recognize. The app is locked down, but the setup script was left in place and does not ask who is calling.
# Vulnerable: the dashboard is protected, the export route is not
@app.get("/dashboard")
@login_required
def dashboard():
return render("dashboard.html")
@app.get("/export/all-users.csv")
def export_users(): # added later, no decorator
return send_file("users.csv")
Other forms include a second port or protocol that skips the check, an API that sits beside a protected web UI, a mobile or legacy endpoint, a debug interface, and a recovery flow that logs the user in.
What an attacker gains
MITRE lists one consequence: bypassing a protection mechanism. That sounds mild, but the protection mechanism is the login. Once it is bypassed, the attacker holds whatever the protected function offers. The installation file example ended in administrator accounts.
How to find it
The method is enumeration, since you are hunting for routes nobody remembers:
- List every way in. Web routes, API paths, static files, ports and protocols, management interfaces, debug and installer files, serial and local consoles.
- Request each without credentials. For every route that serves protected content, call it with no session.
- Compare routes to content. For each sensitive resource, ask which paths reach it. If the answer is more than one, test each.
- Look for leftovers. Installation, setup, test, backup, and example files in a deployed tree.
- Scan the running system. Port scans and web scans show what is exposed, even if the code review missed it.
Try it yourself
The demo below scans a web application and lists pages reachable without logging in. Use the same approach on your own staging environment.
Click through it at your own pace, or open it full screen.
How to fix it
- Protect the resource, not the route. Put the authentication check where the data or action lives, so every path inherits it.
- Deny by default. New routes require authentication unless someone marks them public on purpose.
- Use one choke point. A gateway or middleware in front of everything beats per route decorators that can be forgotten.
- Remove what is not needed. Delete installation files after setup, disable debug interfaces, close unused ports.
- Cover all channels equally. If the web UI needs a login, the API, the mobile endpoint, and the management port do too.
- Test it. Add a test that requests every route unauthenticated and expects a refusal, with an explicit list of public exceptions.
@app.before_request
def require_auth():
if request.endpoint in PUBLIC_ENDPOINTS:
return
if not current_user.is_authenticated:
abort(401)
Quick check
Why is protecting each route individually risky?
Every new route has to remember the protection. One that does not creates an alternate path, as with a later added export route. A default deny check at a single point avoids that.
What is an example of an alternate channel rather than an alternate route?
The router in CVE-2000-1179, where connecting directly to the login screen and typing certain control characters exposed logs without authentication.
Sources
- MITRE CWE-288: https://cwe.mitre.org/data/definitions/288.html
- Safeguard Gold entry: https://gold.safeguard.sh/cwe/CWE-288