
Reconstructed a complete web attack from ELK logs. Nmap → Gobuster → Hydra → web shell → LFI → database dump. The whole attack took 50 minutes. Every single step left obvious traces.
The Setup
Toy company got compromised. Suspicious activity on their e-commerce server, possible database breach.
I got access to ELK Stack with web server logs from July 26, 2023. Zero context. Just “figure out what happened.”
Opened Kibana and started hunting.
Finding the Noise
First thing: find the attacker IP.
Clicked on source.ip field in Kibana → View top values.
Result:
- 10.0.2.15: 2,000+ requests
- Every other IP: 10–50 requests
One IP doing 40x more traffic than everyone else. That’s my guy.
Attacker IP: 10.0.2.15
The Recon Phase
Filtered everything to that IP, sorted by time (oldest first).
First User-Agent I see: Nmap Scripting Engine
So they started with an Nmap scan. Standard recon — checking what services are running, what versions, looking for known vulns.
First tool: Nmap
Directory Brute Force (1,867 Attempts)
Next up in the logs: tons of 404 errors from the same IP.
Checked the User-Agent: Mozilla/5.0 (Gobuster)
Gobuster is a directory brute-forcing tool. It tries thousands of common paths:
- /admin
- /login
- /backup
- /uploads
- etc.
KQL Query:
transaction.remote_address: "10.0.2.15" AND response.status: 404
Result: 1,867 failed requests. They threw everything at the wall to see what stuck.
Failed requests: 1,867
What They Actually Found
If 1,867 requests failed, some must have succeeded. Filtered for 200 responses from Gobuster.
KQL Query:
transaction.remote_address: "10.0.2.15"
AND response.status: 200
AND request.headers.User-Agent: "Mozilla/5.0 (Gobuster)"
Successful finds:
- /robots.txt
- /interesting/a76637b62ea99acda12f5859313f539a ← Flag here
- /admin-login.php ← Jackpot
- /uploads/
They found an admin login page and an upload directory. Game over from here.
Flag in /interesting/: a76637b62ea99acda12f5859313f539a
Admin panel: /admin-login.php
Brute Forcing the Login
Next User-Agent in the logs: Mozilla/4.0 (Hydra)
Hydra = password cracking tool. They’re brute-forcing the admin login.
Found the successful login by filtering for 200 responses to the login page from Hydra.
KQL Query:
transaction.remote_address: "10.0.2.15"
AND request.headers.User-Agent: "Mozilla/4.0 (Hydra)"
AND response.status: 200
AND http.url: "/admin-login.php"
Looked at the request.headers.Authorization field - it's Base64 encoded.
Decoded: admin:thx1138
Cracked password: admin:thx1138
Weak password. No rate limiting. No MFA. They got in.
Web Shell Upload
Now they’re in the admin panel. What’s the first thing attackers do? Upload a backdoor.
Found requests to /admin/upload.php?action=upload
Checked the request body — they uploaded easy-simple-php-webshell.php with a flag embedded in it.
Flag in upload: THM{ecb012e53a58818cbd17a924769ec447}
What’s a web shell?
Simple PHP script that lets you run commands:
Access it like:
http://target.com/uploads/shell.php?cmd=whoami
Now they have command execution on the server.
First Command
Filtered for requests to the web shell file, sorted by time.
First command they ran: whoami
KQL Query:
transaction.remote_address: "10.0.2.15"
AND http.url: *easy-simple-php-webshell.php*
Standard first command. Confirms the shell works, shows what user they’re running as (probably www-data or apache).
First command: whoami
Stealing Database Credentials
Now they need database access. How do they get creds?
Looked for Local File Inclusion (LFI) attempts — when attackers try to read files they shouldn’t access.
KQL Query:
transaction.remote_address: "10.0.2.15"
AND http.url: *file=* OR http.url: *../*
Found:
/view.php?file=../../../../../../../etc/phpmyadmin/config-db.php
They used path traversal to read the phpMyAdmin config file, which contains database credentials.
File they stole: /etc/phpmyadmin/config-db.php
Database manager: phpmyadmin
Database Access and Exfiltration
With stolen database creds, they logged into phpMyAdmin at /phpmyadmin/
Looked for database export activity:
KQL Query:
transaction.remote_address: "10.0.2.15"
AND http.url: "/phpmyadmin/export.php"
Database they exported: customer_credit_cards
Yeah. They stole the customer credit card database.
Covering Tracks (Sort Of)
They also imported data back into the database:
KQL Query:
transaction.remote_address: "10.0.2.15"
AND http.url: "/phpmyadmin/import.php"
Checked the request body — they inserted a row with a flag:
INSERT INTO customers VALUES ('Hacker', 'c6aa3215a7d519eeb40a660f3b76e64c');Flag inserted: c6aa3215a7d519eeb40a660f3b76e64c
Leaving their calling card.
The Complete Timeline
July 26, 2023 — Approximately 50 minutes start to finish:
- 10:15 AM — Nmap scan
- 10:20 AM — Gobuster directory brute force (1,867 requests)
- 10:25 AM — Found /admin-login.php
- 10:30 AM — Hydra password brute force
- 10:35 AM — Logged in with admin:thx1138
- 10:40 AM — Uploaded web shell
- 10:45 AM — Executed whoami command
- 10:50 AM — LFI to steal database config
- 10:55 AM — Accessed phpMyAdmin
- 11:00 AM — Exported customer credit card database
- 11:05 AM — Inserted flag into database
Total time: ~50 minutes from scan to data breach.
What I Actually Learned
Attackers are stupidly obvious when you know what to look for.
Every step left logs:
- Nmap = weird User-Agent
- Gobuster = 1,867 failed requests in minutes
- Hydra = multiple login attempts
- Web shell = suspicious file upload
- LFI = path traversal patterns
- Database dump = large export request
This was a textbook attack:
Recon → Enumeration → Brute Force → Shell Upload → Credential Theft → Data Exfiltration
What could’ve stopped them at each stage:
- IDS alert on Nmap User-Agent
- Rate limiting (1,867 requests should’ve triggered something)
- Strong password + MFA
- File upload validation
- Input sanitization (prevent LFI)
- Separate database credentials per component
- DLP on large database exports
They only needed one weak link. They got seven.
The Queries That Mattered
Find attacker IP:
Click source.ip field → View top values
Timeline reconstruction:
transaction.remote_address: "10.0.2.15"
| sort @timestamp
Failed vs successful requests:
transaction.remote_address: "10.0.2.15" AND response.status: 404
transaction.remote_address: "10.0.2.15" AND response.status: 200
Tool identification:
transaction.remote_address: "10.0.2.15"
| View request.headers.User-Agent field
LFI detection:
http.url: *file=* OR http.url: *../*
Database activity:
http.url: */phpmyadmin/export.php*
http.url: */phpmyadmin/import.php*
Real-World Response
If I saw this in production:
Immediate (first 10 minutes):
- Block 10.0.2.15 at firewall
- Disable admin account
- Take web server offline
- Alert management (data breach = legal notification required)
Investigation (first hour):
- Confirm what data was accessed
- Check for other compromised servers
- Hunt for other attacker IPs
Cleanup (first 4 hours):
- Remove web shell
- Patch LFI vulnerability
- Reset all admin passwords
- Rotate database credentials
- Check for other backdoors
Long-term:
- Implement MFA
- Deploy WAF
- Enable rate limiting
- Set up file integrity monitoring
- Notify affected customers
Final Thoughts
This attack was completely preventable. Any one of these would’ve stopped or slowed them:
- MFA on admin login
- Rate limiting on login attempts
- File upload validation
- Input sanitization
- Proper database credential management
Defense in depth matters. You don’t need perfect security — just enough layers that the attacker gives up or gets detected.
The logs told the complete story. I just had to read them.
Tags: #TryHackMe #ELK #Kibana #ThreatHunting #WebSecurity #IncidentResponse
Every attack leaves breadcrumbs. Your job is to follow them.