# Mini Capture The Flag — Broken Authentication (Online)

<div style="background: linear-gradient(135deg, #0d1117 0%, #161b22 50%, #1f2937 100%); color: #e0e0e0; padding: 18px 24px; border-radius: 10px; margin-bottom: 24px;">
  <strong style="color:#58a6ff; font-size:1.1em;">Scenario</strong><br><br>
  You are a penetration tester hired to assess the security of <strong style="color:#f0883e;">CyberCorp</strong>, a fictional cybersecurity company. You have been given no credentials and no internal documentation — only the URL of their public website.<br><br>
  <span style="color:#8b949e;">Your objective: gain access to the admin panel at <code style="color:#79c0ff;">/admin</code>.</span>
</div>

This lab demonstrates **OWASP A07: Identification and Authentication Failures** through a realistic, multi-step attack chain. Each phase mirrors real-world penetration testing methodology: reconnaissance → enumeration → exploitation → mitigation.

---

## Prerequisites

```bash
# Install Python dependencies
pip install flask requests beautifulsoup4

# Optional: install cewler for live wordlist extraction
pip install cewler
```

## Starting the application

```bash
cd CybercorpWebsite/
python create_db.py    # initialise the database (run once)
python app.py          # start the Flask server on http://127.0.0.1:5001
```

Open your browser at **http://127.0.0.1:5001** and leave it running for the entire lab.

---

## Lab overview

<table style="border-collapse: collapse; width: 100%; margin: 12px 0;">
  <thead>
    <tr style="background: #161b22; color: #58a6ff;">
      <th style="padding: 8px 14px; text-align: left;">Phase</th>
      <th style="padding: 8px 14px; text-align: left;">Technique</th>
      <th style="padding: 8px 14px; text-align: left;">Tool</th>
    </tr>
  </thead>
  <tbody>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">1. Reconnaissance</td><td style="padding:7px 14px;">Manual application exploration</td><td style="padding:7px 14px;">Browser, Wappalyzer</td></tr>
    <tr><td style="padding:7px 14px;">2. Content discovery</td><td style="padding:7px 14px;">Custom wordlist extraction + directory bruteforce</td><td style="padding:7px 14px;">cewler, corp_buster.py</td></tr>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">3. Username enumeration</td><td style="padding:7px 14px;">OSINT from public pages, username pattern generation</td><td style="padding:7px 14px;">usernamer.py</td></tr>
    <tr><td style="padding:7px 14px;">4. Password profiling</td><td style="padding:7px 14px;">Targeted wordlist generation from personal data</td><td style="padding:7px 14px;">cupp.py</td></tr>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">5. Online cracking</td><td style="padding:7px 14px;">Dictionary attack against login form (no rate limit)</td><td style="padding:7px 14px;">corp_cracker.py</td></tr>
    <tr><td style="padding:7px 14px;">6. Mitigation</td><td style="padding:7px 14px;">Rate limiting implementation and re-test</td><td style="padding:7px 14px;">app.py</td></tr>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">7. SQL injection bypass</td><td style="padding:7px 14px;">Authentication bypass via SQL injection</td><td style="padding:7px 14px;">sqlmap, manual</td></tr>
  </tbody>
</table>

---

## Phase 1 — Reconnaissance

Before running any tool, explore the application manually like a real attacker would.

**What to look for:**

- **Page structure** — which URLs are publicly accessible?
- **Technologies** — is the tech stack visible? Install the [Wappalyzer](https://www.wappalyzer.com/) browser extension to detect frameworks, server types, and libraries automatically.
- **Source code** — read the HTML source (`Ctrl+U`). Developers often leave comments, hidden fields, debug notes, or internal paths.
- **Login surfaces** — any form that accepts credentials is a potential target.
- **Search functionality** — any input that queries a database is worth noting.

**What you find on CyberCorp:**

| Page | URL | What's interesting |
|---|---|---|
| Home | `/` | Developer comments in HTML source |
| People | `/people` | Full names and departments of 4 employees |
| Apply | `/apply` | Company email domain (`@cybercorp.com`) |
| Products | `/products` | Search form — potential SQL injection |
| Smartworking | `/smartworking` | Login page — the main target |

<div style="background:#fff8e1; border-left: 4px solid #f9a825; padding: 12px 16px; border-radius: 0 6px 6px 0; margin: 12px 0;">
  <strong>💡 Note:</strong> Always read the page source before running any automated tool. In this lab, the HTML source of the home page contains developer notes that hint at additional pages and vulnerabilities.
</div>

---

## Phase 2 — Content discovery

Manually browsing finds only the pages the developers intended to be public. Content discovery looks for pages that exist but are not linked.

### Step 2a — Extract a custom wordlist with cewler

Generic wordlists (like those from SecLists) work well for standard frameworks but miss company-specific paths. **cewler** scrapes a website and builds a wordlist from its own text — catching things like `/hosting`, `/smartworking`, or `/admin`.

```bash
cewler http://127.0.0.1:5001 -d 5 -o enumeration/cybercorp_dict.txt
```

Expected output:
```
│ URL:           http://127.0.0.1:5001
│ Strategy:      Exact same domain, max depth 5, 20 reqs/s
│ Words:         Mixed case, incl. numbers, min. 5 chars.
│ Words found:   230
```

Open `enumeration/cybercorp_dict.txt` and inspect it. You will see names, technical terms, and page names extracted directly from the site.

### Step 2b — Directory bruteforce with corp_buster.py

```bash
python enumeration/corp_buster.py
```

`corp_buster.py` sends a GET request for each word in the wordlist and checks the HTTP response code. Pages that return `200 OK` exist and are accessible.

**What gets discovered:**

| Found path | Status | What's there |
|---|---|---|
| `/hosting` | 200 | ⚠️ Internal source code repository — accidentally public |
| `/smartworking` | 200 | Login page — the attack target |

<div style="background:#fce4ec; border-left: 4px solid #e53935; padding: 12px 16px; border-radius: 0 6px 6px 0; margin: 12px 0;">
  <strong>⚠️ Cryptographic failure alert:</strong> The <code>/hosting</code> page is an accidental exposure of internal source code. It contains the raw SQL query used in the login function — including a classic SQL injection vulnerability. This is a real-world pattern: internal tools left accessible by misconfiguration. Take note of the query shown there; it will be relevant in Phase 7.
</div>

### Alternative tools

`corp_buster.py` is a minimal educational implementation. In real-world assessments, you would use:

```bash
# Gobuster (Go-based, fast)
gobuster dir -u http://127.0.0.1:5001 -w enumeration/cybercorp_dict.txt

# FFUF (very fast, highly configurable)
ffuf -u http://127.0.0.1:5001/FUZZ -w enumeration/cybercorp_dict.txt

# Dirsearch (Python, good output formatting)
dirsearch -u http://127.0.0.1:5001 -w enumeration/cybercorp_dict.txt
```

---

## Phase 3 — Username enumeration

We know the login page exists. Before guessing passwords, we need valid usernames.

### What we know from Phase 1

From `/people`:
```
Alice Johnson     — Penetration Testing
Bob Smith         — Incident Response
Eve Mallory       — Security Research
Mark Anthony      — Website Programmer
```

From `/apply`:
```
hr@cybercorp.com  → domain is @cybercorp.com
```

### Username pattern generation with usernamer.py

Corporate accounts follow predictable naming conventions. `usernamer.py` takes a list of full names and generates all common patterns automatically.

Create the input file:
```
# usernamer/users.txt
alice johnson
mark anthony
bob smith
eve mallory
```

Run the script:
```bash
python usernamer/usernamer.py
```

**Patterns generated** for each name (example with `bob smith`):

```
bob.smith@cybercorp.com
smith.bob@cybercorp.com
bobsmith@cybercorp.com
smithbob@cybercorp.com
bob_smith@cybercorp.com
smith_bob@cybercorp.com
bsmith@cybercorp.com
smithb@cybercorp.com
bob@cybercorp.com
smith@cybercorp.com
...
```

The output file `usernamer/user_wordlist.txt` contains **48 username candidates** across 4 employees.

<div style="background:#e8f5e9; border-left: 4px solid #43a047; padding: 12px 16px; border-radius: 0 6px 6px 0; margin: 12px 0;">
  <strong>📌 Why this works:</strong> Most organisations enforce a single naming convention for all accounts (e.g. <code>firstname.lastname</code>). Once you identify the pattern for one user, it applies to all. The goal of the wordlist is to find <em>which</em> convention this organisation uses.
</div>

---

## Phase 4 — Password profiling with CUPP

Generic wordlists work well for lazy passwords. For more targeted attacks, **CUPP** (Common User Passwords Profiler) generates a personalised wordlist based on information gathered about the target user.

```bash
python cupp/cupp.py -i    # interactive mode
```

CUPP asks for:
- Name, surname, nickname
- Birth dates (target, partner, child)
- Pet names, company names, favourite sports teams
- Custom keywords

It then generates hundreds of combinations: `alice1992`, `Alice!`, `aliceJohnson90`, `4l1c3`, etc.

**Pre-built profiles** are included in `cupp/` for two users. Inspect the output wordlists to see what CUPP generates and why.

<div style="background:#e3f2fd; border-left: 4px solid #1e88e5; padding: 12px 16px; border-radius: 0 6px 6px 0; margin: 12px 0;">
  <strong>🔍 Reflection:</strong> CUPP requires OSINT — social media scraping, LinkedIn profiles, public records. This is where reconnaissance and password cracking intersect. A technical attack always has a human intelligence component.
</div>

In this lab, we skip full profiling and fall back to a **common password list** for simplicity.

---

## Phase 5 — Online dictionary attack

### The vulnerability: no rate limiting

The login form at `/smartworking` (vulnerable version) accepts an unlimited number of login attempts with no delay, no lockout, and no CAPTCHA. An attacker can try thousands of username/password combinations automatically.

### Running the attack

```bash
python cracker/corp_cracker.py
```

`corp_cracker.py` sends POST requests to the login form combining:
- **48 usernames** from `user_wordlist.txt`
- **1,000 passwords** from `Pwdb_top-1000-tiny.txt`

That is up to **48,000 requests** — all handled automatically in minutes.

### How it detects success

The script sends each request and parses the HTML response with BeautifulSoup. A failed login returns an error message; a successful login redirects to `/admin` without an error. When the error message is absent, credentials are saved to `found_credentials.txt`.

### Result

```
✅ Found: anthony.mark@cybercorp.com : justinbieber
```

Log in manually at http://127.0.0.1:5001/smartworking with these credentials to access the admin panel.

<div style="background:#fce4ec; border-left: 4px solid #e53935; padding: 12px 16px; border-radius: 0 6px 6px 0; margin: 12px 0;">
  <strong>⚠️ Why it worked:</strong> Two compounding failures:<br>
  1. <strong>Weak password</strong> — <code>justinbieber</code> appears in every common wordlist.<br>
  2. <strong>No rate limiting</strong> — the application placed no obstacle in the path of an automated attack.
</div>

---

## Phase 6 — Mitigation: rate limiting

Now switch the application to its hardened login implementation and observe the effect.

### Enabling rate limiting in app.py

Open `CybercorpWebsite/app.py` and make the following changes:

1. **Comment out** the insecure `login()` view function
2. **Uncomment** the secure `login()` alternative

The secure version uses these parameters:
```python
MAX_ATTEMPTS = 5      # max failed attempts before lockout
LOCKOUT_TIME  = 120   # lockout duration in seconds (2 minutes)
```

After 5 failed attempts for a given username, all subsequent requests for that username are rejected for 2 minutes, regardless of what password is tried.

### Re-run the attack

Restart the server and run `corp_cracker.py` again:

```bash
python cracker/corp_cracker.py
```

**What happens:** After 5 attempts per username, the script receives lockout responses and can no longer test candidates. The attack is effectively neutralised for the lockout duration.

### Rate limiting is not a complete solution

<table style="border-collapse: collapse; width: 100%; margin: 12px 0;">
  <thead>
    <tr style="background: #161b22; color: #58a6ff;">
      <th style="padding: 8px 14px; text-align: left;">Mitigation</th>
      <th style="padding: 8px 14px; text-align: left;">What it stops</th>
      <th style="padding: 8px 14px; text-align: left;">What it doesn't stop</th>
    </tr>
  </thead>
  <tbody>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">Rate limiting</td><td style="padding:7px 14px;">Automated rapid guessing</td><td style="padding:7px 14px;">Slow distributed attacks, credential stuffing</td></tr>
    <tr><td style="padding:7px 14px;">CAPTCHA</td><td style="padding:7px 14px;">Fully automated bots</td><td style="padding:7px 14px;">Human-assisted attacks, CAPTCHA farms</td></tr>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">MFA</td><td style="padding:7px 14px;">Password-only attacks</td><td style="padding:7px 14px;">SIM swapping, MFA fatigue attacks</td></tr>
    <tr><td style="padding:7px 14px;">Password policy</td><td style="padding:7px 14px;">Common/short passwords</td><td style="padding:7px 14px;">Does not prevent reuse of compromised passwords</td></tr>
    <tr style="background:#f5f5f5;"><td style="padding:7px 14px;">Breach monitoring</td><td style="padding:7px 14px;">Reuse of known breached passwords</td><td style="padding:7px 14px;">Novel passwords not yet in breach databases</td></tr>
  </tbody>
</table>

In production, these mitigations are layered. No single control is sufficient on its own.

---

## Phase 7 — SQL injection bypass

If rate limiting blocks brute force, an attacker may pivot to a different vector. The `/hosting` page revealed the raw SQL query used in the (vulnerable) login function:

```python
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
```

This query is vulnerable to **SQL injection**: if the attacker controls the input, they can manipulate the query logic.

### Manual bypass

Switch back to the **insecure** login in `app.py`, restart the server, and try this in the login form:

| Field | Value |
|---|---|
| Username | `' OR '1'='1' --` |
| Password | `anything` |

The injected query becomes:
```sql
SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = 'anything'
```

`'1'='1'` is always true. `--` comments out the rest. The query returns the first user in the database — login succeeds without any valid credentials.

### Automated SQL injection with sqlmap

For more thorough testing, sqlmap can discover and exploit SQL injection automatically:

```bash
# Test the products search endpoint
python sqlmap-master/sqlmap.py \
  -u "http://127.0.0.1:5001/products?search=test" \
  --dbs

# Dump the users table once a vulnerability is confirmed
python sqlmap-master/sqlmap.py \
  -u "http://127.0.0.1:5001/products?search=test" \
  -D app \
  -T users \
  --dump
```

<div style="background:#fce4ec; border-left: 4px solid #e53935; padding: 12px 16px; border-radius: 0 6px 6px 0; margin: 12px 0;">
  <strong>⚠️ Two vulnerabilities for the price of one:</strong> SQL injection bypasses authentication entirely, regardless of rate limiting, CAPTCHA, or password strength. This is why input validation and parameterised queries are non-negotiable — no amount of account lockout logic protects a login backed by an injectable query.
</div>

### The fix: parameterised queries

```python
# ❌ Vulnerable
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"

# ✅ Safe — the database driver handles escaping
query = "SELECT * FROM users WHERE username = ? AND password = ?"
cursor.execute(query, (username, password))
```

---

## Directory structure

```
mini_catch_the_flag/
│
├── README.md                          ← this file
├── BrokenAuth_Online_TUTORIAL.md      ← original instructor notes
│
├── CybercorpWebsite/                  ← Flask application (the target)
│   ├── app.py                         ← main application (switch login versions here)
│   ├── app.sql                        ← database schema and seed data
│   ├── create_db.py                   ← database initialisation script
│   ├── app.db                         ← SQLite database (created by create_db.py)
│   └── templates/
│       ├── base.html
│       ├── home.html                  ← contains hidden developer comments
│       ├── people.html                ← source of employee names
│       ├── about.html
│       ├── apply.html                 ← reveals company email domain
│       ├── hosting.html               ← accidentally exposed internal page
│       ├── products.html              ← SQL injection target
│       ├── smartworking.html          ← login form
│       └── admin.html                 ← objective: gain access here
│
├── enumeration/
│   ├── corp_buster.py                 ← directory bruteforcer
│   └── cybercorp_dict.txt             ← wordlist extracted by cewler (230 words)
│
├── usernamer/
│   ├── usernamer.py                   ← username pattern generator
│   ├── users.txt                      ← input: full names
│   └── user_wordlist.txt              ← output: 48 username candidates
│
├── cupp/
│   ├── cupp.py                        ← personalised password profiler
│   ├── cupp.cfg                       ← configuration (leet mappings, years, etc.)
│   ├── peter.txt                      ← sample generated wordlist
│   └── andrea.txt                     ← sample generated wordlist
│
├── cracker/
│   ├── corp_cracker.py                ← automated login attack
│   ├── user_wordlist.txt              ← copy of usernamer output
│   ├── Pwdb_top-1000-tiny.txt         ← top 1,000 common passwords
│   └── found_credentials.txt          ← attack output: cracked credentials
│
└── sqlmap-master/                     ← SQL injection testing tool
    └── sqlmap.py
```

---

## The full attack chain — summary

```
[1] Browse /people          → Alice Johnson, Bob Smith, Eve Mallory, Mark Anthony
[2] Browse /apply           → domain: @cybercorp.com
[3] cewler scrape           → cybercorp_dict.txt (230 words)
[4] corp_buster.py          → discovers /hosting and /smartworking
[5] /hosting source code    → reveals vulnerable SQL query
[6] usernamer.py            → 48 username candidates
[7] corp_cracker.py         → anthony.mark@cybercorp.com : justinbieber
[8] Login → /admin          → 🏁 Flag captured
```

---

## Key takeaways

| Lesson | Detail |
|---|---|
| **OSINT feeds technical attacks** | Employee names from a public page directly enabled the username enumeration |
| **Weak passwords are low-hanging fruit** | `justinbieber` is #XX in every common wordlist |
| **No rate limiting = open invitation** | Without lockout, an attacker can try thousands of candidates unimpeded |
| **Rate limiting alone is not enough** | SQL injection bypasses it entirely |
| **Parameterised queries are mandatory** | The only reliable fix for SQL injection |
| **Defence in depth** | Rate limiting + MFA + parameterised queries + breach monitoring — no single control suffices |

---

## References

- [OWASP A07: Identification and Authentication Failures](https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/)
- [OWASP A03: Injection](https://owasp.org/Top10/A03_2021-Injection/)
- [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
- [SecLists — wordlist repository](https://github.com/danielmiessler/SecLists)
- [cewler](https://github.com/roys/cewler) — custom wordlist extractor
- [CUPP](https://github.com/Mebus/cupp) — Common User Passwords Profiler
- [sqlmap](https://sqlmap.org) — automatic SQL injection tool
