eval(base64_decode()) in WordPress: Reading the Code You Found
By Glenn Lyvers · Updated · 5 min read
You opened a theme file or a plugin and found a line that runs off the right edge of the screen — a few hundred characters of what looks like random letters, wrapped in something like eval(base64_decode("...")). It’s alarming, and the instinct is either to delete it immediately or to paste it somewhere and run it to see what it does. One of those instincts is close to right. The other will infect whatever machine you run it on.
The functions that show up in obfuscated malware
Obfuscated PHP malware is built out of a small, stable set of primitives, and learning to recognize them is most of the skill. base64_decode turns text back into the original bytes. gzinflate and gzuncompress decompress. str_rot13 shifts letters. strrev reverses a string. preg_replace with the old /e modifier and create_function both execute what they’re given. And eval, assert, and variable functions — a variable holding a function name, then called as though it were one — are how the decoded result gets run.
Malware stacks them, usually three or four deep — decode, decompress, rot13, then eval — for one reason: pattern-matching scanners look for recognizable code, and each layer buries the payload one step further from anything a signature can match. The stacking itself is the strongest signal you have. A legitimate library might use base64 to embed a small image or a certificate. Almost none of them decode, decompress, reverse, and then execute the result.
Not every encoded string is malware
Worth being careful here, because deleting the wrong line breaks the site. Legitimate uses of base64_decode in real plugins do exist: embedded font or icon data, small inline images, license keys, encoded API payloads, and cached serialized values. What separates them from malware is what happens next — legitimate code decodes a string into a variable and then uses it as data. Malware decodes a string and immediately executes it.
So the question to ask isn’t “is this encoded?” but “is the decoded result being run?” If the line contains eval, assert, create_function, preg_replace with /e, or a variable being called as a function, you’re looking at execution. Two more context signals help: where it sits — top of functions.php, top of index.php, or a stray file in your uploads folder is very different from deep inside a well-known plugin’s library directory — and when it changed, since a modification date that doesn’t match the rest of the plugin is often the whole answer by itself.
Decode it safely
Never run it to find out what it does. That means no pasting into a live PHP file, no php -r with eval in it, and nothing that ends with the payload actually executing. Decode it as text, and read the text.
The safe approach is to reverse the layers one at a time with a tool that only transforms strings — CyberChef in a browser, or a local script that calls base64_decode and gzinflate and then prints the result instead of evaluating it. Work outward-in: decode the outermost layer, look at what you get, and if it’s another encoded blob, decode that too. Three or four passes usually lands you at readable PHP.
What you find at the bottom is rarely exotic. Most payloads are one of: a file-writing routine that recreates the infection elsewhere; a remote-fetch that pulls fresh instructions from a command-and-control domain; an authentication bypass that grants admin to anyone with the right cookie or query parameter; a spam-link injector that only responds to search engine crawlers; or a plain web shell that accepts commands over POST. Knowing which one matters, because it tells you what else to go check.
Deleting the line isn’t the fix
Removing the obfuscated block gets your site working again and feels like the end. It usually isn’t. That block was placed by something, and whatever placed it is generally still present — a backdoor in another file, a malicious scheduled task, a hidden admin account, or an injected row in the database. Sites that come back infected within a day almost always got cleaned at the line level and never at the cause level.
So after you remove it: search the whole install for the same pattern, not just the file you found it in — the same payload is nearly always in several places. Compare your core, plugin, and theme files against clean copies from the official source rather than eyeballing them. Check the database, since a decoded payload that writes to wp_options leaves nothing on disk at all. My guides on backdoor removal, malicious scheduled tasks, and database malware cover the three places it most often hides next.
When to stop and hand it over
Decoding one blob is a reasonable afternoon. Decoding the eleventh one, in a file that reappears each time you delete it, is a different job — at that point you’re not cleaning an infection, you’re negotiating with one. If the payload turns out to be a remote-fetch or a web shell, assume the attacker had arbitrary code execution on your server and treat every file and every credential as suspect, which is a much larger scope than one line in functions.php.
That full pass — every file, the database, the scheduled tasks, the credentials, and the way in — is what my malware removal service covers, and it’s usually cheaper than the third weekend you’d otherwise spend on it.
Common questions
Is base64_decode always a sign of malware?
No. Legitimate plugins use it for embedded images, fonts, certificates, and encoded API data. The distinguishing question is whether the decoded result is executed — if the line also contains eval, assert, create_function, preg_replace with the /e modifier, or a variable being called as a function, that’s execution, and that’s the red flag.
How do I decode it without running it?
Use a tool that only transforms text, never executes it — CyberChef in a browser is the easy option — or a small local script that decodes and prints the result rather than evaluating it. Peel the layers one at a time from the outside in. Never paste the payload into a live PHP file to ‘see what happens’.
Can I just delete the line and be done?
Deleting it restores the site, but it treats a symptom. The same payload is almost always duplicated in other files, and whatever placed it — a backdoor, a scheduled task, a compromised account — is usually still there. Search the whole install for the pattern, then check the database and the scheduled events before you call it clean.
Why didn’t my security plugin flag the obfuscated code?
Signature scanners match known patterns, and stacking encoders is specifically designed to defeat that: each layer moves the recognizable payload one step further from anything the signature list knows. Newer or lightly modified variants routinely pass a scan that would have caught the original.
What does it mean when several encoders are stacked together?
Decoding, decompressing, reversing, then evaluating — three or four layers deep — has essentially no legitimate purpose. Real code that needs encoded data uses one layer and then treats the result as data. Stacked layers ending in execution is one of the most reliable malware signals you’ll find in PHP.