What would you like to learn?

Try PowerShell, networks, XSS, or Step 138

Browse the full curriculum →

Web security

Step 146. Authentication Attacks, Combined — Four Ways to Knock on the Front Door

Step 146Estimated practice · 4 hours

Level 2 — Introduction to Security and Attack Skill Basics | Difficulty ★★★☆☆ | Estimated time: 4 hours

Prerequisites: Step 122 (online brute force), Step 133 (Burp Intruder), and Step 134 (cookie and session attacks) complete.

  • What you need: Python 3 + Flask (local reproduction), Burp Suite (lab), a text editor
  • ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.

The login is the front door of the attack surface. So far you’ve learned the front-door attack parts separately — brute force (Step 122), session cookies (Step 134), proxy interception (Steps 132–133). Today we assemble those parts into a single system called "authentication attacks." Default credential checks, username enumeration, brute force, client-trust flaws — we measure all four in a row on a login server you build yourself, and organize the defenses against each attack into a table.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Explain why a default-credential check is the "first five minutes" of a real penetration test
  • Demonstrate the principle of enumerating usernames from differences in login failure messages
  • Reproduce and bypass a design flaw where the server trusts client values (role, success flags)
  • Know how to set the "success criterion" in brute force
  • Match four attack types to defenses (rate limit, lockout, unified messages, MFA) in a table

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Python 3 + Flask (local login server) / Burp Suite Intruder (lab)
Today’s commands Repeated login POSTs, ?role=admin parameter manipulation, response message comparison
Concepts needed Default credentials, username enumeration, client-trust flaws, rate limit
Today’s artifact A record of the four authentication attacks + an attack↔defense matching table

2-1. Default Credentials — The First Five Minutes of an Attack

In a real assessment, when an attacker meets a login window, the first thing they do is not brute force. It’s typing admin/admin, admin/password, test/test by hand. Routers, admin consoles, and in-house tools still often ship with their factory passwords unchanged. A try costs a few seconds, and success ends it — the most cost-effective attack, so it’s always first.

2-2. Username Enumeration — What the Failure Message Tells You

If, on a failed login, the server says "no such account" versus "incorrect password," the attacker can learn whether an account exists without knowing a single password. This is username enumeration. Even with identical messages, subtle differences in response time or response body length still leak it. The defense is simple — unify the failure message into one: "The account or password is incorrect."

2-3. Client-Trust Flaws — When the Server Is a Glass Door

Some designs return {"success": true, "role": "user"} after a successful login, and then blindly trust the role value the client sends in later requests. The attacker only has to swap role=user for role=admin in a proxy. Every value the client sends can be manipulated — you already know this, having intercepted and modified requests in Step 132. Authorization decisions must be made from the session store inside the server.

2-4. The Defense Triangle — Slower, Stopped, Blind

Defenses against authentication attacks come in three layers.

  1. Rate limit: cap the attempt speed — it explodes the "cost per attempt" of brute force.
  2. Account lockout: lock the account after consecutive failures — attack halted + a notification to the defender (Step 122).
  3. Unified error messages + MFA: block enumeration, and add a second door even if the password falls.

3. Follow Along

3-1. Target of the Simulation — A Login Server with Four Flaws Planted

We extend Step 122’s login server. Three accounts (admin/admin, alice/correct-horse-battery, test/test1234) and two deliberately planted flaws — differentiated error messages, and client-role trust (this book was measured on 2026-09-09).

Input (the core of auth_lab.py)

from flask import Flask, request, jsonify
import json

app = Flask(__name__)
USERS = {"admin": "admin", "alice": "correct-horse-battery", "test": "test1234"}

@app.route("/login", methods=["POST"])
def login():
    data = json.loads(request.data.decode())
    u, p = data.get("username", ""), data.get("password", "")
    if u not in USERS:
        return jsonify({"success": False, "error": "No such account"}), 401
    if USERS[u] != p:
        return jsonify({"success": False, "error": "Incorrect password"}), 401
    return jsonify({"success": True, "role": "user"})

@app.route("/admin")
def admin_panel():
    role = request.args.get("role", "guest")   # vulnerable: trusts the client value as-is
    if role == "admin":
        return "Admin panel — flag{client_trust_flaw}"
    return "Access denied", 403

How to read it: the flaws are in two places. ① The failure reason is differentiated by message. ② /admin reads "who this is" not from a session but from the request’s role parameter. Both are classics found again and again in real services.

3-2. Default Credential Check — The First Five Minutes of an Attack

Input

for u, p in [("admin", "admin"), ("admin", "password"),
             ("test", "test"), ("test", "test1234")]:
    code, body = post("/login", {"username": u, "password": p})
    print(f"{u} / {p} -> {code}")

Output (measured 2026-09-09):

admin / admin    -> 200 {"role":"user","success":true}
admin / password -> 401 (failure)
test / test      -> 401 (failure)
test / test1234  -> 200 {"role":"user","success":true}

How to read it: two accounts opened in four tries — before any brute-force tool even comes out. In a real report, "login succeeded with the first tried combination" is the single most powerful line, and for the defender it becomes the cheapest measure: "enforce changing factory passwords."

3-3. Username Enumeration — What the Failure Message Tells You

Input

for u in ["admin", "nosuchuser", "alice", "hacker"]:
    code, body = post("/login", {"username": u, "password": "wrongpw"})
    print(u, "->", body)

Output (measured 2026-09-09):

admin      -> {"error":"Incorrect password","success":false}
nosuchuser -> {"error":"No such account","success":false}
alice      -> {"error":"Incorrect password","success":false}
hacker     -> {"error":"No such account","success":false}

How to read it: every password was wrong, yet the responses tell you exactly which accounts exist. admin and alice "exist"; nosuchuser and hacker "don’t." The attacker can now focus brute force only on enumerated accounts — target selection for the dictionary attack, done for free. Unify the messages and this channel closes.

3-4. Client-Trust Flaw — Swapping the role

Log in normally as alice, then request the admin page with a different role attached.

Output (measured 2026-09-09):

Normal login response: {"role":"user","success":true}
GET /admin?role=user  -> 403 Access denied
GET /admin?role=admin -> 200 Admin panel — flag{client_trust_flaw}

How to read it: you logged in as "user," yet one parameter opened the admin panel. Because the server did not check "the requester’s authority" from the session store — it trusted a value inside the request. It’s exactly the same attack as editing one line of a request in Burp Repeater. The defense: read authority only from the server-side session, and use client values for nothing.

3-5. Brute Force and the Success Criterion

We run Step 122’s brute forcer against this server. The point is "how do you tell success" — we set the criterion as the "success":true string in the response body.

Output (measured 2026-09-09):

[+] Breached on attempt 7! alice / correct-horse-battery
7 attempts, 0.06 seconds, 8.9ms per attempt

How to read it: here’s an honest confession. While preparing this measurement, I wrote the match string as "success": true (with a space), but the actual response was "success":true (no space), and the success was missed. If the success criterion is off by even one character, brute force breaks through and never knows. It’s the same in Burp Intruder — measure the failure response’s length first, and configure it to flag responses with a different length or a redirect (302) as success candidates.

3-6. The Attack↔Defense Matching Table

Let’s fold today’s measurements into a table.

Input (written in your notes)

Attack              | Today's measurement   | Defense                    | Bypass possibility
Default credentials | admin/admin succeeded | Enforce initial password change | Low
Username enumeration | Split by error message | Unify failure messages    | May survive via response-time differences
Client-trust flaw   | ?role=admin passed    | Judge authority in server-side session | None if the server design is right
Brute force         | Breached on attempt 7 | Rate limit + lockout + MFA | Partial bypass via slow attacks, distributed IPs

How to read it: the rightmost column is what matters. There is no perfect defense; defense is the work of raising the attack’s cost. Even a rate limit has the bypass "slowly, from distributed IPs" — but at that moment the attack cost balloons to days and the logs grow long. That is a detection victory.


4. Missions & Exercises

Mission — The Four-Piece Authentication Attack Set

  1. Build the 3-1 login server, try the four default-credential combinations, and record the accounts that succeed
  2. Username enumeration: fail with five candidate names and build a list of "existing accounts"
  3. Obtain the admin panel’s flag via ?role=admin manipulation
  4. Set your own brute-force criterion (string or response length) and breach the alice account
  5. Complete an attack↔defense table in the 3-6 format — including the bypass-possibility column

Exercises

Exercise 1. Explain in terms of "cost-effectiveness" why a default-credential check always comes before brute force.

Exercise 2. Name two channels through which username enumeration may remain possible even after failure messages are unified.

Exercise 3. Explain why "every value the client sends can be manipulated" is the first principle of authentication design, connecting it to today’s ?role=admin measurement.

Exercise 4. Describe the double blow an account-lockout policy deals to an attacker, and the side effect a defender must watch for (from a legitimate user’s perspective).


Answers & completion criteria · expand/collapse

5. Model Answers & Completion Criteria

Mission Model Answer

Among the default credentials, admin/admin and test/test1234 succeed. In username enumeration, admin, alice, and test are confirmed as live accounts via "Incorrect password." The flag is flag{client_trust_flaw}.

How to verify: ① is the measured output of each of the four attacks recorded? ② did the enumeration experiment include a "nonexistent account" as a control? ③ is the brute-force criterion stated explicitly? ④ does the defense table include bypass possibilities?

Exercise Answers

Answer 1. Trying default credentials is a few seconds of hand motion, with a surprisingly high success rate. Brute force costs thousands of attempts, time, and log traces. Using the cheapest, highest-yield means first is the basic order of attack.

Answer 2. Differences in response body length, and differences in response time. An existing account runs the password-hash comparison and is microscopically slower; a nonexistent account is rejected early. Unified messages are a necessary condition, not a sufficient one.

Answer 3. Every field of a request — parameters, cookies, headers — is freely modifiable in the attacker’s proxy. Today’s proof: ?role=user became ?role=admin and the admin panel opened. That is why judgments like authority, price, and ownership must come only from state the server keeps (session store, DB).

Answer 4. To the attacker it’s a double blow: ① attempts physically stop, and ② the lockout event is reported to the defender via logs and alerts. The side effect: an attacker can deliberately fail someone else’s account repeatedly to lock it — a denial of service abuse — so in practice you combine delays (increasing wait time) or source blocking instead of pure lockout.

Completion Criteria Checklist

  • [ ] I recorded accounts that succeeded via the default-credential check
  • [ ] I reproduced username enumeration from error-message differences
  • [ ] I entered the admin panel via role-parameter manipulation
  • [ ] I set my own brute-force success criterion and breached the account
  • [ ] I can explain why unified failure messages are a defense
  • [ ] I matched the four attacks to defenses in a table
  • [ ] I wrote one line each on every defense’s bypass possibility

6. Common Pitfalls & Fixes

Wall 1. Brute force succeeded but ends as "failure"

Symptom: the correct password is in the list, but no breach message appears.
Cause: the success-match string must not differ from the actual response by even one character. Even in preparing today’s measurement, the difference between "success": true (space) and "success":true caused the success to be missed.
Fix: before setting the criterion, fetch one raw success response with curl and copy it verbatim. In Burp Intruder, anchoring on "the failure response’s length" is more robust.

Wall 2. Username enumeration doesn’t work — the messages are identical

Symptom: whatever name you enter, the same error comes out.
Cause: the target has already unified its messages. That’s proper defense.
Fix: measure response body length and response time. If both are identical, this channel is closed — and that too is a record: "the defense is working."

Wall 3. It’s 403 even with ?role=admin

Symptom: you changed the parameter but it’s blocked.
Cause: the server has a correct implementation that reads authority from the session — or the parameter name is different.
Fix: compare the login response and later requests in a proxy, and first observe which field (cookie? header? parameter?) carries the authority information. If none does, this flaw doesn’t exist here.

Wall 4. Mid-attack everything turns to failure — lockout triggered

Symptom: from some point on, every login fails.
Cause: a rate limit or account lockout kicked in.
Fix: in a lab, restart the server and lengthen the interval between attempts. The fact that "a lockout triggered" is itself a harvest of the defense evaluation.

Wall 5. admin/admin opened right away and it feels suspicious

Symptom: success on the very first try.
Cause: practice labs are made weak on purpose. In the field too, default passwords are a staple finding.
Fix: it’s normal. But record it — "login succeeded on the first combination" is first-page material for a report.


7. Summary

Today’s Concepts

Concept One-line explanation
Default credentials Factory passwords like admin/admin — the attack’s first five minutes
Username enumeration Learning account existence from failure-message, length, or timing differences
Client-trust flaw A design error that judges authority info like role from client values
Success criterion The rule that distinguishes a success response in brute force (string, length, redirect)
Rate limit Attempt-speed cap — brute-force cost explodes
Unified error message One "account or password is incorrect" blocks enumeration

Today’s Commands

Command What it does
POST /login (repeated) Default-credential and brute-force attempts
GET /admin?role=admin Testing the client-trust flaw
Comparing failure response bodies Username enumeration
Measuring response length/time Checking residual channels after message unification
Burp Intruder § position marking Web-form brute force (lab)

An Instinct More Important Than Commands

The four authentication attacks are not separately learned techniques — they spring from a single question: "What does this login trust?" If it trusts passwords, you knock with a dictionary; if it trusts error messages, you enumerate accounts; if it trusts client values, you swap them. Defense design is that question flipped: the server trusts nothing but the state it keeps itself.

And remember today’s small accident — the success missed because of one wrong match character. An attack tool is not someone who answers you; it’s a machine that counts the criteria you set for it. The eye that sets the criteria is the skill itself.


Once every box is checked, Step 146 is complete.

ONE STEP FURTHER

Finished this lesson?

Check the completion criteria, then mark your progress.

Something wrong with this page or a link? Let us know.
Next