Search code examples
phpserializationevaltransmission

How to create a payload from a anonymus function


I wanted to send a function using some sort of transmission to another script.

For this I needed to pack this function in an PHP evaluable payload (evalueate it as a string in another PHP file may be on another server).

$abc = "testABC";
$xyz = new TestClass();
$test = true;
$x = function () use ($test, $xyz, $abc) {
    echo $abc;
    var_dump($test, $xyz);
};

This function will be packed into a string like this:

$payload = function () {
    $test = unserialize('b:1;');
    $xyz = unserialize('O:9:"TestClass":0:{}');
    $abc = unserialize('s:7:"testABC";');
    echo $abc;
    var_dump($test, $xyz);
};

Solution

  • To get this done, I wrote following function which pulls out the functions code, matches any use(...) clauses and renames the variable it is called. In the end, it'll assign the use(...) variables as a unserializeable string.

    function packAnonFunction($payload, ...$args) {
        $func = new ReflectionFunction($payload);
        $filename = $func->getFileName();
        $start_line = $func->getStartLine() - 1;
        $end_line = $func->getEndLine();
        $length = $end_line - $start_line;
    
        $source = file($filename);
        $body = implode("", array_slice($source, $start_line, $length));
        $body = preg_replace('/(\$[a-z]+)\ \=\ function/', '\\$payload = function', $body);
        if(preg_match('/use\s\((\$[a-zA-Z0-9]+(?:,\s\$[a-zA-Z0-9]+)*)\)/', $body, $matches)) {
            $vars = $matches[1];
            if(strpos($vars, ', ') !== false) {
                $parts = explode(', ', $vars);
            } else {
                $parts = [$vars];
            }
            $return = [];
            foreach($parts as $key => $variable) {
                $return[$variable] = $args[$key];
            }
            $variableString = "";
            foreach($return as $var => $value) {
                $value = serialize($value);
                $variableString .= "\t{$var} = unserialize('{$value}');\n";
            }
    
            $body = str_replace(" use (" . $vars . ")", "", $body);
            $body = str_replace("{\n", "{\n" . $variableString, $body);
        }
        return $body;
    }
    

    You could just use it like this:

    $abc = "testABC";
    $xyz = new TestClass();
    $test = true;
    $x = function () use ($test, $xyz, $abc) {
        echo $abc;
        var_dump($test, $xyz);
    };
    
    echo packAnonFunction($x, $test, $xyz, $abc);
    

    The only lag I wasn't to get around is, that you'll have to put the args ($test, $xyz, $abc) in the same order as you assigned the use(...) statement.

    See it in operation: https://3v4l.org/89pXm

    So what do you think about it?