Path: machine_name/transliterate?replace_pattern=a@e%00&replace=a
Сallback: href="https://api.drupal.org/api/drupal/core%21modules%21system%21src%21Machin...">MachineNameController::transliterate

All parameters that we pass to preg_replace come from the request so can be easily tweaked. That makes it possible to exploit dangers of preg_replace.

The vulnerability is mitigated by the fact that we bumped minimal PHP version yesterday. So that new installations of Drupal 8 are not affected, however tweaked url parameters still produce some warnings for them.

Example of how it works on PHP 5.4.6 and erlier:
http://sandbox.onlinephpfunctions.com/code/7b5b2893eb4ce2ad69f129d683a2a...

This was reported as part of the Drupal 8 security bug bounty program
https://tracker.bugcrowd.com/submissions/ca620796c7ce73e4aaa04283fdda3a4...

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Chi’s picture

we bumped minimal PHP version yesterday

Just realized that we did not yet. It is just a policy.

Chi’s picture

Issue summary: View changes

Sec Bug #55856 was fixed in PHP 5.4.7.
Sandbox: http://3v4l.org/YvY2J

Chi’s picture

Component: syslog.module » system.module
dawehner’s picture

Issue tags: +Needs tests

So what we should do as part of that issue is to write a test. The bufix will be done automatically as part of #2508231: Raise minimum required version of PHP to 5.5.9 if I understand it correctly?

Chi’s picture

The code with wrong parameters emits warnings on any PHP version. I would add some validation for the parameters. At least preq_quote for $replacement_pattern variable. But I am not sure whether it suits Drupal GIGO policy.
In my opinion we should prevent any PHP errors that can be caused by wrong user input, only because it can expose some information about server directory structure. It also reduces the risk of getting security issues like this one.

dawehner’s picture

Well yeah I totally think we should validate our regular expression here ... well ideally we would not pas it along, given what happens if you can execute a really slow regular
expression. Maybe an idea, could we hash them and also sent a token along, so we know, its always coming from Drupal itself?

So would it be safe enough to just validate on of the following characters: [a-zA-Z0-9\[\]\(\)\.\+\*\\]?

pwolanin’s picture

Status: Active » Needs review
FileSize
717 bytes

Here's a simple start on a patch.

Seems like the idea is to replace multi-lingual patterns? I don't know - this whole methods smells bad.

dawehner’s picture

@pwolanin
I think the most important part of this issue is not to fix it but to rather write the tests given that it will be fixed in PHP 5.5 anyway.

pwolanin’s picture

@dawhener - yes, agreed, especially since it's not at all obvious what the point of this method is.

chx’s picture

Wait a minute, if this is fixed in PHP 5.5 and we raising to PHP 5.5 what do we want to test?? It's not our duty to test the PHP engine.

catch’s picture

If we get PHP warnings from user input, we should try to prevent those too, but that's just a major bug and not release blocking at all. The test could definitely check for those.

Once we require PHP 5.5, I think I'd probably downgrade this to major, but we should leave it critical until that's actually happened.

dawehner’s picture

Wait a minute, if this is fixed in PHP 5.5 and we raising to PHP 5.5 what do we want to test?? It's not our duty to test the PHP engine.

Well, tests are also an explicit documentation of what is going on or should. Whether under the hood the problem still exists or not, is kinda an implementation detail :)

xjm’s picture

@catch, @alexpott, @effulgentsia, and I discussed this issue today. This issue becomes major once #2508231: Raise minimum required version of PHP to 5.5.9 is committed, but currently that issue needs to be postponed pending testbot support. Since this is a nasty security vulnerability for current D8 sites, we propose committing the current patch as a hotfix pending the necessary review, without waiting for test coverage. Then we'll downgrade the issue to major for test coverage and any needed improvement.

Fabianx’s picture

Title: Code injection in MachineNameController » Code injection via preg_replace()
Status: Needs review » Needs work
Issue tags: +D8 Accelerate

I've reviewed the issue and proposed fix carefully again and using preg_quote solves the problem.

HOWEVER:

I can't RTBC it, because of:

modules/image/src/PathProcessor/PathProcessorImageStyles.php: $rest = preg_replace('|^' . $path_prefix . '|', '', $path);
modules/search/search.module: $text = trim(preg_replace('/' . $boundary . '(?:' . implode('|', $keys) . ')' . $boundary . '/iu', '\0', ' ' . $text . ' '));
modules/system/src/MachineNameController.php: $transliterated = preg_replace('@' . $replace_pattern . '@', $replace, $transliterated);
lib/Drupal/Core/Block/BlockBase.php: $transliterated = preg_replace('@' . $replace_pattern . '@', '', $transliterated);

using the exact same pattern. (audited via: grep preg_replace -r modules/ lib/ | grep --color '\. \$')

At least BlockBase looks really really suspicious.

I suggest to do a more extensive search - but there are a lot of preg_replace in core.

tim.plunkett’s picture

In BlockBase, on the previous line it's just $replace_pattern = '[^a-z0-9_.]+';, not sure why it even uses a temporary variable.

Though it does have this comment:

    // @todo This is basically the same as what is done in
    //   \Drupal\system\MachineNameController::transliterate(), so it might make
    //   sense to provide a common service for the two.
pwolanin’s picture

Issue summary: View changes
dawehner’s picture

modules/image/src/PathProcessor/PathProcessorImageStyles.php: $rest = preg_replace('|^' . $path_prefix . '|', '', $path);

Well to be clear, this requires access to "administer site configuration" which is registered ...

$directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
    if (strpos($path, $directory_path . '/styles/') === 0) {
      $path_prefix = $directory_path . '/styles/';
    }
    elseif (strpos($path, 'system/files/styles/') === 0) {
      $path_prefix = 'system/files/styles/';
    }

lib/Drupal/Core/Block/BlockBase.php: $transliterated = preg_replace('@' . $replace_pattern . '@', '', $transliterated);
Agreed, we control the replace_pattern entirely.

modules/search/search.module: $text = trim(preg_replace('/' . $boundary . '(?:' . implode('|', $keys) . ')' . $boundary . '/iu', '\0', ' ' . $text . ' '));

Note: $keys are preg_quoted(), see

      // Quote slashes so they can be used in regular expressions.
      $temp_keys[] = preg_quote($key, '/');
    }
  }
  // Several keywords could have simplified down to the same thing, so pick
  // out the unique ones.
  $keys = array_unique($temp_keys);
Fabianx’s picture

I think we still should set a good example in Core.

Someone might just grep for code in core or see it accidentally while looking at the API docs and then "learn" from it ...

dawehner’s picture

FileSize
1.54 KB

Well fair ...

Fabianx’s picture

#19 is missing #7 unfortunately ...

darol100’s picture

Status: Needs work » Needs review
FileSize
613 bytes
2.04 KB

@Fabianx,

I have combine #7 and #19.

Status: Needs review » Needs work

The last submitted patch, 21: 2508735-Code-injection-via-preg_replace-21.patch, failed testing.

Fabianx’s picture

#21 Uhm, no we wanted to change MachineNameController.php, too.

darol100’s picture

Status: Needs work » Needs review
FileSize
2.77 KB
599 bytes

oops... I did not notice that it was a different file. Here is an updated version of the patch base on #23 suggestion.

pwolanin’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests +D8 Security Bounty

per xjm in #13, let's create a follow-up for test coverage and resolve the bug now.

Patch looks like a reasonable combination of the 2 preg_quote calls and cleanup in BlockBase

dawehner’s picture

I think this is far given that its a non issue in 5.5 AND the testbot probably can't test this either because they are running in a higher PHP version.

Fabianx’s picture

RTBC + 1, Looks good to me.

MiroslavBanov’s picture

Doesn't the page on php.net say that the /e modifier is deprecated, not removed?
http://php.net/manual/en/function.preg-replace.php

I don't understand how bumping PHP version solves anything?

Chi’s picture

@MiroslavBanov, since the pattern is enclosed by delimiters you cannot inject /e modifier without exploiting null byte vulnerability that was fixed in PHP 5.4.7.

MiroslavBanov’s picture

@Chi
Thanks for the clarification.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +Needs followup

Committed d433aec and pushed to 8.0.x. Thanks!

As per #13, let's open a followup for adding the test coverage.

  • alexpott committed d433aec on 8.0.x
    Issue #2508735 by darol100, dawehner, pwolanin, Chi, Fabianx, tim....
Fabianx’s picture

YesCT’s picture

Issue tags: -Needs followup

removing tag since the follow up was opened.

willzyx’s picture

This change seems to create some trouble with machine name inputs. See #2525870: Regression: machine name inputs no longer work properly after #2508735

  • webchick committed 227f243 on 8.0.x
    Issue #2525870 by pwolanin, willzyx, Fabianx: Regression: machine name...

  • xjm committed ccfc649 on 8.0.x
    Issue #2525870 by pwolanin, willzyx, Fabianx: Regression: machine name...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.