# rm-rf.es | Administración de sistemas

Bitácora personal de un SysAdmin Gnu/Linux, Windows, BSD...

Vulnerabilidad crítica en instalaciones de PHP basadas en CGI


Todas las instalaciones de PHP en modo CGI están afectadas, al parecer se trata de una vulnerabilidad de la cual nadie se había dado cuenta en nada menos que ocho años.

Básicamente, la vulnerabilidad consiste en que en este tipo de instalaciones (sólo CGI, FastCGI o DSO no se ven afectadas), si la petición/request contiene ?-s permite volcar por pantalla el código fuente PHP en lugar de interpretarlo.

Para verificar si la vulnerabilidad nos afecta, sólo es necesario añadir ?-s a la URL y ver si descargamos el código fuente, ejemplo:

dominio.com/test.php?-s

Por suerte, ya podemos descargar las versiones parcheadas desde el sitio web de PHP, concretamente la PHP 5.3.12 o PHP 5.4.2. Si no queremos o podemos actualizar tan sencillamente, podemos aplicar un workaround mediante el cual configuramos unos rewrite, ya sea en el virtualhost o a nivel de .htaccess para bloquear este tipo de peticiones. Para Apache con mod_rewrite:

         RewriteCond %{QUERY_STRING} ^(%2d|-)[^=]+$ [NC]
         RewriteRule ^(.*) $1? [L]

Más información en www.php.net

Instalar PHP 5.2 en lugar de PHP 5.3 en Debian Squeeze


Logo PHPMuchos pensaréis que para que instalar la versión 5.2, que ya está obsoleta, en lugar de la 5.3 que es la estable actualmente. Lamentablemente hay aplicaciones que todavía no son compatibles con 5.3.3, que es la versión de PHP que trae Debian Squeeze en los repositorios.

Lo que vamos a hacer entonces es configurar el sistema para que instale con repositorios de Lenny todo lo relacionado con PHP. Personalmente, lo que más me ha costado es encontrar unos repositorios activos de la arquitectura de la máquina en la que estaba trabajando, encontré un mirror de dig.

Lo primero es añadir estos repositorios a nuestras sources.list:

# vim /etc/apt/sources.list
# Repositorios para versión antigua de PHP (5.2.X)
deb http://mirrors.digg.com/debian lenny main contrib non-free
deb-src http://mirrors.digg.com/debian lenny main contrib non-free

Además de esto, vamos a cambiar la prioridad para los paquetes PHP y que usen estos repositorios obsoletos:

# vim /etc/apt/preferences
Package: php5*
Pin: release a=oldstable
Pin-Priority: 1100

Actualizamos los repositorios:

# apt-get update

Y ya podemos instalar los paquetes que quereamos de PHP:

# apt-get -t oldstable install php5

En el caso de que ya tuviéramos los paquetes de Squeeze instalados, los tenemos que des-instalar antes, todos:

# dpkg --get-selections | grep php

Y con el resultado:

# apt-get purge ...

Y un método más rápido:

# apt-get remove `dpkg -l | grep php| awk '{print $2}' |tr "\n" " "`

o

# apt-get purge `dpkg -l | grep php| awk '{print $2}' |tr "\n" " "`

Si no os gusta esta opción, siempre se puede compilar PHP a mano y elegir la versión exacta que necesitemos así como los módulos.

Instalación y configuración de suPHP


suPHPHe hablado varias veces sobre configuraciones y posibles errores de suPHP pero nunca sobre el modo de instalarlo. Para quienes no lo sepan, suPHP es un módulo de Apache y a su vez una utilidad independiente que permite que los scripts PHP se ejecuten con el usuario propietario del script. Así mismo, permite establecer restricciones de seguridad como evitar permisos 777 en directorios, establecer un UID y GID mínimos para los usuarios, forzar a que los scripts de encuentren en el document_root del host, etc.

La instalación es sencilla. Vamos a suponer que tenemos tanto Apache como PHP compilados o instalados. La forma más sencilla de activar suPHP es instalando el rpm disponible desde el sitio web oficial, aunque por supuesto podemos compilarlo con los requerimientos que tengamos bajando las sources.

En el caso de rpm, para RHEL, CentOS, Scientific Linux, etc es tan sencillo como bajar el rpm de nuestra arquitectura e instalarlo:

# wget http://pkgs.repoforge.org/mod_suphp/mod_suphp-0.7.1-1.el6.rf.x86_64.rpm
# rpm -ivh mod_suphp-0.7.1-1.el6.rf.x86_64.rpm

Una vez instalado, se debería haber creado el fichero de configuración en /etc/httpd/conf.d/suphp.conf. Antes de nada, debemos mover, eliminar o comentar todo el contenido del fichero /etc/httpd/conf.d/php.conf para no cargar el módulo DSO de php, que como sabéis hace que todos los scripts php se ejecuten con el usuario de Apache (www-root, apache, nobody…) con el problema de seguridad que conlleva.

Lo más importante del fichero de configuración es tener cargado el módulo y especificados los handler que dirán como gestionar los scripts php, así como tener el motor de suPHP activado:

LoadModule suphp_module modules/mod_suphp.so

# This option tells mod_suphp if a PHP-script requested on this server (or
# VirtualHost) should be run with the PHP-interpreter or returned to the
# browser "as it is".
suPHP_Engine on

# To use suPHP to parse PHP-Files
AddHandler x-httpd-php .php
AddHandler x-httpd-php .php .php4 .php3 .phtml

El otro fichero de configuración crítico es /etc/suphp.conf, cuyas variables son suficientemente descriptivas, los valores por defecto son válidos para una configuración correcta. Tened en cuenta únicamente el bug que ya comenté de los handlers. Revisad la documentación oficial para más info.

[global]
logfile=/var/log/httpd/suphp_log
loglevel=info
webserver_user=apache
docroot=/
env_path=/bin:/usr/bin
umask=0077
min_uid=500
min_gid=500

; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false

;Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true

;Send minor error messages to browser
errors_to_browser=false

[handlers]
;Handler for php-scripts
x-httpd-php="php:/usr/bin/php-cgi"

;Handler for CGI-scripts
x-suphp-cgi="execute:!self"

Finalmente, en cada virtualhost tendremos que especificar el usuario y grupo que ejecutará los scripts php, usamos la directiva suPHP_UserGroup en cada Virtualhost:

suPHP_UserGroup usuario grupo

Validamos la configuración y reiniciamos Apache. Tened en cuenta las siguientes consideraciones básicas (revisad los logs de errores de Apache y suPHP porque tendréis seguro algunos), por defecto tenemos lo siguiente:

  • Los scripts php no pueden tener permiso de escritura para el grupo ni para el resto, es decir, lo máximo sería 0644
  • Los directorios no pueden tener permisos para otros ni el grupo, es decir, 777 no es válido y 775 tampoco, recomendable 755.
  • Las directivas php_flag de .htaccess pasan a un fichero php.ini en la misma ruta con el formato estándar de php.
  • El propietario y grupo de los scripts debe ser el especificado en suPHP_UserGroup.

Instalar php-mbstring en RHEL 6 por yum


Hoy toca una entrada rápida. Si tenéis que instalar el módulo php mb-strings por yum/rpm en RHEL 6 de primeras veréis que el paquete no está disponible en los repositorios oficiales. Esto es porque hay que activar el repositorio opcional desde la Red Hat Network.

Para ello acceded a http://rhn.redhat.com con vuestro usuario y password. Entrad en la sección de sistemas (Systems), pinchad en el servidor que queráis activar el repositorio y una vez dentro accedemos a la sección de suscripciones a canales (modificar canales de suscripción / Subscribed Channels (Alter Channel Subscriptions). Ahí es donde activamos RHEL Server Optional y guardamos.

Ahora en la máquina únicamente queda actualizar el perfil rhn e instalar:

# rhn-profile-sync
# yum install php-mbstring

De este modo nos evitamos compilar a mano php y tenemos la versión pre-compilada oficial de RedHat.

10 trucos para securizar PHP


php 5.3.6

En esta entrada vamos a ver unos cuantos puntos que nos servirán para securizar PHP en nuestro servidor web. Nos centramos en la configuración propia de PHP, hay que tener en cuenta que la securización de la capa aplicación es tanto o más importante que la de la configuración del servidor. Hay que tener siempre actualizados a la última versión estable y con los parches de seguridad correctamente aplicados cualquier cms o script de terceros tipo WordPress, Joomla, Oscommerce, etc.

Ocultar la versión de PHP

En su día hice un artículo completo sobre esto, podéis verlo aquí (ocultar la versión de PHP). Básicamente evitamos que con un simple telnet puedan averiguar la versión de PHP que hay corriendo en el servidor:

# telnet servidor 80

Connected to xxx.com (xx.xx.xx.xx).
Escape character is '^]'.
HEAD / HTTP/1.0

HTTP/1.1 200 OK
Date: Fri, 13 Aug 2010 14:18:09 GMT
Server: Apache/1.3.26 (Unix) mod_gzip/1.3.26.1a PHP/5.3.3
Last-Modified: Fri, 12 Feb 2010 12:22:56 GMT
ETag: "44967c-6f-53ca4800"
Accept-Ranges: bytes
Content-Length: 111
Connection: close
Content-Type: text/html

Connection closed by foreign host.

Para evitar esto cambiamos a off la siguiente variable en el fichero de configuración general php.ini:

expose_php Off

Deshabilitar funciones peligrosas

También hice un artículo hace un tiempo: PHP: Deshabilitar funciones peligrosas. Existen funciones que en servidores con aplicaciones estándar rara vez se usan, pero que por contra pueden generar graves problemas de seguridad si el atacante consigue utilizarlas. Es preferible desactivar el mayor número posible y en caso de que una de ellas sea necesario valorar su activación o proponer alternativas.

Las funciones a deshabilitar se añaden en la directiva “disable_functions” en el fichero php.ini. Aquí tenéis un ejemplo con funciones peligrosas que conviene desactivar:

disable_functions = “apache_child_terminate, apache_setenv, define_syslog_variables, escapeshellarg, escapeshellcmd, eval, exec, fp, fput, ftp_connect, ftp_exec, ftp_get, ftp_login, ftp_nb_fput, ftp_put, ftp_raw, ftp_rawlist, highlight_file, ini_alter, ini_get_all, ini_restore, inject_code, mysql_pconnect, openlog, passthru, php_uname, phpAds_remoteInfo, phpAds_XmlRpc, phpAds_xmlrpcDecode, phpAds_xmlrpcEncode, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, shell_exec, syslog, system, xmlrpc_entity_decode”

O un ejemplo más conservador:

disable_functions ="system,passthru,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,popen,show_source,pcntl_exec"

deshabilitar session ID en URL

Si queremos que las URL’s de nuestros sitios PHP no muestren los ID de sesiones:

http://ejemplo.com/?PHPSESSID=4kgj577sgabvnmhjgkdiuy1956if6ska

Modificamos la directiva siguiente en el fichero php.ini:

session.use_trans_sid = off

Deshabilitar register_globals

Pese a ser una directiva antigua y que se encuentra en periodo de extinción todavía quedan desarrolladores/programadores que la utilizan. Esta función permite al atacante manipular cualquier variable global definida en la programación. Por defecto está deshabilitada en las versiones actuales de PHP, pero conviene revisarlo por si en algún momento se ha activado:

register_globals = Off

Desactivar acceso a URL remotas en funciones de manejo de ficheros

Funciones como include, fopen o file_get_contents permiten, además de hacer llamadas a ficheros locales, llamar a ficheros vía URL, esto puede provocar graves errores de seguridad invocando a scripts maliciosos que se encuentran fuera de nuestro servidor y su ejecución remota.

Para deshabilitarlo modificamos la directiva allow_url_fopen a Off en el php.ini:

allow_url_fopen = Off

Evitar el acceso a la información de PHP

Como ya sabéis con un simple script como el siguiente podemos ver vía URL toda la información de PHP, su compilación, sus módulos activados, versión, directivas de configuración, etc.

<? phpinfo() ?>

Si queremos desactivarlo, únicamente hay que modificar a Off la siguiente directiva en el fichero php.ini:

expose_php = Off

Si en algún momento necesitáis conocer alguna información, siempre podéis averiguarla desde línea de comandos: información sobre PHP desde línea de comandos

Safe Mode

safe_mode = On

Si bien por defecto safe_mode On puede significar una restricción demasiado fuerte en determinados entornos, puede ayudar mucho a incrementar la seguridad de nuestro servidor. Activar safe_mode implica que los scripts PHP únicamente pueden acceder a los ficheros que tienen como propietario el mismo que ellos. De este modo evitamos por ejemplo que tengan acceso de lectura a ficheros de sistema como /etc/passwd entre otros.

Efectivamente, esto puede ser un problema en el momento que necesitamos acceder a información generada por otros usuarios en el sistema (ficheros de otras aplicaciones. La solución es la siguiente:

safe_mode = Off
safe_mode_gid = On

Activamos safe_mode_gid en lugar de safe_mode, de modo que en lugar de revisar el usuario se revisa el grupo. Independientemente del UID del fichero, necesitaremos que estar dentro del grupo para poder acceder al ficheros. El grupo del script PHP deberá ser el mismo que el del fichero a acceder.

Otro punto a tener en cuenta con safe_mode es que no podremos ejecutar binarios. Únicamente aquellos que ubiquemos en el directorio especificado en la configuración:

safe_mode_exec_dir = /directorio

Esto se solaparía con las funciones deshabilitadas anteriormente, como por ejemplo system() o exec().

open_basedir

open_basedir = /directorio

La directiva open_basedir permite configurar que PHP pueda acceder únicamente a los ficheros de un único directorio (y sus subdirectorios). Es una buena forma de enjaular PHP si realmente sólo necesitamos que acceda a un determinado directorio.

Visualización y registro de errores

display_errors = Off
log_errors = On
error_log = /ruta/fichero/log

Con estas tres directivas, evitamos que cualquier error o warning se muestre por pantalla y hacemos que se registren directamente en un log especificado. De este modo podemos evitar que se muestre información sensible por pantalla. También podéis hacer uso de la directiva error_reporting si queréis mostrar por pantalla los errores. Con ella podéis especificar que se muestren únicamente los warning, los notice o lo que queráis.

SQL Injection

En su día se utilizaba magic_quotes para limpiar los datos de entrada de un script PHP, pero es una directiva obsoleta a partir de PHP 5.3 así que no merece la pena hablar de ella. En su defecto, si usáis servidor web Apache, os recomiendo encarecidamente configurar mod_security y habilitar sus reglas que permiten detectar y parar una gran cantidad de ataques de este tipo, y sino, puedes crear tus propias reglas.

Como último apunte, conviene siempre si es posible mantener una versión estable de PHP, libre de bugs y fallos de seguridad. Seguro además que una vez aplicados estos trucos os toca lidiar con los programadores. Se quejarán de algo seguro ;)

Stub Start Error en Oracle iPlanet Web Server: PHP + FastCGI


En el artículo anterior veíamos cómo activar PHP en Oracle iPlanet Web Server bajo Solaris. Una vez activado a la hora de verificar el funcionamiento de un script PHP podéis encontrar lo siguiente al acceder vía URL:

Stub Start Error
This server has encountered an internal error which prevents it from fulfilling your request. The most likely cause is a misconfiguration. Please ask the administrator to look for messages in the server’s error log.

Y al revisar los logs de error de la instancia vemos lo siguiente:

[12/Oct/2011:10:13:28] failure ( 1023): for host 192.168.1.128 trying to GET /test.php, responder-fastcgi reports: FCGI1058: Process creation failure
[12/Oct/2011:10:13:43] failure ( 1023): for host 192.168.1.128 trying to GET /test.php, responder-fastcgi reports: FCGI1058: Process creation failure

El servidor web no es capaz de lanzar los procesos fastcgi de php, esto lo podemos ver porque ni siquiera se crea el log de fastcgi (Fastcgistub.log). Según he visto en este post de un foro es un problema común en máquinas con poca memoria swap configurada. En mi caso estaba claro porque esta prueba estaba siendo realizada sobre una pequeña máquina virtual. La solución es tan simple como aumentar el tamaño del area de intercambio o añadir más. Si no os apetece tocar la swap actual porque está en uso podéis añadir una nueva con zfs:

# zfs create -V 1G rpool/swap-extra
# swap -a /dev/zvol/dsk/rpool/swap-extra
# swap -l
swapfile             dev    swaplo   blocks     free
/dev/zvol/dsk/rpool/swap 171,2         8  1048568  1048568
/dev/zvol/dsk/rpool/swap-extra 171,3         8   819192   819192

Una vez activada la nueva swap los procesos FastCGI deberían arrancar sin problemas y ejecutar los PHP sin errores.

Configurar PHP + FastCGI en Oracle iPlanet Web Server


A través del sitio web de soporte de Oracle (https://support.oracle.com) podemos descargar el patch de PHP 5.2.X disponible para la versión de Oracle iPlanet Web Server que tengamos instalada, en este caso para la 7.0.12. sobre Solaris. Este parche nos va a permitir activar PHP con FastCGI a modo de plugin del web server.

Una vez bajado el patch (p12680045_10000_Generic.zip en mi caso), procedemos a descomprimirlo y copiar la carpeta “php” resultante al directorio plugins en la ruta donde hayamos instalado el servidor web:

# cp -Rp /var/tmp/php /opt/iplanet-webserver7/plugins/

Exportamos la variable LD_LIBRARY_PATH con la ruta hacia este directorio:

# export LD_LIBRARY_PATH=/opt/iplanet-webserver7/plugins/php

Ahora, podemos hacer una verificación de que php funciona desde línea de comandos:

# cd /opt/iplanet-webserver7/plugins/php/bin && ./php -v
PHP 5.2.X (cgi-fcgi) (built: Jan XX XXXX 22:31:04)
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2011 Zend Technologies

Llegado este punto, únicamente tendríamos que activar php para la instancia en la que queramos usarlo, en este caso en la instancia https-website. Para ello haremos uso del script setupPHP disponible en el directorio “php”:

# cd /opt/webserver7/plugins/php/
# ./setupPHP -instancename=https-website

UPDATED: /opt/iplanet-webserver7/https-www/config/magnus.conf
UPDATED: /opt/iplanet-webserver7/https-www/config/obj.conf
UPDATED: /opt/iplanet-webserver7/https-www/config/mime.types

Como veis en la salida del comando, automáticamente se han actualizado los ficheros de configuración de la instancia, añadiendo los handler de php, las configuraciones y directivas de FastCGI, etc.

Si utilizáis un usuario distinto de root para correr el servidor web, aseguraos de que los propietarios de los tres ficheros anteriores se mantienen bien, sino no arrancará la instancia.

Ya solo quedaría hacer el pull-config con los nuevos cambios de configuración de la instancia y el deploy. Podemos hacerlo a través de la interfaz web de administración de iPlanet Web Server o desde línea de comandos con wadm:

# /opt/iplanet-webserver7/bin/wadm [--user=admin-user] [--password-file=admin-pswd-file] [--host=admin-host] [--port=admin-port]
pull-config --config=www nodo
pull-config --config=website hostname
deploy-config website

Apache: servir PHP usando extensión HTML


ApacheExiste la posibilidad de que queramos introducir código PHP dentro de ficheros estáticos (.html, .htm…), de modo que al servir la página a través del navegador dicho código php se ejecute en lugar de mostrarse como texto plano. Este truco valdría tanto para cualquier otra extensión (cgi por ejemplo).

Para realizar esta configuración, debemos utilizar la directiva AddType en el fichero .htaccess del website o directamente a nivel general del servidor web en el fichero httpd.conf.

Lo primero que debemos verificar es que tenemos cargado en Apache el módulo mod_mime, que es el encargado de servir la directiva AddType.

# httpd -l | grep mime
  mod_mime.c

Una vez verificado, si quisieramos mapear las extensiones .html para servir contenido PHP, deberíamos añadir la siguiente directiva al fichero .htaccess:

AddType application/x-httpd-php .php .htm .html .shtml

Si tuvierais varias versiones de PHP instaladas en un mismo servidor, es posible que necesitéis especificar la versión del siguiente modo:

AddType application/x-httpd-php5 .php .htm .html .shtml

o

AddType application/x-httpd-php4 .php .htm .html .shtml

Esto lo podemos añadir también a nivel general en Apache, ya sea directamente en el httpd.conf o en el fichero php.conf junto con el resto de configuraciones de PHP. Si lo añadimos en httpd.conf reiniciamos el servicio tras hacerlo:

# /etc/init.d/httpd graceful

Ahora, si hacemos un fichero test.html con el siguiente contenido, al servirlo en el navegador web ejecutará el código PHP en lugar de mostrarlo como si fuera html puro y duro:

<h1>Prueba ejecutar PHP en fichero HTML</h1>
<? phpinfo ?>