The file was called singIe.php. In a terminal listing next to sidebar.php, header.php, and footer.php, it looked perfectly normal — just another WordPress template file. But that lowercase l in “single” wasn’t an l. It was a capital I. And behind that one-pixel difference was a command-execution webshell with access to system(), shell_exec(), and passthru().
This wasn’t a one-off trick. Over the course of two weeks, the attacker deployed seven waves of fake WordPress themes, each a near-perfect clone of a legitimate theme, each carrying a single backdoor file with a homoglyph filename. The technique is elegant, low-tech, and devastatingly effective against anyone who investigates by reading filenames rather than comparing them.
What Is a Homoglyph?
A homoglyph is a character that looks identical or nearly identical to another character. In the context of Latin script and monospace terminal fonts, several pairs are visually indistinguishable at normal reading speed:
l (lowercase L) vs I (uppercase i)
0 (digit zero) vs O (uppercase o)
1 (digit one) vs l (lowercase L)
rn vs m
Homoglyph attacks have been used in phishing domains for years (paypa1.com vs paypal.com). But this case applied the same principle to filenames on disk — not to trick a user clicking a link, but to trick a security analyst reviewing a directory listing during incident response.
The Attack Pattern
Each of the seven waves followed the same playbook:
- Clone a legitimate theme. The attacker copied the site’s active theme — a popular starter theme — with all its files intact. The clone used a randomized directory name (a word plus a long numeric suffix) to look like a development or staging copy.
- Add one malware file with a homoglyph name. A single PHP file that looked like a standard WordPress template but contained a webshell.
- Stage in the application’s temp directory. Each clone was placed in the app’s
tmp/folder before being deployed towp-content/themes/.
Here’s what the attacker deployed across the seven waves:
Wave 1 │ sldebar.php ← "sldebar" not "sidebar" (missing 'i')
Wave 2 │ wp-config.php ← fake 187-byte wp-config (legitimate name, wrong content)
Wave 3 │ singIe.php ← capital I instead of lowercase l
Wave 4 │ 4O4.php ← letter O instead of digit 0
Wave 5 │ archlve.php ← lowercase L instead of i in "archive"
Wave 6 │ lndex.php ← lowercase L instead of i in "index"
Wave 7 │ wp-config.php ← another fake wp-config with marker
Why This Works
Consider what a typical directory listing looks like during investigation. You SSH into the server, cd into the theme folder, and run ls:
$ ls wp-content/themes/starter-theme/
404.php comments.php header.php singIe.php
archive.php footer.php index.php sidebar.php
assets/ functions.php screenshot.png style.css
At a glance, singIe.php reads as single.php. Your brain autocorrects it. You’re scanning for files that shouldn’t be there — random strings, test- prefixes, .suspected extensions — not files that look like they belong. The attacker is counting on exactly this cognitive shortcut.
The same applies to every other homoglyph in the campaign:
4O4.php → reads as "404.php" (every theme has one)
archlve.php → reads as "archive.php" (standard template)
lndex.php → reads as "index.php" (exists in every directory)
Even automated scanners that check for “known suspicious filenames” will miss these. singIe.php isn’t on any blocklist. It’s not a random string. It’s a legitimate filename with one character wrong.
Inside the Payload
The most complete webshell recovered from the campaign (475 bytes before the server’s security software cleaned it) revealed the function arsenal through obfuscated variable declarations:
$request_approved = "hex\x32\x62\x69\x6E"; // hex2bin
$settings1 = "sys\x74em"; // system
$settings2 = "she\x6C\x6C_\x65xe\x63"; // shell_exec
$settings3 = "e\x78\x65c"; // exec
$settings4 = "pa\x73\x73thru"; // passthru
$settings5 = "pop\x65n"; // popen
$settings6 = "s\x74\x72e\x61m_\x67\x65t_contents"; // stream_get_contents
$settings7 = "\x70\x63lo\x73e"; // pclose
The actual execution logic — the part that received commands via GET/POST parameters and routed them through these functions — had been removed by the security scanner. But the variable declarations paint a clear picture: this was a full command-execution webshell supporting seven different PHP functions for running system commands, with hex2bin for decoding incoming payloads.
The obfuscation itself is minimal — hex-escaped characters mixed with plaintext. Just enough to evade simple grep patterns looking for literal strings like shell_exec or passthru, but trivial to decode. The attacker was relying on the homoglyph filename for primary evasion, not the code obfuscation.
The Multi-Wave Strategy
Why seven waves instead of one? Each wave served as a redundant re-entry point. If the security scanner cleaned one wave, the next was already staged or would be deployed within days. The timeline from this case:
Aug 7 → Wave 1 deployed (sldebar.php)
Aug 8 → Wave 2 deployed (fake wp-config.php)
Aug 9 → Waves 3-4 deployed (singIe.php, 4O4.php)
Aug 12 → Wave 5 deployed (archlve.php)
Aug 17 → Waves 6-7 deployed (lndex.php, fake wp-config.php)
Each wave also used a different HTML comment marker as a tracking tag — an 8-character alphanumeric string embedded at the top of every file in that wave (<!--Q5dY927H-->, <!--Y41EuDGG-->, etc.). This allowed the attacker to identify which wave a particular file belonged to and whether it had been cleaned.
Three fake plugin directories were deployed alongside the themes: wp-security-helper, a11y-image-attributes-fix, and sticky-block. Notice the naming — two of these sound like legitimate accessibility and performance plugins. A plugin called wp-security-helper is exactly the kind of name an admin wouldn’t think twice about seeing in their plugin list.
Detection: What Actually Works
Visual inspection of filenames is unreliable against this technique. Here’s what does work:
1. WordPress Core Checksum Verification
wp core verify-checksums
This compares every file in the WordPress installation against the official checksums for that version. It won’t catch malware in themes or plugins, but it will flag any file in the webroot that shouldn’t exist — and singIe.php is not a WordPress core file. The command explicitly reported: Warning: File should not exist: wp-salt.php. A fake wp-settings.php or wp-Ioad.php would be caught the same way.
2. Theme/Plugin Integrity Comparison
For themes and plugins, compare the installed files against the official distribution:
# List all PHP files in the theme and compare against known file list
diff <(find /path/to/theme/ -name "*.php" -printf '%f\n' | sort) \
<(echo -e "404.php\narchive.php\ncomments.php\nfooter.php\n..." | sort)
Any file that exists on disk but not in the official theme distribution is suspicious, regardless of how normal its name looks.
3. Byte-Level Filename Inspection
When you suspect homoglyph tricks, dump filenames as hex:
# Compare two "identical" filenames at the byte level
echo -n "singIe.php" | xxd
# 7369 6e67 4965 2e70 6870 → 0x49 = 'I' (uppercase)
echo -n "single.php" | xxd
# 7369 6e67 6c65 2e70 6870 → 0x6c = 'l' (lowercase)
The difference — 0x49 vs 0x6c — is invisible in a terminal listing but immediately obvious in hex.
4. Content-Based Scanning
Scan for behavior, not filenames:
grep -rlE "eval\s*\(|base64_decode|shell_exec|passthru|system\s*\(" \
/path/to/themes/ --include="*.php"
A file named singIe.php containing shell_exec will be caught regardless of its name. The homoglyph only defeats visual review — not content analysis.
Key Takeaways
This campaign was not sophisticated in its payload. The webshell was a standard command-execution backdoor with basic hex obfuscation. The server’s security software caught and cleaned every instance. What made it effective was the delivery mechanism:
- Homoglyph filenames defeat the most common form of investigation: a human reading a directory listing.
- Cloning a legitimate theme means the malware sits among real files that belong there, not in an obviously fake directory.
- Multi-wave deployment ensures persistence even when individual waves are detected and cleaned.
- Plausible naming for fake plugins (
wp-security-helper,a11y-image-attributes-fix) exploits the assumption that plugins with boring names are safe.
The defense is straightforward: never trust filenames. Use checksum verification for core files. Compare theme and plugin contents against known-good distributions. Scan for malicious behavior in code, not malicious-looking names on disk. The attacker in this case bet everything on the gap between what a file is called and what it actually does — and for two weeks, across seven deployment waves, that bet paid off.