TL;DR I spend my evenings on Hack The Box and it’s made me better at my day job, which is platform engineering and has nothing to do with pentesting. The first ten minutes on a CTF box and the first ten minutes on a broken production server are the same loop, with “flag” swapped for “incident”. Below is the toolkit that loop runs on, from one host up to a Kubernetes pod, with the CTF use and the work use side by side for every command.
A production server is misbehaving. You SSH in. Now what?
The people who stay calm here already know the next ten commands, because they’ve run this exact loop a hundred times before. On their own time, with the word “flag” where “incident” is now.
I spend my evenings on Hack The Box and the odd CTF competition. I’ve no plans to become a pentester. I like platform engineering and I’m staying in it. I keep at the challenges because the skills carry straight into the day job, and the carryover is bigger than I expected when I started.
Pwning a web app looks nothing like running a Kubernetes cluster. The first ten minutes are identical, though. You’re inside a system you don’t fully understand and you have to work out what it’s doing, fast. That’s understanding over trust, the thing I care about most when something breaks, and a CTF lets you practice it while the only thing at stake is your evening.
Here’s what carries over, starting on a single host and ending inside a pod.
The same four questions
Drop into a CTF box and the first questions are always the same:
- What’s running?
- What’s vulnerable?
- What can I access?
- How do things connect?
Swap “vulnerable” for “broken” and that’s incident response. Same questions when you SSH into a server that’s misbehaving or a pod that keeps crashing, and the same questions when you’re working out what an attacker touched.
The difference is reps. A CTF player does this every week, so the enumeration stops being a checklist and becomes a reflex. Once it’s a reflex you stop guessing under pressure and start gathering. The rest of this post is the toolkit that reflex runs on, from the filesystem up to the container.
Filesystem
Recently modified files
On a CTF box this finds the planted backdoor or the config an attacker edited. At work it finds what changed right before the system broke, and most of the time that’s plain config drift.
# Files modified in the last 24 hours
find / -type f -mtime -1 2>/dev/null
# Files modified in the last 10 minutes
find / -type f -mmin -10 2>/dev/null
# Files changed after a specific date
find / -type f -newermt "2026-02-01" 2>/dev/null
# Find files by size (useful for finding log bombs or large dumps)
find / -type f -size +100M 2>/dev/null
A deployment fails and the app won’t start. Which config changed? find /etc -type f -mmin -30 shows someone edited the wrong file 15 minutes ago.
Files by content
On a box you’re grepping for passwords, keys and flags hidden in files. At work you’re grepping for where a config value gets set, or for the hardcoded credential that shouldn’t be there.
# Find files containing a string
grep -r "password" /etc 2>/dev/null
grep -r "API_KEY" /app 2>/dev/null
# Find files containing a pattern (like IP addresses)
grep -rE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /etc 2>/dev/null
# Find files with specific permissions (world-writable)
find / -perm -002 -type f 2>/dev/null
# Find SUID binaries (privilege escalation, but also config issues)
find / -perm -4000 -type f 2>/dev/null
“Where is that environment variable being set?” grep -r "DATABASE_URL" /etc /app ~/.* 2>/dev/null finds it in three places, with three different values.
File metadata
In a CTF the metadata is where you find the hidden file, and the one somebody tampered with. At work the same commands explain the permission problem you’d otherwise sit staring at.
# Extended attributes
lsattr /etc/passwd
getfacl /var/log/secure
# File type and magic bytes
file /usr/bin/mystery-binary
file /var/log/weird.log
# Detailed stat info
stat /etc/shadow
# Immutable files (can't be modified even by root)
lsattr -a /etc/ | grep "\-i\-"
“Why can’t I delete this file, even as root?” lsattr shows the immutable flag is set.
Processes
What’s running and why
On a box you’re looking for the malicious process, the reverse shell, the thing eating the CPU. At work you’re looking for the runaway process, and for what the application is really doing.
# Detailed process tree
ps auxf
pstree -p
# What's using the most CPU/memory right now
top -bn1 | head -20
ps aux --sort=-%mem | head -10
ps aux --sort=-%cpu | head -10
# Process by name with full command line
ps aux | grep -E "[n]ginx|[a]pache"
# What process is using a specific port
lsof -i :8080
ss -tlnp | grep 8080
netstat -tlnp | grep 8080
# What files does a process have open
lsof -p <pid>
ls -la /proc/<pid>/fd/
# Process environment variables
cat /proc/<pid>/environ | tr '\0' '\n'
# Process working directory
ls -la /proc/<pid>/cwd
A Java app is eating 100% CPU. ps aux --sort=-%cpu gives you the PID. /proc/<pid>/fd/ shows 50,000 open file handles, so it’s a connection leak.
Tracing what a process does
In a CTF you trace a binary to learn what it does and where the hidden functionality is. At work you trace the app that says it can’t connect, to see which syscall is failing and on what.
# System calls made by a process
strace -p <pid>
strace -f -e trace=network <command> # only network calls
strace -f -e trace=file <command> # only file operations
# Library calls
ltrace -p <pid>
# Trace a specific command
strace -o output.txt -f ./mystery-app
# See what a process is doing in real-time
watch -n 1 "cat /proc/<pid>/io"
The app says “connection refused” but the service is up. strace -e connect shows it’s connecting to the wrong IP, because an environment variable is set wrong.
Network
Connections
On a box you’re hunting C2 connections and exfiltration channels, and looking for somewhere to pivot to. At work you’re debugging why two services can’t reach each other, and now and then finding a connection that shouldn’t be there at all.
# All network connections
ss -tunapl
netstat -tunapl
# Established connections only
ss -tnp state established
# Connections by process
ss -tnp | grep <pid>
# Listening services
ss -tlnp
netstat -tlnp
# Connections to a specific host
ss -tn dst 10.0.0.50
# DNS resolution trace
dig +trace example.com
host -v example.com
nslookup -debug example.com
The application times out on the database. ss -tn shows the connection stuck in SYN_SENT, which points at a firewall or a wrong route.
Packet capture
In a CTF you capture traffic for credentials and for data hidden in a protocol. At work you capture it to see what’s on the wire when the application’s own logs don’t say, and to check that TLS is doing what you think it’s doing.
# Capture traffic on an interface
tcpdump -i eth0 -w capture.pcap
# Capture specific traffic
tcpdump -i eth0 port 443
tcpdump -i eth0 host 10.0.0.50
tcpdump -i eth0 'port 80 and host 10.0.0.50'
# Read capture with full packet content
tcpdump -r capture.pcap -A
tcpdump -r capture.pcap -X
# Quick HTTP debugging
tcpdump -i eth0 -A port 80 | grep -E "GET|POST|Host:"
# Check TLS handshake
openssl s_client -connect example.com:443 -servername example.com
HTTPS works from one server and fails from another. openssl s_client shows the failing server doesn’t trust the CA, because the root cert is missing.
Logs
Finding the pattern
Attack traces, and the one anomaly in a week of noise. That’s the CTF side. At work it’s the same work with a different question, which is why did this break and when did it start.
# Follow logs in real-time
tail -f /var/log/syslog
journalctl -f -u myservice
# Logs since specific time
journalctl --since "2026-02-01 10:00:00"
journalctl --since "1 hour ago"
# Logs for specific service
journalctl -u nginx -n 100
# Filter by priority
journalctl -p err # errors only
journalctl -p warning..err # warnings and errors
# Count occurrences of patterns
grep -c "error" /var/log/app.log
awk '/error/ {count++} END {print count}' /var/log/app.log
# Extract timestamps and group by minute
awk '{print $1, $2, $3}' /var/log/app.log | cut -d: -f1,2 | uniq -c | sort -rn
# Find errors with context
grep -B5 -A5 "FATAL" /var/log/app.log
Intermittent 500 errors. grep -c "500" access.log per hour shows a spike at :00 every hour, and that’s a cron job.
Correlating across services
On a box you’re rebuilding the attacker’s timeline and following their lateral movement. At work you’re rebuilding the incident timeline across services, and cause and effect rarely sit in the same log file.
# Auth failures
grep "Failed password" /var/log/auth.log | awk '{print $1,$2,$3,$9,$11}' | sort | uniq -c
# Commands run by users (if auditd is enabled)
ausearch -k commands -i
# Correlate by timestamp across files
grep "2026-02-27T10:15" /var/log/*.log
# Parse structured logs (JSON)
cat app.log | jq 'select(.level == "ERROR")'
cat app.log | jq -r '[.timestamp, .message] | @tsv'
Users and authentication
In a CTF the compromised account is the way in, so you map who can do what. At work that same map is the access audit, the permission bug and the security review.
# Users with login shells
cat /etc/passwd | grep -v nologin | grep -v /bin/false
# Users with UID 0 (root equivalent)
awk -F: '$3 == 0 {print $1}' /etc/passwd
# Last logins
last -n 20
lastlog
# Failed login attempts
lastb -n 20
faillog -a
# Who is logged in now
w
who
# User's groups and sudo rights
id username
sudo -l -U username
# SSH authorized keys
cat ~/.ssh/authorized_keys
find / -name "authorized_keys" 2>/dev/null
You suspect someone got in who shouldn’t have. last shows a login from an IP you don’t know. grep "Accepted" /var/log/auth.log shows they came in with an SSH key rather than a password.
Disk
On a box, disk forensics is recovering deleted files and reading the layout. At work it’s the disk that’s full when du says it isn’t.
# Disk usage by directory
du -sh /* 2>/dev/null | sort -h
du -sh /var/* 2>/dev/null | sort -h
# Find what's using space
ncdu / # interactive
# Find large files
find / -type f -size +1G 2>/dev/null
# Deleted files still open (holding disk space)
lsof +L1
# Inode usage
df -i
# What's mounted
mount | column -t
cat /proc/mounts
findmnt
Disk 100% full and du says 50% used. lsof +L1 shows deleted log files still held open by a process, and restarting that process gives the space back.
Containers
The CTF version is the container escape, and the misconfiguration that makes it possible. The work version is the container that won’t start, and knowing what state it’s in.
# What's in a running container
docker exec -it <container> /bin/sh
kubectl exec -it <pod> -- /bin/sh
# Container processes from host
docker top <container>
ps aux | grep containerd
# Container resource usage
docker stats
kubectl top pods
# Container logs
docker logs -f <container>
kubectl logs -f <pod>
# Inspect container config
docker inspect <container>
docker inspect <container> | jq '.[0].Config.Env'
kubectl describe pod <pod>
# Container filesystem
docker diff <container> # what changed
docker export <container> | tar -tf - # list files
# Check capabilities
docker inspect <container> | jq '.[0].HostConfig.CapAdd'
kubectl auth can-i --list
A container crashes on startup and docker logs shows nothing. docker inspect shows the wrong entrypoint, set by an image override.
What shows up at work
After months of evenings on this, the habits turn up at work without me deciding to use them. I enumerate before I touch anything, because fixing the wrong thing under pressure is how a single outage turns into two. I read the error message instead of retrying and hoping, and the answer is usually in it.
I trace a problem from the application down to the syscall and out to the network, the same path I’d trace from a web app to root on a box. I notice what’s misconfigured before it bites, because a CTF trains you to look for exactly that. And grep, find, awk and sed have stopped being things I look up.
All of that is reps, and a CTF is the cheapest place I know to get them without taking a security job.
Where to start
If you want the same toolkit, start in the shallow end:
- OverTheWire Bandit is pure Linux command line, zero setup. Do this first.
- TryHackMe has guided rooms with hints, a gentler on-ramp.
- Hack The Box after that. Start with the easy boxes and stick to the Linux machines.
- PicoCTF has the good forensics challenges, for when you want to go deeper.
An evening a week is enough. I’ve been at it for months and I’m still not a pentester. I do know the next ten commands when I SSH into a broken server.
