I am currently hosting a Drupal 6 site on a CentOS machine. The Drupal (CMS) configuration contains a few dozen third-party modules that should not be forked as a general best coding practice. However, some of these modules make use of the php exec
command in order to function properly.
The site allows for admins to embed php code snippets in any page via a UI configuration, granted that they have access to the php code input format. I need to keep this input format available to admins because there are several nodes (pages) and panel panes that make use of small, harmless php code snippets, like embedding a specific form into the content region, for example.
The issue is that if someone were to compromise an admin account, then they could run arbitrary php code on the site, and thus run shell commands via php's exec
, passthru
, etc. Is there any way, from an operating system level, to restrict what shell commands php can pass through to the machine? Could this be done via restricting file permissions to some programs from php?
Note: I cannot use the php.ini disable_functions directive as I still need exec
to function normally for many cases, where modules make use of certain shell commands, like video encoding, for example.
If your admininstrator accounts get hacked, you are doomed. You are trying to be less doomed, but this will not work.
Disabling exec()
This is only a fraction of all the functions that are able to make calls to the system. There are plenty more, like
passthru()
,shell_exec()
,popen()
,proc_open()
, the backtick operator, and so on. Will not help.Restricting available executables
Would work only if the attacker cannot bring his own executables. But
file_put_contents()
will be able to write the executable to the harddisk, and it can then be called. Will also not help.PHP cannot do any harm itself, can it?
Wrong. Executing stuff on the server via
exec()
might seem the best idea, but PHP itself is powerful enough to wreak havoc on your server and anything that is connected to it.I think the only real solution is:
Do not allow your admin accounts to get hacked.
And if they get hacked, be able to know it immediately. Be able to trace back an attack to an administrator. Be able to know what exactly the attacker did to your machine so that you might be able to undo it. A very important part is that you implement an audit trail logger that saves it's data on a different machine.
And you'd probably implement tighter restrictions on who can login as an administrator in the first place. For example, there probably is no need to allow the whole worlds IP range to login if you know for sure that one certain admin always uses the IP range of his local ISP to work. At least, ring a bell and inform somebody else that a login from china is going on if this is not expected (and unless you are operating in china :-) ).
And there is two factor authentication. You could send an SMS to the phone number with an additional login code. Or you might be able to completely outsource the login by implementing Google or Facebook authentication. These players already have the infrastructure to support this.
And additionally, you get higher resistance against inside jobs. People do value their personal social network logins higher than the login for their employer. Getting someones facebook password will cost 30$ on average, but the company login is already shared for 8$. Go figure...
To answer your question: in theory, if you created a user account using an extremely restricted account that PHP could run commands as, you could tune your installation to be more secure. However, the real problem is that administrator users are able to execute arbitrary commands. If that happens, you will have a significantly larger problem on your hands.
The real solution here is:
Another approach:
We believe that we need to create a test user that only has access to the system to perform a telnet to another machine on the network. Since we only need to run a telnet need to restrict the other commands available in a standard bash session. Let's go step by step configuring everything.
1) We create user test
This will be a regular user of the system, so should we as a normal user. The only peculiarity is that we change the shell of that user. The default is usually / bin / bash and we will set / bin / rbash. rbash is actually a copy of bash, but it really is a "restricted bash".
2) We create the file. Bash_profile
We must create this file in the user's home that was created and for which we want to apply the permissions. The contents of the file will be as follows,
3)We avoid changes
Once you have created the file, we stop nobody can make changes to the file.
4) We create the apps directory and install the programs 'access'
Now once you have everything set up and only have to create the apps and inside it, create a link to the programs you want the user to have permissions. All programs that are within apps, the user can run the but, no.
5) We found that works
Now you can access the system and verified that it works correctly.
Team:
Here's my approach.....
I created a small script with a list of accepted commands, and depending on the command, the execution will be controlled for avoiding issues. I'm not sure if it is in the scope of the question. The sample code have commands in Windows, but you can use it in Linux as well...
Output:
Is it possible to restrict what commands php can pass through exec at an OS level?
test of an accepted command: 117 Dir(s) 11,937,468,416 bytes free
test of an unaccepted command: Sorry, format command is not allowed
This isn't at the OS level, but inside the Drupal core there is a module called PHP. You could use this as a base and create a custom module that extends the functionality of this module, and then simply enable this module as opposed to the Drupal 6 core module. The big problem with this however comes with disabling the Drupal 6 core module and then enabling your new module. I'd test it on a dev install to make sure previous content is not deleted and that the new module correctly parses stored PHP input. It should be alright as the module install only has a disable hook warning against PHP content now showing in plain text.
As for extending the core module, it's a very simple module to start. You could hardcode in a list of allowed or not-allowed commands to execute. You can then check the exec statement with resolved variables against this list and do whatever is appropriate. It's not a perfect match against simply blocking at the OS level programs themselves, but it's better than nothing. To hardcode a list, you'll simply want to modify the php_filter hook at the bottom of the module file and before doing a drupal_eval, do your test.
You could also extend this module to be configurable in the Drupal admin interface to create that list instead of hardcoding it. Hope this idea helps.