PHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])


In the situation where you can create a new arbitrary object like new $_GET["a"]($_GET["a"])you might be able to obtain RCE, and this writeup exposes different ways to get RCE.

RCE via Custom Classes or Autoloading

In the construction new $a($b), the variable $a stands for the class name that the object will be created for, and the variable $b stands for the first argument that will be passed to the object’s constructor.
If $a and $b come from GET/POST, they can be strings or string arrays. If they come from JSON or elsewhere, they might have other types, such as object or boolean.
Let’s consider the following example:
class App {
function __construct ($cmd) {
# Additionally, in PHP < 8.0 a constructor might be defined using the name of the class
class App2 {
function App2 ($cmd) {
# Vulnerable code
$a = $_GET['a'];
$b = $_GET['b'];
new $a($b);
In this code, you can set $a to App or App2 and $b to uname -a. After this, the command uname -a will be executed.
When there are no such exploitable classes in your application, or you have the class needed in a separate file that isn’t included by the vulnerable code, you may take a look at autoloading functions.
Autoloading functions are set by registering callbacks via spl_autoload_register or by defining __autoload. They are called when an instance of an unknown class is trying to be created.
# An example of an autoloading function
spl_autoload_register(function ($class_name) {
include './../classes/' . $class_name . '.php';
# An example of an autoloading function, works only in PHP < 8.0
function __autoload($class_name) {
include $class_name . '.php';
# Calling spl_autoload_register with no arguments enables the default autoloading function, which includes lowercase($classname) + .php/.inc from include_path
Depending on the PHP version, and the code in the autoloading functions, some ways to get a Remote Code Execution via autoloading might exist.

RCE via Built-In Classes

When you don’t have custom classes and autoloading, you can rely on built-in PHP classes only.
There are from 100 to 200 built-in PHP classes. The number of them depends on the PHP version and the extensions installed. All of built-in classes can be listed via the get_declared_classes function, together with the custom classes:
Classes with useful constructors can be found via the reflection API.
Displaying constructors and their parameters using the reflation API:\
If you control multiple constructor parameters and can call arbitrary methods afterwards, there are many ways to get a Remote Code Execution. But if you can pass only one parameter and don’t have any calls to the created object, there is almost nothing.
I know of only three ways to get something from new $a($b).

SSRF + Phar deserialization

The SplFileObject class implements a constructor that allows connection to any local or remote URL:
new SplFileObject('');
This allows SSRF. Additionally, SSRFs in PHP < 8.0 could be turned into deserializations via techniques with the Phar protocol.

Exploiting PDOs

The PDO class has another interesting constructor:
new PDO("sqlite:/tmp/test.txt")
The PDO constructor accepts DSN strings, allowing us to connect to any local or remote database using installed database extensions. For example, the SQLite extension can create empty files.

SoapClient/SimpleXMLElement XXE

In PHP ≤ 5.3.22 and ≤ 5.4.12, the constructor of SoapClient was vulnerable to XXE. The constructor of SimpleXMLElement was vulnerable to XXE as well, but it required libxml2 < 2.9.

RCE via Imagick Extension

Checking the dependencies of the project you are trying to exploit you could find new classes that could be abused to execute commands creating a new object. In this case, Imagick was found to be useful for that purpose.

VID parser

The VID parser allows to write arbitrary content in an arbitrary path inside the filesystem, which would allow an attacker to write a PHPshell in an accessible folder from the web page and get RCE.

VID Parser + FIle Upload

When a file is uploaded to PHP it's temporary stored in /tmp/phpXXXXXX . The VID parser of Imagick with the msl protocol allows to specify wildcards in the file paths (so the temporary uploaded file can be easily accessed) and copy it to any arbitrary location. This is another way to get arbitrary file writing inside the filesystem:

PHP Crash + Brute Force

The original writeup explained another way to get RCE by uploading files with specific content and making the server crash before it deletes that file and then bruteforcing the name of the temporary file until Imagick executes arbitrary PHP code.
However, apparently the crash trick discovered only worked in an old version of ImageMagick.