Bali, Indonesia
Insights

Auditing .htaccess Files for Injected Redirects After a cPanel Server Compromise

October 20, 2026
0:00
A server gets patched, the obvious malware gets removed, passwords get rotated, and everyone breathes out. Then the tickets start again: visitors from Google land on a pharmacy spam site, or phone users get bounced to a fake prize page, while the site looks fine to its owner. In most of these cases the leftover is a few lines in a .htaccess file, and it is sitting in more places than anyone checked. This comes up a lot after server-level incidents like CVE-2026-41940, the cPanel and WHM authentication bypass caused by CRLF injection in the login path. Attackers who exploited it could forge session data and get root-level access to WHM, which means every account on the box was writable. Below are the questions that usually come up when cleaning up the redirect part of that mess.
Because the main cleanup usually targets what is loud: webshells, suspicious processes, rogue cron jobs, new WHM users. A modified .htaccess file does none of those things. It does not run as a process, it does not show up in a malware scanner that focuses on PHP, and it does not consume resources. Apache or LiteSpeed simply reads it on each request and follows the instructions. The other reason is scale. With root access, an attacker does not need to pick one site. A script can append rules to every .htaccess under every home directory in seconds, including directories nobody thinks about, like old addon domain folders, staging copies, or the account home directory one level above public_html. Rules in a parent directory apply to everything below it, so fixing the file inside public_html can leave the real culprit untouched.
Injected rules are almost always conditional. A RewriteCond line (a condition that must match before the next RewriteRule runs) checks something about the visitor, and only then does the redirect fire. Common conditions look at %{HTTP_REFERER}, so only people arriving from a search engine get redirected, or at %{HTTP_USER_AGENT}, so only mobile browsers or search engine crawlers are affected. Some also skip visitors who have a WordPress login cookie, which conveniently hides the problem from the admin. The owner types the URL directly on a desktop browser, matches none of the conditions, and sees a normal site. That mismatch is why these tickets often get closed as "cannot reproduce." When testing, a support engineer should send a request that looks like the affected visitor: a mobile user agent and a search engine referer. The curl example in the next section does that and shows only the response headers, so you can see a 301 or 302 with a foreign Location without following it.
Start read-only. You want a list of files to review, not an automatic fix, because legitimate .htaccess files also use these variables. Security plugins block bad bots by user agent, caching plugins check cookies, and some sites intentionally redirect mobile users. A blind delete of every rule containing HTTP_USER_AGENT will break real sites.
BASH
# 1) List .htaccess files that contain referer or user-agent conditions (read-only)
grep -rlE --include='.htaccess' 'HTTP_(REFERER|USER_AGENT)' /home

# 2) Show the matching lines with context so you can judge them
grep -rnE -A2 --include='.htaccess' 'HTTP_(REFERER|USER_AGENT)' /home

# 3) .htaccess files modified in the last 14 days (adjust to your incident window)
find /home -type f -name '.htaccess' -mtime -14 -ls

# 4) Files with the immutable attribute set (look for 'i' in the flags)
find /home -type f -name '.htaccess' -exec lsattr {} + 2>/dev/null | grep -E '^[^ ]*i'

# 5) Reproduce what a mobile visitor from a search engine sees (headers only)
curl -sI -A 'Mozilla/5.0 (Linux; Android 14; Mobile)' -e 'https://www.google.com/' 'https://example.com/'
When reading the output, the things that stand out are a RewriteRule whose target is a full external URL on a domain the customer does not own, conditions listing search engines or mobile keywords, and rules placed far below the normal content, sometimes after a long run of blank lines so they are off screen in a file editor. Also check for ErrorDocument lines pointing to an external domain, which turn every 404 into a redirect. If your accounts live outside /home, for example on a second disk, add those paths too. And keep in mind that with root access an attacker could also touch global web server include files, whose location depends on your Apache build; the per-account scan will not cover those.
If the lines reappear within minutes or hours, something on the server is rewriting them. The usual candidates are a leftover PHP backdoor that restores the file on each request, a cron job under one of the accounts or under root, or a process that was never killed. Removing the redirect without finding the writer just starts a loop. The modification time of the restored file is a useful clue: match it against access logs and cron schedules to see what ran at that moment. The immutable flag check above matters for the opposite reason. Attackers sometimes run chattr +i on the file so that cPanel's File Manager, FTP, and even the account user cannot edit it, and the customer concludes their edit "didn't save." Only root can remove that flag with chattr -i. Before you do, make a copy of the file for your incident notes, because once you clean it the evidence is gone. It is also worth being honest about the bigger picture. A root-level compromise means you cannot fully trust anything on the server, including system binaries and other config files. Cleaning .htaccess fixes the visible symptom, but whether the server should be cleaned in place or rebuilt and migrated is a separate decision, and the patching side of that is covered in Mitigating cPanel CVEs: Low-Level Triage, Runtime Patching, and Multi-Tenant Isolation.
The simplest habit is a baseline. Once a server is clean, record a checksum of every .htaccess file along with its path, and store that list somewhere off the server. After any future security incident, or on a regular schedule, generate the list again and compare. Anything new or changed goes on a review list. That turns "read every file on the server" into "read the handful that changed," which is realistic for a support team on a busy shift. It also helps to make the audit a standard line in your incident checklist, right next to checking cron jobs and new WHM users, so it does not depend on someone remembering. For junior staff, the key lessons are to test with a mobile user agent and search referer before closing a redirect ticket, to check parent directories and not only public_html, and to never mass-delete rules without reading them.
It reduces the chance of the initial break-in rather than cleaning up after it. A flaw like CVE-2026-41940 needs the attacker to reach the WHM login path, so limiting who can reach the administrative ports narrows the exposure considerably while a patch is pending. The trade-offs between doing that at the panel level and at the firewall are discussed in Should You Restrict WHM Ports with Host Access Control or a Firewall?. It will not undo rules that were already planted, which is why the audit still needs to happen. The practical takeaway: after any incident where an attacker may have had root or broad account access, assume .htaccess files were touched until you have proven otherwise. Scan every account read-only, test the way a real visitor arrives, look for the process that rewrites the files, and keep a checksum baseline so the next audit takes minutes instead of a day.