php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #74207 escapeshellcmd PHP function bypass
Submitted: 2017-03-04 22:49 UTC Modified: 2017-10-16 03:04 UTC
From: apparitionsec at gmail dot com Assigned: pollita (profile)
Status: Closed Package: Unknown/Other Function
PHP Version: Irrelevant OS: Windows
Private report: No CVE-ID: None
 [2017-03-04 22:49 UTC] apparitionsec at gmail dot com
Description:
------------
I am reporting the following vulnerability in PHP function  'escapeshellcmd', it can be bypassed easily as below example using Windows cmd.exe /C ....

Best regards,
John Page AKA hyp3rlinx

Bypass easily using cmd /c %26 etc... on Windows.

Test script:
---------------
1) PHP file using escapeshellcmd to 'safely' call single command.

<?php

$c = escapeshellcmd($_GET['c']);
system($c);

?>


2) Bypass it.

http://localhost/test.php?c=cmd%20/c%20calc%26taskmgr

OR

http://localhost/test.php?c=cmd%20/c%20calc%26taskmgr%26mspaint



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-03-06 21:50 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2017-03-06 21:50 UTC] stas@php.net
I'm not sure I understand how this is "bypassing" anything. You feed the command to the shell, it is executed. escapeshellcmd just escapes cetrain characters, it can't magically make any command safe. So I'd like a bit more details about what you'd expect escapeshellcmd to do and in what sense this is a "bypass"?
 [2017-03-06 22:25 UTC] apparitionsec at gmail dot com
-Status: Feedback +Status: Open
 [2017-03-06 22:25 UTC] apparitionsec at gmail dot com
example supplied was just a for instance in that it doesn't stop mistakenly used things like /c being passed in.

PHP Code to get an EXE version  php, cmd.exe etc

<?php
$c = escapeshellcmd($_GET['n']);
system($c . " -v ");       
?>

1) open Windows command like

2) call from browser
http://localhost/test.php?n=c:\Windows\system32\cmd.exe%20/c%20calc

3) you will see script trying to start calc.exe

4) go to Windows CL and do taskkill /f /im calc.exe

You will see it terminated... that's my point.

Using this however will fail obviously:
http://localhost/escapeshellcmd-tests.php?n=c:\Windows\system32\cmd.exe%20calc
 [2017-03-06 22:51 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2017-03-06 22:51 UTC] stas@php.net
I'm sorry I still don't see where "bypass" is. You've launched a command that is supposed to run calc. It runs calc. It is expected. Where is the problem? What did you expect to happen instead?
 [2017-03-07 02:06 UTC] apparitionsec at gmail dot com
-Status: Feedback +Status: Open
 [2017-03-07 02:06 UTC] apparitionsec at gmail dot com
Another example:

On Windows CL below command will write info about a file named 'hi.txt' to file named 'test' and do dir listing.

C:\> dir hi.txt  > test | dir

But if we do this using PHP and use escapeshellcmd to try prevent arbitrary cmds it fails.

<?php
$c = escapeshellcmd($_GET['n']);
system("dir " . $c . " > TEST | dir");
?>

Then,

http://localhost/test.php?n=%26calc%26taskmgr

calc.exe and taskmgr get called.

feels a bit inconsistent at least on Windows, maybe bypass though was wrong word for it.
 [2017-03-07 02:50 UTC] pollita@php.net
Perhaps it would clarify matters if you removed the irrelevant parts of your code.  It doesn't matter if the input comes via $_GET or not, so just include a literal in your example code.

Executing the command isn't as diagnostic as showing what the output of escapeshellcmd() looks like (and what you expect it to look like).  So forget the call to system/shell_exec/etc...   Just echo it out.

3v4l.org is a great resource for this as well: https://3v4l.org/BD8XF is what I understand your example to be and as you can see the ampersands are escaped just as the manual says they will be.

How does this output differ from your expectations?
 [2017-03-07 02:52 UTC] pollita@php.net
Also, in your example:

$c = escapeshellcmd($_GET['n']);
system("dir " . $c . " > TEST | dir");

You should use escapeshellarg() here, not escapeshellcmd(), as $_GET['n'] is an argument, not a command.
 [2017-03-07 03:15 UTC] apparitionsec at gmail dot com
-Status: Open +Status: Closed
 [2017-03-07 03:15 UTC] apparitionsec at gmail dot com
Appreciate your replies, I was expect it to not run arbitrary command as the name implies. Yea forget the $_GET but this is the whole point. I don't care what the function output looks like diagnostically, only that it works to prevent arbitrary commands being called which in my examples it does not. Anyway, need to be careful using this function I think.

Thanks for anything.
John
 [2017-03-07 04:17 UTC] pollita@php.net
-Assigned To: +Assigned To: pollita
 [2017-03-07 04:17 UTC] pollita@php.net
> I don't care what the function output looks like diagnostically

Diagnostics are the only thing that make bug reports actionable.

All we have at this point is: "system() successfully runs commands, and escapeshellcmd() doesn't prevent it from running commands."

You've offered a nebulous definition of what a "bad" command is, but it's not up to the runtime to decide what commands you want to run, and escapeshellcmd() doesn't claim to offer that functionality.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 23:01:29 2024 UTC