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:

  1. 10:15 AM — Nmap scan
  2. 10:20 AM — Gobuster directory brute force (1,867 requests)
  3. 10:25 AM — Found /admin-login.php
  4. 10:30 AM — Hydra password brute force
  5. 10:35 AM — Logged in with admin:thx1138
  6. 10:40 AM — Uploaded web shell
  7. 10:45 AM — Executed whoami command
  8. 10:50 AM — LFI to steal database config
  9. 10:55 AM — Accessed phpMyAdmin
  10. 11:00 AM — Exported customer credit card database
  11. 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.