After discovering malware, the most common and dangerous approach is to immediately delete the malicious files you see. The danger lies in the fact that while you think it’s cleaned up, the backdoor remains, and the site gets infected again the next day—often more stealthily than before.
Cleanup must follow a strict process; the order cannot be reversed. Below are five steps, each explaining what to do and why.
Step 1: Isolation (Stop the Bleeding First)
The goal here is not to “fix” the issue but to prevent further spread. Skipping this step means everything done afterward may be in vain.
# Option A: Serve a maintenance page (preferred if brief downtime is acceptable)
# Temporarily return a maintenance page via nginx
location / { return 503; }
# Option B: Restrict access to yourself only (needed for ongoing investigation)
location / {
allow YourIP;
deny all;
}
# Option C: Temporarily stop the web service
systemctl stop nginx
# Perform three actions simultaneously
# ① Preserve the scene (essential for later post-mortem analysis)
cp -a /var/www/html /root/backup-infected-$(date +%F)
# ② Disconnect external connections (if mining or outbound connections are confirmed)
iptables -A OUTPUT -p tcp --dport 3333 -j DROP
# ③ Record current processes for comparison
ps aux > /root/ps-$(date +%F).txt
Preserving the scene is crucial. After cleanup, you may want to know “how did they get in?” At that point, your only clues will be the copies of infected files and the process snapshots from that time.
Step 2: Locating (Find All, Not Just One)
# ① Search by time (most effective)
find /var/www -type f -name '*.php' -mtime -7 -print
# ② Search by characteristics
grep -rEl 'eval(|base64_decode|assert(|system(|shell_exec|passthru|exec('
/var/www --include='*.php'
# ③ Verify WordPress core files (directly lists modified files)
wp core verify-checksums
# ④ These two files must be checked separately (PHP scanners often miss them)
cat /var/www/html/.htaccess
cat /var/www/html/.user.ini
# ⑤ Look for persistence mechanisms
crontab -l
ls -l /etc/cron.d/ /var/spool/cron/
systemctl list-unit-files --state=enabled
ls -la /tmp
# ⑥ Check accounts
awk -F: '$3>=1000 {print $1}' /etc/passwd
last -20
# ⑦ Database level (WordPress content often has injected JS)
mysql -e "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%';" dbname
Step 3: Removal (In Order, Do Not Skip)
- Clear scheduled tasks—If not done first, malicious files will be immediately rewritten after deletion.
- Remove injection items from .user.ini / .htaccess
- Remove backdoor accounts (system accounts + site admin accounts)
- Delete malicious files (including those in
/tmp) - Remove injected content from the database
- Change all passwords: SSH / FTP / Database / Site Admin
# Example of changing passwords
passwd # System user
wp user update 1 --user_pass='NewStrongPassword' # WP Admin
# Tighten permissions uniformly after cleanup
find /var/www/html -type d -exec chmod 755 {} ;
find /var/www/html -type f -exec chmod 644 {} ;
Step 4: Verification (Confirm It Is Truly Clean)
# Rescan for dangerous functions
grep -rEl 'eval(|base64_decode|assert(' /var/www --include='*.php'
# Verify core files
wp core verify-checksums
# Confirm scheduled tasks are clean
crontab -l
# Check for abnormal outbound connections
netstat -antp | grep ESTABLISHED
# Check page source for unknown scripts
curl -s https://example.com/ | grep -iE '<script|iframe'
Step 5: Recheck and Hardening (Review Again the Next Day)
- Must scan again the next day: To confirm there is no resurgence. Being clean on the same day proves nothing.
- Identify and block the intrusion vector: Weak password? Plugin vulnerability? Upload directory allowing PHP execution?
- Update: OS, Web server, CMS, and plugins.
- Clear CDN cache: Otherwise, users will still see the infected page.
# Recheck
find /var/www -type f -name '*.php' -mtime -1 -print
Decision Tree
Should you reinstall the OS?
├─ Rootkits found / System commands replaced → Must reinstall; cleanup is unreliable
├─ Attacker gained root access → Recommended to reinstall
├─ Unknown system accounts or SSH keys added → Recommended to reinstall
├─ Confined to Web directory, privileges not escalated → Can follow cleanup process
└─ Unable to determine scope of impact → Reinstall (cost is lower than secondary incidents)
Infected again after cleanup?
└─ Almost certainly due to incomplete cleanup of these three:
① Scheduled tasks ② .user.ini / .htaccess ③ Passwords not changed
Scenario Analysis: Recurrence Despite Three Cleanups Indicates a Skipped Step
Symptom: A site was found with backdoor files. After cleanup, the same symptoms appeared a week later.
Cleaning again, scanning with different tools, and restarting services still resulted in recurrence.
- Stop looking at “whether backdoors exist” and start looking at “how they return” → This time, do not clean immediately.
Instead, record the paths and modification times of all suspicious files. - Analyze timestamps for patterns → Modification times of suspicious files are highly concentrated,
indicating batch writing during a single intrusion; however, new files appearing after cleanup point to a specific fixed time. - Trace the automatic trigger mechanism at that fixed time → A scheduled task executes a command periodically,
downloading and executing content from an external source. This is the cause of recurrence: cleanup only deleted the result, while the triggering mechanism remained active. - Disable the trigger mechanism before cleaning → Remove the scheduled task. Only after confirming automatic rewriting stops should file deletion begin.
- Investigate other potential channels → Check startup items, accounts, and keys
to ensure there are no second entry points. - Recheck the next day using the same scanning method → If no new suspicious files appear,
then this round of cleanup is truly complete.
# First, log: Record paths and times of suspicious files, do not rush to delete
find /var/www -name '*.php' -newermt '2026-09-01' -printf '%TY-%Tm-%Td %TH:%TM %pn' | sort
# Find automatic trigger mechanisms (this step must precede file deletion)
crontab -l -u root
ls -la /etc/cron.d/ /var/spool/cron/
cut -d: -f1 /etc/passwd | xargs -I{} crontab -l -u {} 2>/dev/null
# Recently added services in startup items
systemctl list-unit-files --type=service | grep enabled
ls -lt /etc/systemd/system/ | head
# Scan again the next day using the same method for comparison
Root Cause: Incorrect cleanup order.
Deleting files without disabling the automatic trigger mechanism means only handling the result.
As long as the mechanism exists, it will rewrite the files at the next trigger moment.
Two Key Takeaways:
- The correct order is “Disable Mechanism → Delete Files → Verify Channels”.
Reversing the order leads to a cycle of repeated cleanup and recurrence, which may seem like “the attacker is sophisticated,” but is actually a procedural error. - The criterion for “Cleanup Complete” is a clean re-scan the next day, not just finding nothing on the same day.
Finding nothing on the same day only means it was clean at that moment. Verifying recurrent backdoors requires a window spanning at least one trigger cycle.
Outputs and Consequences of Skipping Steps
| Step | Expected Output | Consequence of Skipping |
|---|---|---|
| Isolation | Site stopped serving public traffic or rate-limited | Continued writing during cleanup; never finishes |
| Locating | List of backdoor files (with timestamps) | Only deleted the one found; new ones appear the next day |
| Removal | Files deleted/replaced, accounts/keys rotated | One remaining entry point ruins all efforts |
| Verification | Full site scan results + homepage source confirmation | Assumed clean, but hidden elements remain |
| Recheck | Comparison results from next-day scan | Uncertainty about whether it was truly cleaned |
The step most often skipped is not locating, but rechecking. Announcing completion after a same-day scan finds nothing,
only to see the same symptoms a week later—this is rarely “attacked again,” but rather indicates incomplete initial cleanup.
A practical criterion during the locating phase: Check if file modification times cluster together.
Backdoor files are usually written in bulk during a single intrusion, so timestamps will be highly concentrated.
Sorting by time is far more efficient than guessing by filename.
# Find PHP files modified in the last 7 days, sorted by time descending
find /var/www -name '*.php' -mtime -7 -printf '%T@ %TY-%Tm-%Td %TH:%TM %pn' | sort -rn | head -50
# Check only content modifications, ignoring permission changes: use -newermt to set start date
find /var/www -name '*.php' -newermt '2026-10-01' -printf '%TY-%Tm-%Td %TH:%TM %pn' | sort
8 Common Pitfalls
- Skipping isolation and directly deleting files: Backdoors remain active during cleanup, rewriting files as they are deleted.
- Failing to preserve the scene: No evidence left for post-mortem analysis of “how they got in.”
- Scanning only PHP files:
.user.iniand.htaccessare often missed by scanners but are common persistence points. - Forgetting to check the database: Injected scripts in WordPress content cannot be removed by file deletion alone.
- Not changing passwords: Leaving an old password allows attackers to return easily.
- No next-day recheck after cleanup: Being clean on the same day does not rule out resurrection mechanisms.
- Not clearing CDN cache: Even if the origin server is clean, users still see the infected cached page.
- Restoring backup program files during OS reinstallation: Bringing the backdoor back with the restored files.
Real Customer Feedback (From Cloud Market Reviews and Inquiries for Similar Products)
The following excerpts are taken from real buyer reviews and inquiries for similar operations and maintenance services on Alibaba Cloud / Tencent Cloud / Huawei Cloud Cloud Markets (providers include our store and competitors, provided for reference only, not fictional tickets from this site):
1****8 Rating: 5.0 2019-10-29 22:35 Good 1
1****1 Rating: 5.0 2020-06-18 11:50 1****1 Rating: 5.0 2020-04-26 18:46 1****1 Rating: 5.0 2020-04-26 18:45 1****1 Rating: 5.0 2020-04-26 18:45 1****1 Rating: 5.0 2020-04-26 18:45 1****1 Rating: 5.0 2020-04-26 18:44 He****She Rating: 5.0 2019-09-26 09:47