Website:

This site looks great, but appears to only have functionality to submit an email address to subscribe to a newsletter.

Untitled

Messing with the email filed a little shows that we must submit a valid email also. Even posting through burpsuite, the email is validated, making any XSS or SQLI attempt very difficult.

Untitled

Source Code:

The following function from Database.php handles the submitting of the email into the database, along with an IP Address.

public function subscribeUser($ip_address, $email)
    {
        return $this->db->exec("INSERT INTO subscribers (ip_address, email) VALUES('$ip_address', '$email')");
    }

The IP address is passed into this function, and this comes from the SubscriberModel.php file.

<?php
class SubscriberModel extends Model
{

    public function __construct()
    {
        parent::__construct();
    }

    public function getSubscriberIP(){
        if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){
            return  $_SERVER["HTTP_X_FORWARDED_FOR"];
        }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
            return $_SERVER["REMOTE_ADDR"];
        }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
            return $_SERVER["HTTP_CLIENT_IP"];
        }
        return '';
    }

    public function subscribe($email)
    {
        $ip_address = $this->getSubscriberIP();
        return $this->database->subscribeUser($ip_address, $email);
    }
}

The IP address is fetched here by getSubsriberIP. What is interesting, is that the order of the checks for IP are in an order which allow injection of an IP address as the HTTP Header X-Forwarded-For.

The website does not make use of prepared statements when parsing user input into an SQL query, which introudces an SQLI vulnerability.

In order to abuse this, we substitute the value of RANDOMIP', 'test'); <SQL QUERY HERE> -- - as the IP address in the below statement, which escapes the query, and will instead use our query:

INSERT INTO subscribers (ip_address, email) VALUES('$ip_address', '$email')

This SQLI is blind, and the flag is put in a file with a random name, making simply reading the flag and brute forcing it impossible.

The backend database is SQLite, which also limits us in writing to files, reading files, or attempting operating system commands.

However, it is entirely possible if we can execute stacked queries, which has to be enabled.

We attempt to write a web shell to the application root by creating a new database file stored in this location, and inserting a PHP web shell into a row within the table. This works, and we now have a webshell at http://challenge/shell.php.