DEV Community

Cover image for Day 4 — I Built a Program That Caught a Reverse Shell
Hafiz Shamnad
Hafiz Shamnad

Posted on

Day 4 — I Built a Program That Caught a Reverse Shell

Until yesterday, my scripts were observers.
They scanned, parsed, and reported.

Today my computer started watching back.

Not scanning a target.
Not running Nmap.

Watching itself.

And that’s actually what most real cybersecurity tools do.


The Realization

I had a simple thought:

Malware rarely announces itself.
But it always has to talk to its operator.

An attacker who compromises a machine needs communication.
That communication is called a Command & Control (C2) callback.

Reverse shells
Botnets
RATs
Backdoors

They all share one unavoidable behavior:

They create an outbound network connection.

So instead of scanning attackers, I tried something different.

I decided to monitor my own machine’s network connections in real time.

Essentially, I attempted to build a tiny Host Intrusion Detection System (HIDS) using Python.


The Core Idea

If an unknown process suddenly connects to an external IP or a suspicious port, that is not normal behavior.

For example:

  • A browser connecting to port 443 → normal
  • A PDF reader connecting to port 4444 → very suspicious

So the job of the program became:

  1. Watch every active connection
  2. Identify the process that opened it
  3. Check destination IP and port
  4. Alert if it looks malicious

Technology Used

I used the Python library:

psutil

This library allows Python to see operating system internals:

  • processes
  • memory
  • CPU
  • open ports
  • network sockets

Basically, Python gained system-level visibility.


The Program

The script continuously inspects all TCP/UDP connections and correlates them with running processes.

import psutil
import time
import socket
import logging

logging.basicConfig(
    filename="connection_alerts.log",
    level=logging.WARNING,
    format="%(asctime)s - %(message)s"
)

SUSPICIOUS_PORTS = {4444, 5555, 6666, 1337, 9001, 12345}

seen_connections = set()
connection_times = {}
ALERT_TIMEOUT = 60

def resolve_ip(ip):
    try:
        return socket.gethostbyaddr(ip)[0]
    except:
        return ip

print("Starting Network Monitoring")

while True:
    for conn in psutil.net_connections(kind='inet'):

        if conn.status != "ESTABLISHED":
            continue

        if not conn.raddr:
            continue

        remote_ip = conn.raddr.ip
        remote_port = conn.raddr.port

        if conn.pid:
            try:
                pname = psutil.Process(conn.pid).name()
            except:
                pname = "UnknownProcess"
        else:
            pname = "Kernel/Hidden"

        connection_id = f"{pname}-{remote_ip}-{remote_port}"
        current_time = time.time()

        # cleanup old alerts
        for cid in list(connection_times):
            if current_time - connection_times[cid] > ALERT_TIMEOUT:
                seen_connections.discard(cid)
                del connection_times[cid]

        if remote_port in SUSPICIOUS_PORTS:
            if connection_id not in seen_connections:
                seen_connections.add(connection_id)
                connection_times[connection_id] = current_time
                msg = f"[ALERT] Suspicious port connection! {pname} -> {resolve_ip(remote_ip)}:{remote_port}"
                print(msg)
                logging.warning(msg)

    time.sleep(0.2)
Enter fullscreen mode Exit fullscreen mode

The Problem I Faced (Important Lesson)

At first, nothing worked.

I tested using:

ping google.com
Enter fullscreen mode Exit fullscreen mode

No alerts.

I thought the script was broken.

It wasn’t.

I learned something important:

Ping uses ICMP, not TCP.

My detector only watches socket connections.
ICMP has no ports and no socket, so it is invisible to this type of monitoring.

Then I tried curl.

Still nothing.

The real reason?

Connections open and close in milliseconds.
My script was checking every 5 seconds.

By the time Python looked, the connection already disappeared.

This was my first exposure to a real security engineering concept:

Polling is unreliable for security monitoring.

Real EDR products use kernel-level event monitoring for exactly this reason.


The Breakthrough

I simulated an attacker using netcat.

Terminal 1:

nc -lvnp 4444
Enter fullscreen mode Exit fullscreen mode

Terminal 2:

nc 127.0.0.1 4444
Enter fullscreen mode Exit fullscreen mode

And immediately:

[ALERT] Suspicious port connection! nc -> 127.0.0.1:4444
Enter fullscreen mode Exit fullscreen mode

My program detected a reverse shell.

At that moment I understood:

I wasn’t writing a script anymore.

I had created behavior-based detection.


Another Real Problem: Alert Flooding

The program printed the same alert dozens of times per second.

Because the connection stayed open.

This is actually a real SOC issue called:

Alert Fatigue

So I implemented alert deduplication and timeout logic to report each incident only once.

Now the system behaves like a real monitoring tool instead of a spam generator.


What This Taught Me

This single exercise taught multiple cybersecurity concepts:

  • Difference between ICMP and TCP monitoring
  • Process to network correlation
  • Reverse shell detection
  • Why EDR tools need kernel visibility
  • Why SIEM systems require alert suppression

Most importantly:

Signature-based detection looks for known malware.

Behavior-based detection looks for suspicious activity.

Attackers can change code easily.
They cannot operate without communicating.


Final Thought

I started the day trying to monitor connections.

I ended the day understanding how modern endpoint protection actually works.

Instead of asking:

“Is this file malicious?”

The better question is:

“Is this behavior normal?”

Tomorrow I plan to monitor file modifications and detect ransomware-like activity.

Because if malware needs communication…

Ransomware needs to touch files.

Top comments (0)