<?php

namespace PHPGGC;

/**
 * Class handling the generation of a gadget chain specific to a PHP CMS,
 * framework, or library.
 * The class will automatically include the chain.php file present in the same
 * directory.
 *
 * Upon calling generate(), this object produces a gadget chain object.
 * The object can then be serialize()d into a serialized string.
 * Calling unserialize() on the string, in the right environment, should produce
 * an action: execute a PHP function, write a file, delete a file, etc.
 *
 * Depending on the type of the chain, some parameters must be given. For
 * instance, an RCE Gadget Chain generally requires the name of a function to
 * execute, along with its parameter(s).
 * A file write gadget chain requires the path of the remote file we wish to
 * write, and the path of a local file whose content is to be written.
 *
 * Along with the generate() method, which converts parameters into an object,
 * three generic methods are available:
 * - process_parameters($parameters)
 * - process_object($object)
 * - process_serialized($serialized)
 *
 * Those methods are to be found in other PHPGGC classes, for instance the main
 * class for handling CLI, PHPGGC. Refer to their documentation to understand
 * their usage.
 */
abstract class GadgetChain
{
    public $name;
    public static $type;
    public static $version = '?';
    # Vector to start the chain: __destruct, __toString, offsetGet, etc.
    public static $vector = '';
    public static $author = '';
    public static $parameters = [];
    public static $information;

    # Types
    const TYPE_RCE = 'RCE';
    const TYPE_RCE_FUNCTIONCALL = 'RCE (Function call)';
    const TYPE_RCE_PHPCODE = 'RCE (PHP code)';
    const TYPE_RCE_COMMAND = 'RCE (Command)';
    const TYPE_CMD = 'CMD';
    const TYPE_SSRF = 'SSRF';
    const TYPE_FR = 'File read';
    const TYPE_FW = 'File write';
    const TYPE_FD = 'File delete';
    const TYPE_SQLI = 'SQL injection';
    const TYPE_INFO = 'phpinfo()';

    function __construct()
    {
        $this->load_gadgets();
    }

    protected function load_gadgets()
    {
        $directory = dirname((new \ReflectionClass($this))->getFileName());
        require_once $directory . '/gadgets.php';
    }

    /**
     * Generates the gadget chain object from given parameters.
     * Parameters are expected to have been processed before.
     *
     * @param array $parameters Gadget chain parameters
     * @return Object
     */
    abstract public function generate(array $parameters);

    /**
     * Modifies given parameters if required.
     * Called before `generate()`.
     * This is called on the gadget chain's parameters, such as for instance
     * "remote_file" and "local_file" for a file write chain.
     *
     * @param array $parameters Gadget chain parameters
     * @return array Modified parameters
     */
    public function process_parameters(array $parameters)
    {
        return $parameters;
    }

    /**
     * Modifies the object generated by this class if required.
     * Called after the object has been generated using `generate()`, and before
     * `serialize()`.
     *
     * One of the main usages is to convert given object into something that can
     * be processed by the targeted system.
     * @param Object $parameters Gadget chain object
     * @return Object Modified object
     */
    public function process_object($object)
    {
        return $object;
    }

    /**
     * Modifies given serialized string if required.
     * Called after `serialize()`.
     * For instance, if a class is meant to be named A\B\C but has been named
     * A_B_C in the gadget for convenience, it can be str_replace()d here.
     *
     * @param string $serialized Serialized string representing the gadget chain
     * @return string Modified serialized string
     */
    public function process_serialized($serialized)
    {
        return $serialized;
    }

    /**
     * Returns a string describing the gadget chain.
     */
    public function __toString()
    {
        $infos = [
            'Name' => static::get_name(),
            'Version' => static::$version,
            'Type' => static::$type,
            'Vector' => static::$vector
        ];

        $strings = [];

        if(static::$information)
        {
            $information = trim(static::$information);
            $information = preg_replace("#\n\s+#", "\n", $information);
            $infos['Informations'] = "\n" . $information;
        }

        foreach($infos as $k => $v)
        {
            $strings[] = str_pad($k, 15) . ': ' . $v;
        }

        return implode("\n", $strings);
    }

    /**
     * Returns a standard name for the gadget chain, generally of the form
     * <Framework>/<Type><N>, for instance Guzzle/RCE1.
     */
    public static function get_name()
    {
        $class = static::class;
        $class = substr($class, strpos($class, '\\') + 1);
        $class = str_replace('\\', '/', $class);
        return $class;
    }
}
