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

Adding Symfony 1.0 to 1.5 RCE gadget chains #182

Merged
merged 4 commits into from
Mar 13, 2024

Conversation

darkpills
Copy link
Contributor

I created 2 gadget chains for all versions of Symfony 1 including the 1.5 fork maintained here https://github.com/FriendsOfSymfony1/symfony1

Symfony1 is not maintained any more. Sensiolabs has been contacted but they won't provide a fix since Symfony1 is end of life.

Symfony1.5.13 and greater are still partially vulnerable depending on how you install it, but no maintainers are responding.

@cfreal
Copy link
Collaborator

cfreal commented Feb 26, 2024

Hello,

Thanks! I like the usage of process_serialized(). Can you tell me how you created your test environment, because although the library is present, Swift classes don't seem to be included by default. I tried downloading the instance from Symfony's website, and using composer.

Charles

@darkpills
Copy link
Contributor Author

darkpills commented Feb 26, 2024

Hello !

Thank you for your tests!

Current version of Symfony 1.5 is partially vulnerable. All depend how you install it:

  • If you install Symfony 1.5 with git method described in the git README, Swift Mailer code is downloaded through a git sub-module. Sub-module definition targets branch 5.x of Swift Mailer which contains the vulnerability.
  • If you install Symfony 1.5 with composer, before 1.5.13, you will install branch 5.x also of Swift Mailer. But if you install after 1.5.13 and via the master branch, you will end-up installing last version of Swift Mailer 6.x that is not vulnerable anymore.

@darkpills
Copy link
Contributor Author

Here are my notes if it can help about installing 1.5:

With composer

Install composer:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e21205b207c3ff031906575712edab6f13eb0b361f2085f1f1237b7126d785e826a450292b6cfd1d64d92e6563bbde02') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

And then install Symfony with composer:

php composer.phar require friendsofsymfony1/symfony1 "1.5.*"
php composer.phar install

If you launch install command like in 1.4 you will get a fatal error due to Doctrine components not found. You can either add Doctrine with composer and declare sf_doctrine_path in your project configuration file, or like me since we don't need an ORM, disable Doctrine during installation. Edit vendor/friendsofsymfony1/symfony1/lib/plugins/sfDoctrinePlugin/config/installer.php:

$this->installDir(dirname(__FILE__).'/skeleton');
//$this->enablePlugin('sfDoctrinePlugin');
$this->reloadTasks();

And start creating a project, an application and a module:

php ./vendor/symfony/data/bin/symfony generate:project test
php symfony generate:app frontend
php symfony generate:module frontend test

Several files will be created, among them: web/index.php and web/frontend_dev.php for respectively production and development configuration of the frontend application.

Then start a webserver with PHP installed and make the root point to web directory (renamed public in version 2.x and higher).

Fix the possible permission issues.

Open a web browser to http://localhost/frontend_dev.php.

With git

Create a git repo:

git init .
git submodule add https://github.com/FriendsOfSymfony1/symfony1.git lib/vendor/symfony
git submodule update --init --recursive

If you try to generate a project, you will get an error due to PHP7.1+ new syntax to declare visibility of constants. However, this syntax is not compatible with PHP5. Here is a snipet to fix it:

find . -name "*.php" -type f -exec grep -l "public const" '{}' \; | noglob xargs -I '{}' sed -i -e s#"public const"#"const"# '{}'

Then, the rest is the same:

php ./vendor/bin/symfony generate:project test
php symfony generate:app frontend
php symfony generate:module frontend test

@cfreal
Copy link
Collaborator

cfreal commented Feb 27, 2024

Hello,

Symfony/RCE12

Thanks for the detailled information. I was able to pull it off with the GIT method.

Symfony/RCE13

Despite a successful installation, when I unserialized, I get the dreaded __PHP_Incomplete_Class for every lime_* object. The lime classes you use in the gadget are NOT in scope when I run the exploit from the symfony website, because lime.php is never included in the web part of the framework, or present in the autoloader.

For the sake of precision: I installed everything using the official method described here. I then put the unserialize() code in sfContext::getController() to make sure that everything has been loaded. I get (stripped for brevity):

object(__PHP_Incomplete_Class)#26 (10) {
  ["__PHP_Incomplete_Class_Name"]=>
  string(9) "lime_test"
  ["output":protected]=>
  object(__PHP_Incomplete_Class)#27 (2) {
    ["__PHP_Incomplete_Class_Name"]=>
    string(17) "lime_output_color"
    ["colorizer"]=>
    object(sfOutputEscaperObjectDecorator)#28 (2) {
      ["value":protected]=>
      object(__PHP_Incomplete_Class)#29 (1) {
        ["__PHP_Incomplete_Class_Name"]=>
        string(14) "lime_colorizer"
      }
      ...
    }
  }
  ["output"]=>
  object(__PHP_Incomplete_Class)#31 (2) {
    ["__PHP_Incomplete_Class_Name"]=>
    string(17) "lime_output_color"
    ...
 ...
}

Charles

@darkpills
Copy link
Contributor Author

Thank you a lot for the time you spent on testing :)

Actually, there are a lot of possibilities for the chains, but i tried to provide as less chains as possible (2) to bring all the possible coverage on all versions of symfony 1.

The first chain (Symfony/RCE12) covers versions 1.3.0 < 1.5.13~17 including 1.4.x and with a "maybe" between 1.5.13 and 1.5.17 depending on how you install it.

The second chain (Symfony/RCE13) covers versions 1.0.0 < 1.2.12. For those versions, lime_test is loaded in the autoload of symfony, as seen in this extract of www-1.2.12/cache/frontend/dev/config/config_autoload.yml.php:

'sfPager' => '/var/www/lib/addon/sfPager.class.php',
  'lime_test' => '/var/www/lib/vendor/lime/lime.php',
  'lime_output' => '/var/www/lib/vendor/lime/lime.php',
  'lime_output_color' => '/var/www/lib/vendor/lime/lime.php',
  'lime_colorizer' => '/var/www/lib/vendor/lime/lime.php',
  'lime_harness' => '/var/www/lib/vendor/lime/lime.php',
  'lime_coverage' => '/var/www/lib/vendor/lime/lime.php',
  'lime_registration' => '/var/www/lib/vendor/lime/lime.php',
  'sfFunctionCache' => '/var/www/lib/cache/sfFunctionCache.class.php',

However, the installation of version 1.x is a bit tricky. I have a blog post waiting to be published. You can find above an extract of the installation notes if it can help:

Version 1.4

Install documentation is still available at https://symfony.com/legacy/doc/getting-started/1_4/en/03-Symfony-Installation and need few changes to get it running:

As the SVN infrastructure has been discontinued, download the version from git and untar the project:

tar -xvzf symfony1-*.tar.gz
mv symfony1-*/* .

And start creating a project, an application and a module:

php ./lib/vendor/symfony/data/bin/symfony generate:project test
php symfony generate:app frontend
php symfony generate:module frontend test

Version 1.3

Same as 1.4, except a light change in default symfony bin path:

php ./data/bin/symfony generate:project test

Version 1.2

In version 1.2 and lower, full installation path can be found in cache files and project configuration class like config/ProjectConfiguration.class.php:

require_once '/workspace/www-1.2.12/lib/autoload/sfCoreAutoload.class.php';
sfCoreAutoload::register();

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    // for compatibility / remove and enable only the plugins you want
    $this->enableAllPluginsExcept(array('sfDoctrinePlugin', 'sfCompat10Plugin'));
  }
}

So depending on your setup, you might need to play with symlinks on your host/docker.

In lower versions of symfony, deprecated warnings can be seen. Error reporting verbosity can be changed in apps/frontend/config/settings.yml.

Version 1.1

Same as 1.2

Version 1.0

Same as 1.2, except some changes in the initial task names and no default symlink created:

php ./data/bin/symfony init-project test
php ./data/bin/symfony init-app frontend
php ./data/bin/symfony init-module frontend testclone

In 1.0 versions, the actions file name as been renamed:

  • 1.1+: apps/frontend/modules/test/actions/indexAction.class.php
  • 1.0: apps/frontend/modules/test/actions/actions.class.php

Also, the request object is not given as an argument to the controller, and it needs to be fetch manually from sfContext singleton by the developper:

class testActions extends sfActions
{
  public function executeIndex()
  {
    $request = sfContext::getInstance()->getRequest();
    $a = unserialize($request->getParameter('user'));
  }
}

@cfreal
Copy link
Collaborator

cfreal commented Feb 27, 2024

We seem to have a different autoload configuration; I don't have lime.php in cache/frontend/dev/config/config_autoload.yml.php, and you do.

$ cat cache/frontend/dev/config/config_autoload.yml.php | grep lime -i
$

PS: Also, your folder name is www-1.2.12. Is it vulnerable as well ? You indicate that it is not (using < instead of <=).

@darkpills
Copy link
Contributor Author

Could you precise the version of PHP you are using so that i can try to reproduce? I'm on PHP 5.6.40 for testing sf <= 1.4 to match with configuration of most applications which would remain on a 5.x stack.

I will need to update the versions. It's a <= for all the mentioned versions.

@cfreal
Copy link
Collaborator

cfreal commented Feb 27, 2024

Yes, sorry: PHP 5.6.40-68+ubuntu22.04.1+deb.sury.org+1 (cli)

@darkpills
Copy link
Contributor Author

OK, this is my fault: I uncompressed the symfony archive in the current directory, not in lib/vendor, making all symfony classes being autoloaded, even the one in lib/vendor/symfony/vendor/*

Arf, it breaks my chain for symfony <= 1.2.12 with a __destruct(). I will make a commit to remove this last one and propose other alternative chains, maybe based on Propel.

@thePanz
Copy link

thePanz commented Feb 28, 2024

@darkpills any updates on this?

ps: would you please check the advisory message thread? we made some progress and we're waiting for your advice. thank you

@darkpills
Copy link
Contributor Author

@thePanz the chain for >= 1.3.0 is valid, but the other one is not. I need to put more work into this one to provide complete chains. This can be done within the next days I think.

@cfreal
Copy link
Collaborator

cfreal commented Feb 29, 2024

OK, this is my fault: I uncompressed the symfony archive in the current directory, not in lib/vendor, making all symfony classes being autoloaded, even the one in lib/vendor/symfony/vendor/*

Oh, makes sense.

@darkpills
Copy link
Contributor Author

@cfreal : i provided the following updates and new chains replacing the old one to provide a coverage on all versions. However, most of these depends on enabled plugins ORM.

However^2, I found another nice gadget chain covering almost all versions of 1.x and without any dependencies. I will publish it here later on. I opened another advisory on Symfony1 repository and wait for them to patch it.

  • Symfony/RCE12: fixed the numbering to include the boundaries: 1.3.0 <= 1.5.13~17
  • Symfony/RCE13: 1.2.0 <= 1.2.12 with sfDoctrinePlugin enabled (will become the standard after)
  • Symfony/RCE14: 1.2.0 <= 1.2.12 with sfPropelPlugin enabled (the default for 1.2.x)
  • Symfony/RCE15: 1.0.0 <= 1.1.9 with sfPropelPlugin enabled (the default for <= 1.1.x), which contains for an unknown reasons many vendors, not only propel but also Creole ORM, and i use Creole for the gadget chain entry point.

Hope this time, there won't be any mistake :)

Another question for you that is not directly linked with the following PR: a class in the framework has this code:

public function __destruct()
{
    @mysql_close($this->db);
}

I dig in the php5 mysql extension source code to find any "callback" from C code to PHP code to trigger a gadget chain. However, I could only find zend resource access by with a zval input and resource deletion. Do you have the same analysis? Just if you have time to check, if not don't bother :)

@cfreal
Copy link
Collaborator

cfreal commented Mar 4, 2024

Hello,

Now PEAR is giving me hell. I cannot install the modules:

php5.6 -dinclude_path=/usr/local/lib/php:. ./symfony plugin:install sfPropelPlugin
>> plugin    installing plugin "sfPropelPlugin"
>> sfPearFrontendPlugin Attempting to discover channel "pear.symfony-project.com"...
>> sfPearFrontendPlugin Attempting fallback to https instead of http on channel
>> sfPearFrontendPlugin "pear.symfony-project.com"...

                                                         
  Unable to register channel "pear.symfony-project.com" (use --force-license to force installation)

Same as root, same with --force-license.

Can you walk me through your steps?

Charles

@darkpills
Copy link
Contributor Author

Actually, sfDoctrinePlugin and sfPropelPlugin are already bundled with the tar.gz of Symfony distribution from the legacy website. I did not install any third party component.

Here the is steps for 1.2:

mkdir lib/vendor 
cd lib/vendor
tar -xvzf ../../symfony1-*.tar.gz
mv symfony1-* symfony

php ./lib/vendor/symfony/data/bin/symfony generate:project test
php symfony generate:app frontend
php symfony generate:module frontend test

chmod 777 cache/ log/ 

sed -i -e s#"die("#"//die("#g ./web/frontend_dev.php
 sed -i -e s#"\$this->forward('default', 'module');"#"\$a = unserialize(\$request->getParameter('user'));"#g ./apps/frontend/modules/test/actions/actions.class.php

In config/ProjectConfiguration.class.php, enable the needed plugin:

public function setup()
{
  $this->enablePlugins('sfDoctrinePlugin');
}

For 1.0, there are some more steps:

Same as 1.2, except some changes in the initial task names:

php ./lib/vendor/symfony/data/bin/symfony new test
php symfony app frontend
php symfony module frontend test

In 1.0 versions, the actions file name as been renamed:

  • 1.1+: apps/frontend/modules/test/actions/indexAction.class.php
  • 1.0: apps/frontend/modules/test/actions/actions.class.php

Also, the request object is not given as an argument to the controller, and it needs to be fetch manually from sfContext singleton by the developper:

class testActions extends sfActions
{
  public function executeIndex()
  {
    $request = sfContext::getInstance()->getRequest();
    $a = unserialize($request->getParameter('user'));
  }
}

Finally, you need to remove some deprecated PHP options so that your lib/vendor/symfony/data/config/php.yml will look like this:

set:
  log_errors:                  on
  arg_separator.output:        |
    &amp;

check:

warn:
  session.auto_start:          off

@cfreal cfreal merged commit 9e33622 into ambionics:master Mar 13, 2024
@cfreal
Copy link
Collaborator

cfreal commented Mar 13, 2024

Hello darkpills,

Thanks to your detailed infos I was able to make it work! Everything works fine.

Thank you for you contribution!
Charles

@darkpills
Copy link
Contributor Author

Hello @cfreal thank you for your time for testing! Another PR coming quickly I hope.

ricardojba added a commit to ricardojba/poi-slinger that referenced this pull request Mar 25, 2024
### Add:
 - Symfony/RCE12
 - Symfony/RCE13
 - Symfony/RCE14
 - Symfony/RCE15

### Ref: 
ambionics/phpggc#182
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

Successfully merging this pull request may close these issues.

3 participants