Ransomware on Your Web Server: Safe Backup Restore Without Re-Infection
Ransomware hit your web server? Learn how to isolate the host, verify clean backups, strip backdoors, and restore without inviting the same attack back—plus when Fixwebnode should take over.
If your site is serving ransom notes, encrypted files, or locked admin panels, this guide walks you through a safe restore from backups without re-infecting the live server.
Homeowners running a small business site and local shops on shared or VPS hosting often restore too fast: they pull yesterday’s tarball, drop it over the infected tree, and watch the encryptor return within hours. That usually means the backup was already dirty, a web shell survived, or stolen credentials were never rotated. Fixwebnode provides hands-on server support for exactly this incident path—isolation, clean recovery, and hardening—so you are not guessing under pressure.
Below is a practical runbook you can follow on a typical Linux web stack (Nginx/Apache, PHP, MySQL/MariaDB, WordPress or similar CMS). Work from a second machine or console session you trust. If the box is still reachable by the attacker, assume every password is burned until proven otherwise.
Why safe restore matters more than “just roll back”
Ransomware on a web server is rarely a single encrypted folder. Attackers drop persistence: PHP webshells, rogue cron jobs, modified wp-config.php, new admin users, and outbound reverse shells. A naive file restore puts clean content on top of dirty system state—or restores the malware itself if the backup window overlapped the intrusion.
A safe restore sequence is: isolate → preserve evidence → validate backup integrity and age → rebuild or wipe the runtime → restore only known-good data → rotate every secret → verify with malware scans and integrity checks. Skipping isolation is the most common reason teams get hit again the same night.
Common issues when restoring after web-server ransomware
These are the failure modes we see repeatedly on small-business sites—not generic “update your software” advice.
- Issue 1 — The “good” backup already contains the payload. Symptoms: after restore, the same
.encryptedextensions or ransom HTML reappear within minutes;findstill shows recently modified PHP droppers underuploads/or cache dirs; file dates in the archive sit after the first odd login. - Issue 2 — Files look clean but a webshell or cron re-infects the tree. Symptoms: homepage is fine, then random 404 PHP files appear; high CPU from
php-fpm; outbound connections on odd ports;crontab -lshows base64 one-liners you never added. - Issue 3 — Database and CMS admins were never scrubbed. Symptoms: new WordPress users with administrator role; options table points to unknown domains; scheduled posts or WooCommerce hooks fire malware; login works with an old password the attacker also has.
- Issue 4 — Secrets and SSH keys were not rotated, so re-entry is instant. Symptoms: fresh deploy encrypts again after a few hours; auth logs show successful logins from unfamiliar IPs using your real username; deploy keys or
.envAPI tokens still match the pre-incident set.
Issue 1: Confirm the backup is clean before you trust it
Never restore the newest backup by default. Ransomware often dwells for days. You need the last known-good snapshot and a malware pass on the archive offline.
Step 1 — Isolate the server from the public internet (keep console/SSH from a trusted jump host only).
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT DROP
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
sudo iptables -A INPUT -p tcp -s YOUR.TRUSTED.IP.ADDR --dport 22 -j ACCEPT
sudo iptables -A OUTPUT -p tcp -d YOUR.TRUSTED.IP.ADDR --sport 22 -j ACCEPT
Replace the IP with your admin address. This stops C2 and mass-encryption callbacks while you work. On cloud firewalls, lock security groups the same way.
Step 2 — Snapshot the infected disk for forensics, then work from copies.
sudo mkdir -p /root/incident
sudo tar -czf /root/incident/webroot-infected-$(date +%F).tar.gz /var/www
sudo mysqldump --all-databases --single-transaction > /root/incident/db-infected-$(date +%F).sql
Store that copy off-box if possible. Do not “clean in place” as your only copy.
Step 3 — Stage candidate backups offline and list timelines.
mkdir -p ~/restore-stage && cd ~/restore-stage
# example: pull object-storage backup to a clean workstation, not the hot server
aws s3 cp s3://your-backup-bucket/weekly/ ./ --recursive
ls -latr
# inspect archive members and dates
tar -tzvf site-2025-03-01.tar.gz | head
tar -tzvf site-2025-03-01.tar.gz | grep -E '\.(php|phtml|phar)$' | head -50
Step 4 — Scan the extracted tree with ClamAV (and optional YARA) before promote.
sudo apt-get update
sudo apt-get install -y clamav clamav-daemon
sudo systemctl stop clamav-freshclam
sudo freshclam
sudo systemctl start clamav-freshclam
mkdir -p ~/restore-stage/extract
tar -xzf site-2025-03-01.tar.gz -C ~/restore-stage/extract
clamscan -r -i ~/restore-stage/extract 2>&1 | tee clam-report.txt
If ClamAV flags droppers inside uploads, cache, or random theme copies, discard that generation and step back further in the backup chain.
Step 5 — Prefer restore to a clean host or wiped volume, not over the live infected OS. Rebuild the VPS image or reinstall the panel stack, then copy only application data from the verified archive.
When backups are only on the same disk that was encrypted, object versions are missing, or you cannot establish a clean timeline, book Fixwebnode—recovery without a trustworthy restore point needs specialist imaging and negotiation of partial rebuilds.
Issue 2: Strip persistence so the restore is not immediately re-poisoned
Even a clean content restore fails if cron, systemd user units, or a leftover shell rewrites files.
Step 1 — On the infected host (read-only investigation), inventory persistence.
sudo crontab -l
sudo ls -la /etc/cron.* /var/spool/cron/crontabs
systemctl list-timers --all
ls -la /etc/systemd/system /usr/lib/systemd/system | head
sudo find /var/www -type f \( -name '*.php' -o -name '*.phtml' \) -mtime -14 -ls
sudo find /var/www -type f -name '*.php' -size -20k -exec grep -lE 'eval\s*\(|base64_decode\s*\(|gzinflate\s*\(|shell_exec\s*\(' {} \;
Step 2 — Disable web and PHP workers before cutover.
sudo systemctl stop nginx apache2 php*-fpm 2>/dev/null
sudo systemctl stop mysql mariadb 2>/dev/null
Step 3 — Rebuild runtime, then restore application files from the scanned archive only.
# after OS rebuild or clean volume mount
sudo mkdir -p /var/www/site
sudo rsync -a --delete ~/restore-stage/extract/var/www/site/ /var/www/site/
sudo chown -R www-data:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;
Step 4 — Reinstall CMS core and plugins from vendor packages instead of trusting every restored binary. For WordPress, keep wp-content/uploads (after scan) and wp-config.php values, but replace core with a fresh download:
cd /tmp
curl -O https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
sudo rsync -a --delete wordpress/ /var/www/site/ \
--exclude wp-content/uploads \
--exclude wp-config.php
wp core verify-checksums --path=/var/www/site
wp plugin list --path=/var/www/site
wp plugin install plugin-name --force --path=/var/www/site
Step 5 — Wipe unknown crons and lock down write paths.
sudo crontab -r
sudo rm -f /etc/cron.d/*malware* 2>/dev/null
# uploads should not execute PHP
sudo printf '%s\n' '<FilesMatch "\\.php$">' 'Require all denied' '</FilesMatch>' \
| sudo tee /var/www/site/wp-content/uploads/.htaccess
# Nginx equivalent: location ^~ /wp-content/uploads/ { location ~ \\.php$ { deny all; } }
If you find kernel modules you do not recognize, unknown SSH authorized keys system-wide, or reverse shells surviving reboot, stop DIY and escalate—rootkits are outside safe homeowner scope.
Issue 3: Clean the database and CMS control plane
File restore without database hygiene leaves attacker admins and poisoned options intact.
Step 1 — Restore DB from a dump taken before compromise indicators.
sudo systemctl start mysql
mysql -u root -p -e "CREATE DATABASE site_clean CHARACTER SET utf8mb4;"
mysql -u root -p site_clean < ~/restore-stage/db-known-good.sql
Step 2 — Audit users, options, and odd tables (WordPress example).
wp user list --role=administrator --path=/var/www/site
wp db query "SELECT option_name,option_value FROM wp_options WHERE option_value LIKE '%<script%' OR option_value LIKE '%base64%' LIMIT 50;" --path=/var/www/site
wp db query "SELECT * FROM wp_users;" --path=/var/www/site
Step 3 — Remove unknown admins and reset remaining passwords from a clean workstation.
wp user delete ATTACKER_LOGIN --reassign=YOUR_ADMIN --path=/var/www/site
wp user update YOUR_ADMIN --user_pass='LONG-RANDOM-PASSPHRASE' --path=/var/www/site
wp option delete siteurl --path=/var/www/site 2>/dev/null
wp search-replace 'http://malicious-example.tld' 'https://your-real-domain.example' --path=/var/www/site
Step 4 — Flush caches, transients, and object cache that may hold injected HTML.
wp cache flush --path=/var/www/site
wp transient delete --all --path=/var/www/site
redis-cli FLUSHALL 2>/dev/null
Call Fixwebnode when the dump itself is encrypted, table prefixes were renamed by the attacker, or ecommerce order history must be surgically merged from a dirty DB into a clean schema.
Issue 4: Rotate every secret so the attacker cannot walk back in
A perfect file restore is worthless if SSH keys, panel passwords, database passwords, and application salts are unchanged.
Step 1 — Rotate OS and panel access first.
sudo passwd youradmin
sudo rm -f /home/youradmin/.ssh/authorized_keys.bak
# install only keys you control today
install -m 700 -d ~/.ssh
echo 'ssh-ed25519 AAAA... your-new-key' > ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
sudo sed -i 's/^PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
Step 2 — New database credentials and CMS salts.
mysql -u root -p -e "ALTER USER 'wpuser'@'localhost' IDENTIFIED BY 'NEW-DB-PASSWORD-HERE'; FLUSH PRIVILEGES;"
# update DB_PASSWORD in wp-config.php to match
wp config shuffle-salts --path=/var/www/site
Step 3 — Rotate host firewall back to least privilege and force HTTPS-only admin.
sudo apt-get install -y fail2ban
sudo systemctl enable --now fail2ban
# reopen 80/443 carefully after app tests; keep SSH limited to admin IPs
Step 4 — Invalidate CDN, DNS admin, registrar, email, and payment API keys from their respective dashboards. Any key that lived in .env or hosting panel notes should be treated as leaked.
If you share hosting credentials across multiple client sites, or deploy keys were embedded in CI, Fixwebnode can map the blast radius and sequence rotations without locking you out mid-recovery.
Verification checklist before you reopen the site
- ClamAV (or equivalent) clean on webroot and uploads.
wp core verify-checksumspasses; unknown plugins removed.- Only expected admin users exist; all passwords and salts rotated.
- No suspicious cron or systemd timers.
- Outbound connections from the server match expected package mirrors and APIs only.
- Fresh off-site backup taken after cleanup, retained immutable if your storage supports object lock.
clamscan -r -i /var/www/site
wp core verify-checksums --path=/var/www/site
ss -tulpn
sudo journalctl -u ssh --since '1 hour ago' | tail
When DIY is enough vs when to book Fixwebnode
DIY is reasonable when you still have offline backups from before the intrusion, you can rebuild the VPS, you control DNS and registrar, and the stack is a standard CMS you already administer. Follow isolation → clean backup selection → rebuild → restore → rotate → verify.
Book a specialist when any of these are true: backups only exist on the encrypted volume; the host may have a rootkit; multiple sites share the same stolen panel login; checkout or customer PII may have been exfiltrated; you cannot afford another night of downtime experimenting; or you need hardened automated backups and migration so the next incident has a clean restore path. Fixwebnode works across Australian service areas—including teams supporting builds around Canberra and recovery workflows familiar to Footscray-hosted WordPress stacks—see all service areas, plus related work on website solutions for Canberra builders & construction specialists and WordPress automated backups & AWS/DigitalOcean migration in Footscray.
Do not reopen marketing traffic until verification passes. A half-restored shop that re-infects overnight costs more than a controlled cutover.
Talk through your restore plan with Fixwebnode
If ransomware is on your web server right now, or a restore already bounced back infected, get a calm second pair of eyes before the next overwrite. Fixwebnode’s server support path is built for isolation, clean recovery, and backup redesign—not marketplace bidding.
Start a conversation and book the next step here: https://fixwebnode.com.au/step-by-step. Bring what you know about backup dates, hosting type (VPS, shared, cloud), and whether SSH still responds—we will help you choose safe DIY boundaries versus full incident handling.