跪拜 Guibai
← Back to the summary

A Server Breach Went Undetected for 85 Days — Here's the Full Forensic Breakdown

Utter Humiliation: My Website Was Injected with Malware, and My Server Had Been Compromised for Three Months

I'm a wild coder. I work at a project department during the day and tinker with my own small website at night. Recently, I've been taking a cybersecurity course and have been training an AI advisor running in Kali.

On August 16th, when I opened a page on my website, the browser redirected directly to a pornographic gambling interface. I closed it and reopened it, and the page was normal. I opened it again, and it was still normal. It was as if that redirect had been a hallucination.

It wasn't a hallucination. My server was putting on an act for me, and it had already been occupied for three months.

That Redirect Was Only Shown to the "Prey"

My first reaction was that a browser plugin was acting up. But my gut told me it was more than that, so I right-clicked to view the page source.

At the very bottom of the page lay a piece of code I couldn't understand: eval(atob("IShmdW5jdGlvbigp...")), a long string of garbled characters. This is Base64 encoding, a common technique to turn text into gibberish. It's not a virus in itself, but a normal webpage has no reason to hide its code.

I couldn't understand it, so I threw the whole thing to my AI advisor.

The advisor decoded the garbled text, then followed the addresses inside to pull down the second and third layers of code. It then told me: this is a typical search-hijacking black-market script, and it's hidden very cleverly. Its trigger conditions are a set of filters:

Do you see? It wasn't acting randomly; it was screening for prey. As the webmaster, using my own computer and typing the URL directly, I would never have seen that redirect in my lifetime. The only people who could see it were ordinary visitors coming from search engines. It had been stealing my traffic for as long as it had been living in my website.

The advisor also gave a judgment: being able to inject this kind of thing into a webpage means the attacker has write permissions to the website directory. The server is likely compromised far beyond just having a "webpage trojan."

After Getting on the Server, I Found the Webpage Files Were Clean

I sent the server password to my AI advisor. It logged in via SSH and first located the file for that page on the hard drive.

The file was clean. Not a single word had been changed, and the modification time was stuck in 2024.

This is the most cunning part of the whole affair. The malicious code wasn't in the webpage file at all; it had been written into the nginx configuration of the Baota panel. nginx is the "conveyor belt" on the server responsible for delivering webpages to visitors. It has a legitimate feature called sub_filter, which can replace content before a webpage is sent out.

The attacker stuffed a malicious sub_filter directive into all 16 PHP configuration files on the server. Thus, every page passing through the conveyor belt was silently tagged with a malicious script.

The files weren't modified, so antivirus scans of the files found nothing. After the modification, the attacker even disguised the configuration files' modification time to look like an old date. But they missed one thing: the system's "change time" (ctime) cannot be faked. All configuration files were batch-modified at 6:25 AM on August 15th. The time was precise to the second, clearly the work of a batch script.

When the Timeline Was Laid Out, a Chill Ran Down My Spine

Following the logs, the advisor dug deeper and uncovered something far more serious than a webpage trojan:

Time What Happened
May 22 An account named linuxsafe with UID 0 was created in the system.
May 25-26 An unfamiliar IP logged in using my root password and switched to this backdoor account.
June 6 The backdoor account's password was written into the system account file, hardening it.
August 14 Another unfamiliar IP logged in again with the root password, four times in a row.
August 15, 06:25 16 nginx configurations were batch-tampered with, and the malware injection took effect.

Let me explain UID 0: In a Linux system, root's ID number is 0. Setting another account's UID to 0 is like hiding a second root in the system. The name looks harmless, but its permissions are identical to mine. This account lay quietly in my server for 85 days.

In other words, my server had been breached before May 22nd. The webpage trojan was just a side hustle; they had already made a copy of my house keys long ago.

My exact words were: This is an utter humiliation! My server had been breached multiple times, and it had been compromised since May.

How Was the Door Pried Open?

This was my biggest concern. The answer the advisor gave had solid evidence parts and speculative parts; I'll present it as is.

Solid evidence: The attacker logged in openly via SSH using the root password both times. My server's port 22 was open to the public internet, allowed root password login, had no anti-brute-force software installed, and the failed login records were filled with day-and-night attempts from IPs all over the world.

Speculation: My root password was a 12-character mix, making the probability of a brute-force hit extremely low. A more likely path is that my local Win11 machine was compromised. The password saved in a browser or text file was stolen by an infostealer trojan. This type of trojan specializes in exactly this—stealing all passwords saved in browsers, SSH configurations, and the clipboard. I had just finished a cybersecurity course a few days ago; I guess I paid my tuition with my own server.

Additionally, an exposure surface I was completely unaware of was uncovered: the database root account allowed remote connections from any IP, the password was a weak one set years ago, and port 3306 had been wide open to the public internet. Even if SSH hadn't been compromised, this database door had long been left ajar.

An Unemotional Disposal Report

The following is an objective record of this emergency response, listed in order of execution. Credential-related information has been redacted.

Malicious Code Removal. The 16 tampered nginx configuration files were first backed up for evidence, then all malicious sub_filter directives were removed. After the configuration syntax was verified, the service was reloaded. Dual verification from the server itself and an external network confirmed that the entire site's responses contained no injected content.

Backdoor Removal. The UID 0 backdoor account linuxsafe was deleted. The cleanup scope covered the system account file, shadow password file, group file, and its home directory. The investigation found no webshells, malicious processes, malicious system services, or abnormal SSH public keys.

Credential Rotation. The server root password, MySQL root password, mcms database account password, and Baota panel password were all replaced with strong, random passwords. The database configurations for the website's three applications were updated synchronously, and business functions were verified to be normal one by one.

Attack Surface Reduction. The firewall removed the external network allow rule for MySQL port 3306. The fail2ban intrusion prevention software was deployed; within minutes of enabling the SSH anti-brute-force rule, it automatically banned 3 brute-force source IPs. The Baota panel was upgraded to the latest LTS version.

Intrusion Path Conclusion. The confirmed channel was SSH root password login. The first successful breach occurred before May 22nd. The exact method cannot be fully determined as the early logs have been rotated. A comprehensive judgment suggests that local credential leakage (via an infostealer trojan) is more likely than brute-forcing.

Residual Risk Statement. A root-level compromise persisted for nearly three months. No anomalies were found in standard locations, but deeper hidden backdoors cannot be completely ruled out. The standard disposal recommendation is to reinstall the system and migrate data. Also, because a local infostealer trojan is suspected, all new credentials must be rotated again on a trusted device after the local machine is confirmed clean.

If You Also Have a Server Running Naked, Check It Against This List

The biggest output from this ordeal is this self-check checklist. Ten items, each directly actionable:

  1. Use a mobile User-Agent and a Baidu referrer to visit your own website and see if it redirects (the most common self-check for search hijacking).
  2. Baota users, directly search: grep -r "sub_filter" /www/server/nginx/conf/. If there are results you didn't add, you've been hit.
  3. Check for hidden roots: awk -F: '$3==0{print $1}' /etc/passwd. The result should only be root.
  4. Check login traces: last -i | head and lastlog. See if you recognize the IPs.
  5. Check SSH backdoors: cat /root/.ssh/authorized_keys. How many of the public keys inside do you recognize?
  6. Check scheduled tasks: crontab -l plus the /etc/cron.d/ directory.
  7. Check abnormal connections: ss -antp | grep ESTAB. Look for remote addresses you don't recognize.
  8. Don't use the database root account for direct business connections, and especially don't allow root@'%' logins from any source.
  9. Install fail2ban. If SSH is running naked on the public internet, brute-force attacks are non-stop, 24/7.
  10. If you find a breach, back up the evidence first before cleaning. Deleting files immediately is equivalent to helping the attacker destroy evidence.

A final note on the last point: Don't save passwords in your browser, especially server root passwords. My biggest suspect this time fell right on this point.

Who Is the Attacker? I Uncovered Their Entire Operation

Cleaning up the virus was just the first step. The advisor followed the addresses in the malicious code downstream and ran through the entire black-market chain, obtaining solid evidence at every layer. This chain is divided into four layers, each separated—the hallmark of a professional gang:

The August attack IP, 154.89.151.143, comes from Cloudvalley in Malaysia—a cheap VPS, a typical disposable springboard.

There are also two extremely valuable clues: two Baidu Analytics IDs (36783bbed18dfe5e6429f1dbfbedfaa4 and 60b9eff27f66975f19c6102404a3b350) are buried in this chain. Analytics IDs are asset fingerprints of the gang. Searching for them can correlate and uncover other victim sites this group has trojanized.

Finally, a disturbing detail: all the landing layer domains were newly registered within 10 days before the trojan deployment. This isn't a botnet living off old stock; this is an actively operating gang, ready to change its shell at any moment.

Fighting Back: 5 Abuse Reports Have Been Sent

After uncovering all this, my exact words were just two characters: Get 'em.

The advisor immediately stopped me. Counter-attacking the opponent's server falls under Articles 285 and 286 of the Criminal Law—illegally intruding into or destroying computer information systems. That's an operation that would land me in jail. Even if I didn't touch the law, the platform review would kill this article first. When bullied, you must fight back, but the counterattack must be legal and must hurt them more.

The legal way to fight is to attack its infrastructure. They paid for the domains, and they rented the servers. Block one, and they have to spend money to buy another. On August 16th, the same day, 5 abuse report emails were sent out:

Recipient Target of Report Demand
NameSilo (Domain Registrar) Domains pucdpqit.top, uasxjts.top Suspend domains
NameCheap (Domain Registrar) Domain domenecentosl.xyz Suspend domain
GoDaddy (Domain Registrar) C2 domain 511a.co Suspend domain
Dimension Network (HK Datacenter) IPs 103.68.175.58/59, attack source & C2 host Investigate & shut down service
CTG Server (HK Datacenter) IP 202.79.175.57, gambling landing site Investigate & shut down service

The reports included the complete intrusion timeline and forensic records. Receipts generally take 1 to 3 business days. After a domain is suspended, use dig to recheck the resolution status and confirm the action is effective. If even one link in this four-tier chain is blocked, the entire line must be scrapped and rebuilt. This is the right way to fight back: using the only language it understands—cost.

Conclusion

When you're bullied, just being angry is useless. My counterattack this time consisted of three things:

First, reporting. Five abuse reports were sent directly to registrars and data centers to scrap its domains and servers one by one. Second, public disclosure. The attack IPs, C2s, redirect chain, and Baidu Analytics IDs are all written above. Exposure kills them, forcing them to constantly burn money changing shells. Third, documentation. The complete investigation and disposal process is written into this article, so every webmaster running naked can use it to self-check and recognize this gang's methods.

Now, a word for three types of readers:

If you're a novice and have never checked your server: go now, run through the ten-point checklist above. It's a ten-minute job. If you're a webmaster and find any of the above IOCs on your own site: report it together. Report until they get tired of changing domains. If you're a security expert: the two Baidu Analytics IDs and the four-tier chain are all provided for you. You're welcome to continue digging into this chain, and also welcome to point out any deficiencies in my tracing and forensics. See you in the comments or the tech community.

There's a saying among security peers: There are only two types of servers in the world—those that have been breached, and those that don't yet know they've been breached. My server was the second type for 85 days. Now, it has fought back. How long has it been since you checked yours?

FAQ

Q: How can I tell if my website has been search-hijacked? A: Hijacking code usually only redirects visitors from mobile devices and search engine sources. Use browser developer tools to simulate a mobile User-Agent and a Baidu referrer, then visit your site. Or, directly grab the webpage source code and search for keywords like eval, atob, or iframe. Clean files don't mean you're safe—this time, the malicious code was hidden in the nginx configuration.

Q: What's the first thing to do when you discover a server intrusion? A: It's not deleting files; it's collecting evidence. First, back up the tampered files, logs, and account lists as they are, then start cleaning. The cleanup order is: cut off the entry point (change passwords, block ports), clear the backdoors (accounts, public keys, scheduled tasks, configurations), and verify the business. If the order is reversed, the evidence is gone, and the attacker might still get back in.

Q: If all passwords have been changed, why is a system reinstall still recommended? A: Root privileges were in the opponent's hands for nearly three months. Everything in standard locations that could be checked has been checked, but kernel-level hiding methods cannot be fully cleaned from inside the system. This is a universal principle of incident response: a machine that has had its root compromised is no longer trustworthy. Not reinstalling is a choice made under a trade-off, not a risk elimination.

I am Wild Coder, an AI practitioner. I've built an AI local knowledge base system, an AI digital employee team, an AI exam prep system for software exams, and a quantitative trading system, all publicly documented from start to finish.

This "AI Decryption Offense and Defense" series has torn through WeChat, DingTalk, Evernote, OPPO Notes, and To-Do apps. This is the seventh battle: the battlefield shifted from local software to my own server, and the opponent changed from "keys hidden by the manufacturer" to a real black-market intruder.

Follow me to see the real deal.

Wild Coder · Same Name Across All Platforms · AI Digital Employee Practical Replays, Data Only Reports Actual Measurements