Capstone: investigate a breach
Work through real-looking logs like a security analyst: find the scanner, the stolen account and the stolen data.
- Separate background noise (scanners) from a targeted attack
- Reconstruct how an account was taken over
- Measure what data left, and recommend fixes
Monday, 9:12. The support team reports that a customer list is being sold online. Your job: work out what happened from the web and sign-in logs, using everything from this track.
Investigations follow a rhythm: form a hypothesis, test it against the evidence, write down what you found - with timestamps. Start broad (who was active?), then narrow down (which activity was malicious?).
Step 1 - the noise. Every public server is constantly probed by automated scanners looking for leaked files (/.env, /.git/config) and common admin pages. They’re worth blocking, but usually aren’t the story. Your first exercise flags them so you can set them aside.
Step 2 - the story. Look for an account that was taken over: a run of failed sign-ins followed by a success from the same address. Then follow that address: what did it download?
from collections import Counter
requests = ["192.0.2.66 /.env", "198.51.100.20 /cart", "192.0.2.66 /.git/config"]
print(Counter(line.split()[0] for line in requests).most_common(1))[('192.0.2.66', 2)]Key takeaways
Filter background noise before hunting for the real attack.
Failures followed by a success from one address is a classic account-takeover signal.
Follow the attacker’s address forward in time to measure impact.
Lesson quiz
5 questions · pass with 4 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: write Python
Write Python in the editor and run it against sample inputs. Python runs locally in your browser using a WebAssembly runtime.
Step 1: flag the scanners
Each line is SECONDS IP METHOD PATH STATUS. Count, per IP, requests to the probe paths /.env, /.git/config, /wp-login.php, /phpmyadmin, /admin, and responses with status 404.
An IP is a scanner if it made 3 or more probe requests or got 5 or more 404s. Print IP: P probes, N not found for each scanner, sorted by IP.
- Two scanners and a customer
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Step 2: report the account takeover
Each line is either SECONDS IP LOGIN USER fail|success or SECONDS IP GET PATH STATUS BYTES, in time order.
An account is compromised when a login succeeds from an IP that had 3 or more earlier failed logins for that same user. For each compromise, print:
compromised: USER from IP at t=SECONDS (after N failures)
first seen: t=FIRST_TIME_THIS_IP_APPEARS
exported: B bytes in K requestswhere the export counts that IP’s later requests whose path starts with /export and whose status is 200. If there’s no compromise, print no compromise found.
- The breach
- A forgetful user, not an attack
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…