“Cloudflare” falso (ClickFix) que te hace instalar malware: cómo bloquearlo en WordPress y limpiar tu sitio

TL;DR: está circulando una campaña (“ClickFix”) que muestra una pantalla falsa de Cloudflare pidiéndote presionar Windows + R y pegar un comando msiexec para instalar un programa “necesario”. Es un engaño. Cloudflare nunca te pedirá ejecutar comandos fuera del navegador.
Si tu WordPress redirige a dominios como defensecorex.com o llama a metricaltic.com, tu sitio probablemente tiene inyección de JavaScript (plugin/tema/snippet/archivo modificado). Abajo tienes un MU-plugin que bloquea las cargas maliciosas (sin romper Elementor/admin) y, después, una guía de limpieza completa.

Cómo funciona el engaño

Los atacantes comprometen sitios (a menudo WordPress) e insertan un “Cloudflare Check” falso. La página imita Turnstile/CAPTCHA y pide presionar Win+R para ejecutar un comando que descarga/instala un MSI malicioso. Es ingeniería social: el usuario se instala el malware. Esta táctica se conoce como ClickFix y se ha observado ampliamente contra webs y usuarios finales en 2024–2025. Cloudflare no solicita ejecutar comandos; cualquier “verificación” real ocurre dentro del navegador.

Por qué es peligroso (msiexec)

msiexec.exe es el instalador de Windows; los atacantes lo abusan para descargar e instalar paquetes en silencio (/qn, /passive). Eso permite desplegar stealers (e.g., Lumma, MetaStealer, LegionLoader) y tomar credenciales/sesiones.

Bloqueo inmediato: MU-plugin (solo front, no rompe Elementor)

Este MU-plugin impide que WordPress imprima/inyecte scripts remotos hacia los hosts maliciosos en el front (no corre en admin/editor). También bloquea inserciones dinámicas de <script> en tiempo de ejecución. Ajusta la lista si detectas más dominios.

Guarda como wp-content/mu-plugins/bm-block-bad-scripts.php:

<?php
/*
Plugin Name: BM Guard (Front + Elementor) [MU]
Plugin URI: https://devcristian.com
Description: Bloquea IOCs (defensecorex/metricaltic/daftarhargapipa) en frontend y en el editor de Elementor; corta HTTP saliente, limpia <script> maliciosos y loguea con stack en consola.
Version: 1.4.0
Author: devcristian
Author URI: https://devcristian.com
*/

if (!defined('ABSPATH')) exit;

/** ================== Config ================== */
function bm_iocs() {
  return ['defensecorex.com','metricaltic.com','daftarhargapipa.com'];
}
function bm_site_host(){
  static $h = null;
  if ($h !== null) return $h;
  $h = parse_url(home_url(), PHP_URL_HOST);
  return $h ? strtolower($h) : '';
}
function bm_host_of($url){
  $h = parse_url($url, PHP_URL_HOST);
  return $h ? strtolower($h) : '';
}
/* Compat helpers (PHP < 8) */
function bm_starts_with($hay,$nee){ return $nee==='' || strncmp($hay,$nee,strlen($nee))===0; }
function bm_ends_with($hay,$nee){ if($nee==='') return true; $l=strlen($hay)-strlen($nee); return $l>=0 && strpos($hay,$nee,$l)!==false; }
function bm_is_same_or_sub($host,$root){ return $host===$root || ($root && bm_ends_with($host,'.'.$root)); }
function bm_in_iocs($host){
  foreach (bm_iocs() as $d) { if ($host===$d || bm_ends_with($host,'.'.$d)) return true; }
  return false;
}

/** ================== Contextos ================== */
/* Frontend normal */
function bm_is_front_ctx(){
  if (defined('REST_REQUEST') && REST_REQUEST) return false;
  if (wp_doing_ajax()) return false;
  return !is_admin(); // front puro
}
/* Editor/preview de Elementor dentro del admin */
function bm_is_elementor_admin_ctx(){
  if (!is_admin()) return false;
  // Editor: post.php?post=ID&action=elementor
  if (isset($_GET['action']) && $_GET['action'] === 'elementor') return true;
  // Vista previa: elementor-preview
  if (isset($_GET['elementor-preview'])) return true;
  return false;
}

/** ================== 1) Bloqueo HTTP saliente (server-side) ==================
 * Evita phone-home desde plugins/temas hacia IOCs (también en admin/elementor).
 */
add_filter('pre_http_request', function($pre, $args, $url){
  $h = bm_host_of($url);
  if ($h && bm_in_iocs($h)) {
    return new WP_Error('bm_blocked', 'BM: blocked server-side -> '.$h);
  }
  return $pre;
}, 10, 3);

/** ================== 2) Limpiar HTML (quitar <script src> IOCs) ==================
 * Se aplica en frontend y en páginas de visualización/preview (no afecta REST/AJAX).
 */
function bm_start_buffer_cleaner(){
  if (bm_is_front_ctx() || bm_is_elementor_admin_ctx()) {
    ob_start(function($html){
      if (!$html) return $html;

      // a) Eliminar <script src> que apunten a IOCs (por si se imprimieron fuera de las colas)
      if (preg_match_all('#<script\b[^>]*\bsrc=["\']\s*(https?:)?//([^/"\']+)[^"\']*["\'][^>]*>\s*</script>#i', $html, $m, PREG_SET_ORDER)) {
        foreach ($m as $mm) {
          $full = $mm[0]; $host = strtolower($mm[2]);
          if (bm_in_iocs($host)) {
            $html = str_replace($full, "<!-- BM removed IOC script: //{$host}/... -->", $html);
          }
        }
      }

      // b) Neutraliza y LOGUEA (luego en JS) posibles URLs defensecorex ya impresas en HTML
      //    (no rompemos el HTML aquí, solo lo dejamos; el runtime las intercepta)

      return $html;
    });
  }
}
add_action('template_redirect', 'bm_start_buffer_cleaner', 0);
add_action('admin_init', 'bm_start_buffer_cleaner', 0); // para editor Elementor en admin

/** ================== 3) Cortar scripts encolados a IOCs (WP_Scripts) ==================
 * También en admin cuando estás en el editor de Elementor (no toca otros screens).
 */
function bm_dequeue_ioc_scripts(){
  $apply = bm_is_front_ctx() || bm_is_elementor_admin_ctx();
  if (!$apply) return;

  global $wp_scripts; if (!$wp_scripts) return;

  foreach ($wp_scripts->registered as $handle => $obj) {
    $src = isset($obj->src) ? $obj->src : '';
    if (!$src) continue;
    $abs = (bm_starts_with($src,'http://') || bm_starts_with($src,'https://'))
        ? $src
        : trailingslashit($wp_scripts->base_url).ltrim($src,'/');
    $h = bm_host_of($abs);
    if ($h && bm_in_iocs($h)) {
      wp_dequeue_script($handle);
      wp_deregister_script($handle);
      // Sustituimos por un aviso en consola (para que el DOM no falle por dependencias)
      add_action(bm_is_front_ctx() ? 'wp_footer' : 'admin_footer', function() use ($abs,$handle){
        echo "<script>console.warn('[BM] BLOCKED enqueued script IOC:', ".wp_json_encode($abs).", 'handle:', ".wp_json_encode($handle).");</script>";
      }, 9999);
    }
  }
}
add_action('wp_print_scripts',    'bm_dequeue_ioc_scripts', 999);
add_action('admin_print_scripts', 'bm_dequeue_ioc_scripts', 999);

/** ================== 4) Runtime (JS) con stack trace ==================
 * Inyecta en <head> del front y del editor de Elementor:
 * - bloquea fetch/XHR/WebSocket y redirecciones hacia IOCs
 * - loguea <script> dinámicos a IOCs
 * - decodifica defensecorex (domain/link base64) si aparece
 * NOTA: si tu CSP bloquea inline scripts, cambia a una versión que encole un .js de este plugin.
 */
function bm_print_runtime_js(){
  $apply = bm_is_front_ctx() || bm_is_elementor_admin_ctx();
  if (!$apply) return;

  $site = bm_site_host();
  $iocs = wp_json_encode(bm_iocs());
  ?>
<script>
(function(){
  try{
    var SITE=<?php echo json_encode($site); ?>;
    var IOCs=new Set(<?php echo $iocs; ?>);
    function hostOf(u){ try{ return new URL(u, document.baseURI).hostname.toLowerCase(); }catch(e){ return ''; } }
    function isIOC(h){ if(!h) return false; for (var b of IOCs){ if (h===b || h.endsWith('.'+b)) return true; } return false; }
    function trace(kind, url){
      try{ throw new Error('[BM] STACK '+kind+' -> '+url); }
      catch(e){ console.warn('[BM] BLOCKED '+kind+' ->', url, '\n'+(e.stack||e)); }
    }
    // fetch
    if (window.fetch){
      var _f=window.fetch;
      window.fetch=function(i,o){
        var url=(typeof i==='string')? i : (i && i.url ? i.url : '');
        var h=hostOf(url);
        if (isIOC(h)){ trace('fetch', url); return Promise.reject(new Error('BM blocked '+url)); }
        return _f.apply(this, arguments);
      };
    }
    // XHR
    if (window.XMLHttpRequest){
      var _o=XMLHttpRequest.prototype.open;
      XMLHttpRequest.prototype.open=function(m,u){
        var h=hostOf(u);
        if (isIOC(h)){ trace('XHR', u); try{this.abort();}catch(e){} throw new Error('BM blocked '+u); }
        return _o.apply(this, arguments);
      };
    }
    // WebSocket
    if (window.WebSocket){
      var _WS=window.WebSocket;
      window.WebSocket=function(u,p){
        var h=hostOf(u.replace(/^ws(s)?:\/\//,'https://'));
        if (isIOC(h)){ trace('WS', u); throw new Error('BM blocked '+u); }
        return new _WS(u,p);
      };
    }
    // <script> dinámicos
    ['appendChild','insertBefore'].forEach(function(fn){
      var orig=Node.prototype[fn];
      Node.prototype[fn]=function(el){
        try{
          if (el && el.tagName==='SCRIPT' && el.src){
            var h=hostOf(el.src);
            if (isIOC(h)){ trace('dynamic<script>', el.src); return el; }
          }
        }catch(e){}
        return orig.apply(this, arguments);
      };
    });
    // Redirecciones
    var _open=window.open; window.open=function(u,n,s){ var h=hostOf(u); if (isIOC(h)){ trace('window.open', u); return null; } return _open.apply(this, arguments); };
    try{
      var L=Object.getPrototypeOf(window.location);
      if(L && L.assign){ var _a=L.assign; L.assign=function(u){ var h=hostOf(u); if (isIOC(h)){ trace('location.assign', u); return; } return _a.call(this,u); }; }
      if(L && L.replace){ var _r=L.replace; L.replace=function(u){ var h=hostOf(u); if (isIOC(h)){ trace('location.replace', u); return; } return _r.call(this,u); }; }
    }catch(e){}
    // defensecorex decoder (base64 query params)
    function scanDefensecorex(s){
      try{
        var m=s && s.match && s.match(/https?:\/\/defensecorex\.com\/\?[^"'\s<>]+/i);
        if(!m) return;
        var u=new URL(m[0]);
        var d=u.searchParams.get('domain'), l=u.searchParams.get('link');
        var decD=d?atob(decodeURIComponent(d)):null;
        var decL=l?atob(decodeURIComponent(l)):null;
        console.warn('[BM] defensecorex detected -> raw:', u.href, 'domain_dec:', decD, 'link_dec:', decL);
      }catch(e){}
    }
    scanDefensecorex(location.href);
    try{
      var mo=new MutationObserver(function(list){
        list.forEach(function(r){
          r.addedNodes && r.addedNodes.forEach(function(n){
            try{
              if(n.nodeType===1 && n.tagName==='SCRIPT' && !n.src && n.textContent){ scanDefensecorex(n.textContent); }
            }catch(e){}
          });
        });
      });
      mo.observe(document.documentElement, {childList:true,subtree:true});
    }catch(e){}
  }catch(e){}
})();
</script>
<?php
}
add_action('wp_head',    'bm_print_runtime_js', 0); // Front muy temprano
add_action('admin_head', 'bm_print_runtime_js', 0); // Editor Elementor en admin

/** ================== 5) Reporte compacto al final (opcional) ==================
 * Solo imprime si hubo algo bloqueado por server-side (pocas veces necesario, puedes comentar si no quieres nada)
 */
add_action('wp_footer', function(){
  // intencionalmente vacío: los bloqueos se muestran en vivo con stack (fetch/xhr/ws/script/open/assign/replace)
}, 9999);
add_action('admin_footer', function(){
  // idem para editor Elementor
}, 9999);

este bloqueo no sustituye la limpieza. Úsalo para cortar la redirección hoy mismo y ganar tiempo para el saneamiento completo (siguiente sección). Reportes recientes confirman que estas campañas cambian de dominio con frecuencia; revisa periódicamente tu consola de navegador y amplia la lista si aparece un host nuevo

Limpieza profunda de WordPress y BD (lo recomendado)

Aislar y actualizar

  • Poner el sitio en mantenimiento.
  • Reinstalar core de WordPress y actualizar todos los plugins/temas (elimina los que no uses).

Revisa tu computador (por si ejecutaste el comando)

  • Microsoft Defender escaneo sin conexión y una segunda opinión (Malwarebytes, etc.).
  • Comprueba tareas programadas, programas de inicio y conexiones salientes anómalas.
  • Si viste un instalador “Cloudflare.msi/PDF”, trátalo como malicioso.

Preguntas frecuentes

¿Cloudflare puede pedirme Win+R o copiar un comando?

No. Cualquier verificación real se hace en el navegador (checkbox, desafío visual). Si te piden ejecutar comandos, es falso.

¿Por qué mi sitio redirige solo a algunos usuarios?

Porque muchas inyecciones son condicionales (solo para Chrome/ciertos países, 1 de cada N visitas). A veces no verás nada en curl pero sí en el navegador.

¿Basta con el MU-plugin?

Es una mitigación inmediata. La limpieza de archivos/BD y el hardening siguen siendo indispensables; estas campañas rotan dominios y técnicas.

Compartir esta información