Aside from CGI, one overlooked use of sh
is in exec()
calls, or through the use of system()
and popen()
, on most Linux systems this means bash
. The exec()
family of calls are often used with "/bin/sh -c
" to provide various features like shell redirection, pipelines or even just argument expansion when invoking processes.
Apache uses exactly this (via APR, see the use of SHELL_PATH
in apr/threadproc/unix/proc.c
) when you use piped logs, and also in other circumstances such as external output filters (via ExtFilterDefine
), and processing of #exec cmd
SSI includes. This behaviour almost certainly applies to many third party modules too.
The best steps to take to secure your web server:
- patch or upgrade your bash package (if you do this yourself make sure there are no stray
sh
instances, check for sh
in any manually set up chroots too)
run Apache in a chroot, use a minimal /bin/sh
if and only if it is required:
- in Apache httpd 2.2 switch from using "
|
" to "||
" in piped logs, this removes the need for /bin/sh
(available since Apache 2.2.12)
- in Apache httpd 2.4 make sure you're not using "
|$
" in piped logs, this reverts to the httpd.2.2 behaviour. 2.4 supports |
and ||
, neither use /bin/sh
make sure the default CGI scripts are disabled, double check from the top-level Options
(e.g. <Directory />
down for ExecCGI
consider mod_security
if you're not already using it. Red Hat have provided some rules which can be used to log and clean up the environment in KB article 1212303.
You may also consider using "#!/bin/sh -p
" in scripts, this forces bash into privileged mode, this prevents importing functions from the environment (along with other changes). This might help if you have vulnerable CGI scripts on a system, but no patch and no compiler. This might not work on Debian and derived systems, so check carefully.
(Also, if /bin/sh
is bash
, don't blindly replace it with something else, it might affect your startup scripts.)
Any application code that runs via CGI (other than bash scripts), or under Apache modules (Python and PHP in your case) may also play a part in this. If any variable can be sufficiently controlled by an attacker (URI-encoding may be a challenge), and if the application uses /bin/sh
when invoking processes, and if those variables are preserved while doing so, then you might have a problem.
If you are running a contemporary Linux system, but you have not set up a chroot or some other container for apache, you should be able to use unshare
and a bind mount to replace /bin/sh
when starting Apache (or any other service).
unshare -m -- sh -c "mount --bind /usr/local/bin/bash /bin/sh &&
/usr/local/apache2/bin/apachectl start"
The advantages to this are
- less risk of breaking things, since you can selectively replace
bash
- you can log and audit attempts to invoke
sh
more easily
(What you cannot easily do with this is write a bash script as a wrapper to inspect and log suspicious variables, you'll need to use something else of course...)
If you're running auditd
on Linux, you can easily watch the use of /bin/sh
by adding a couple of rules to audit.rules
and reloading:
-w /bin/sh -p x
-w /bin/bash -p x
(I need both, since sh
is a symlink to bash
)
Red Hat have published an alternate approach, a runtime "fix" which you can use with LD_PRELOAD
, you can find that in the advisory. This could also be adapted to log dubious variables, if required. Given that there are currently questions over the completeness of the patches so far, this solution is probably a good short-term fix for Apache at least.
You can also find more general Apache hardening tips here: Apache Server Hardening