DocsMalware scan

Malware scan

Scan hosting accounts for webshells, backdoors and injected code, quarantine or ignore findings, restore a file, and clean up a hacked website step by step.

Malware scan checks the website files of hosting accounts for webshells, backdoors, obfuscated PHP and injected JavaScript. Every account is scanned automatically once a week, and administrators, resellers and customers can start a scan at any time. Findings can be moved to a quarantine that the account cannot reach, or marked as false positives.

Who can use it

Role What they see and do
Administrator All accounts. Scan one account or all of them, quarantine, ignore for good.
Reseller Their own customers' accounts. Scan, quarantine, ignore (as Ignored by the customer).
Customer Their own account. Scan, quarantine, ignore (as Ignored by the customer).

A package can leave the feature out: untick Malware scan under Features in Packages. Customers on that package then cannot open the page or use its API, but their administrator or reseller can still scan their account. Limited administrators need the Security area.

What is scanned

The scanner reads files under domains/ in each account's home directory (all websites of the account) and checks them against a set of signatures:

  • File types: .php, .phtml, .php3, .php4, .php5, .php7, .phar, .inc, .suspected, .js, .html and .htm. JavaScript and HTML files are checked only for JavaScript injection and long hex-encoded strings.
  • Size: files up to 4 MB.
  • Skipped: node_modules, .git and the Git working copy of deployed apps (repo). For deployed apps only the live release is scanned, not older releases.
  • Ownership: only regular files that belong to the account. Symbolic links are not followed, and hard links to files of other accounts or of root are not read.

Databases, mailboxes, backups and files outside domains/ (for example ~/tmp) are not scanned.

Detections

Detection Severity What it matches
Encoded eval High eval or assert of base64_decode, gzinflate, str_rot13 and similar
Eval of user input High eval, assert or create_function on $_POST, $_GET, $_REQUEST, $_COOKIE or HTTP headers
Command execution High system, exec, shell_exec, passthru, popen, proc_open on request data
Shell execution High Backticks around request data
Dynamic function call High A function name taken from request data, called with request data
Known webshell High Markers of known shells such as FilesMan, WSO, c99, r57, b374k, IndoXploit, AnonymousFox, ALFA
preg_replace /e Medium preg_replace with the /e modifier on request data
File uploader Medium move_uploaded_file driven by request data
Writes user input to file Medium file_put_contents, fwrite or fputs of request data
Obfuscated (chr) Medium Long chains of chr() calls
Obfuscated (hex) Medium Long \x.. strings that decode to readable text (not in vendor/ or wp-includes/)
Large encoded payload Medium eval, assert or base64_decode of a string of 1,000+ base64 characters
JavaScript injection Medium document.write(unescape(...)) writing a script or iframe, or long String.fromCharCode chains
PHP in uploads folder Medium A PHP file with code inside an uploads folder (including wp-content/uploads)

Some legitimate patterns are filtered out: hex strings that decode to binary tables, document.write(unescape(...)) that loads no external script or iframe, and comment-only placeholder files such as WordPress's <?php // Silence is golden.

Run a scan

  1. Open Malware scan.
  2. Administrators and resellers choose an account in the account picker, or all accounts.
  3. Click Scan now. The task log lists each account, the number of files checked and every finding, and ends with a line such as Done: 5230 files, 2 findings in 14.2s.

The scanner uses a quarter of the server's CPU cores (between 1 and 8), so websites stay responsive during a scan.

Weekly scan

Every Sunday at 03:00 (server time), ZoPanel scans all hosting accounts automatically. The schedule cannot be changed; run Scan now whenever you need an extra scan.

Review findings

The page shows three counters (Open findings, High severity, Quarantined) and a table with one row per finding:

  • File: the path relative to the account's home and the line number, with the matching line below it.
  • Owner (administrators and resellers): the account.
  • Detection: the rule, coloured by severity, and the date it was first found.
  • Status: Open, Quarantined, Ignored by the customer or Ignored.

Open the file in the File Manager or over SFTP to look at it before you act. Open findings, including those ignored by the customer, also lower the score in Security Center and appear on the dashboard.

When an account is scanned again:

  • an Open finding whose file is gone or no longer matches is removed from the list;
  • a Quarantined finding is set back to Open if the same file matches again (it was put back, or the attacker recreated it);
  • an Ignored finding stays ignored.

Quarantine a file

  1. Click Quarantine on the finding and confirm.
  2. The file is moved out of the website to /var/lib/zopanel/quarantine/<account>/ on the server. Only root can read that folder: the account, and anyone who broke into it, can no longer run, restore or delete the file.

The quarantined copy is named after the time and the original path, for example 20261012-031502_a1b2c3d4_domains__example.com__public_html__wp-content__uploads__x.php.

Note: If the file was legitimate, the website may break where it used the file. Check the site after quarantining, and restore the file if needed.

Restore a quarantined file

There is no restore button. An administrator restores the file over SSH as root:

# List the account's quarantined files
ls -l /var/lib/zopanel/quarantine/USER/

# Put one back, owned by the account
install -m 0640 -o USER -g www-data \
  /var/lib/zopanel/quarantine/USER/20261012-031502_a1b2c3d4_domains__example.com__public_html__file.php \
  /home/USER/domains/example.com/public_html/file.php

The next scan will find the file again and set the finding back to Open. If it is a false positive, click Ignore then.

Ignore a false positive

Click Ignore on the finding.

  • When an administrator ignores it, the status is Ignored and the finding no longer counts anywhere.
  • When a customer or reseller ignores it, the status is Ignored by the customer. It still counts in Security Center and on the dashboard, and the administrator still sees Quarantine and Ignore on it, so a compromised customer login cannot hide a backdoor.

Common false positives: security, backup and file manager plugins that legitimately use eval or write uploaded files; minified libraries with encoded data; and PHP files that a plugin keeps in its own uploads subfolder. If a file belongs to a known plugin, compare it with a fresh copy of the same version before you ignore it.

After an infection

A webshell is a symptom: the attacker got in through something else. Quarantining the file is the first step, not the last.

  1. Quarantine every high-severity finding of the account, then review the medium ones.
  2. WordPress sites: on the website's WordPress tab, run Verify file integrity to find modified core and plugin files, update everything with Update all, remove plugins and themes you do not use, then Rotate security keys (signs everyone out) and use Reset password for administrator users. Click Apply all in the Security card. See WordPress.
  3. Change passwords of the hosting account, its SFTP/FTP accounts and its database users, and update the application configuration with the new database password.
  4. Look for persistence: unknown cron jobs, unknown SSH keys, and new administrator users in the application.
  5. If in doubt, restore files and database from a backup taken before the infection. See Backups.
  6. Turn on the web application firewall for the website in its Tools tab. See Security.
  7. Check outgoing mail: hacked sites often send spam. Look at the mail queue and sending limits in Email.
  8. Run Scan now again until no open findings remain.

Notifications

  • Administrators: the Malware detected event in Settings → Alerts (e-mail, Telegram or webhook). It reports the number of Open and Ignored by the customer findings in the scanned accounts. See Panel settings.
  • Customers and resellers: Suspicious files are found in My account → Notifications, by e-mail or Telegram, with the number of findings still Open in their own account.

Alerts are checked after every scan, including the weekly scan. Findings that are Quarantined or Ignored by an administrator do not trigger alerts again, and a finding the customer ignored no longer alerts the customer.

Troubleshooting

Message or symptom Cause and fix
"No threats found" right after installing No scan has run yet, or nothing matched. Click Scan now.
Quarantine fails with mv: cannot stat … The file was already moved or deleted. Run Scan now to refresh the list.
"the file does not belong to the account" The file is owned by another user (for example root). Investigate it as root on the server; the panel only quarantines the account's own files.
"invalid path" The finding points outside domains/. Rescan the account.
A known infected file is not reported It may be larger than 4 MB, have another extension (for example a .jpg with PHP code), sit outside domains/, or use a technique the signatures do not cover. Inspect the site manually and restore from a clean backup.
The website broke after a quarantine The file was needed. Restore it (see above) and ignore the finding if it is clean.

← Country blocking S3 storage providers →