The Gambling Hack
gambling script injection ojs security incident

I Found a Gambling Hack on Our University's Research Server
One morning I opened the server logs and found hundreds of pages injected with gambling links. Here's how I cleaned it up, hardened the server, and built an automated monitoring system so it never happens again.
A few months into my role as IT Staff at LPPM Universitas Syiah Kuala, I was doing a routine check on our research publication portal when something looked off. Pages that should only contain academic journal content were being indexed by Google with titles like "slot online terpercaya" and "daftar situs judi terbaik."
We had been hacked. Gambling scripts had been injected into our OJS (Open Journal Systems) installation.
This is the story of how I found it, cleaned it, and built a system to make sure it never happens undetected again.
What Happened
The attack was a classic SEO poisoning via file injection. The attacker didn't steal data or take the site down — that would have been noticed immediately. Instead, they injected hidden content and redirects into PHP files throughout the OJS installation, turning our academic portal into a vehicle for gambling site SEO.
From the outside, the site looked normal. But Google was indexing hundreds of gambling-related pages under our university domain.
The injected code looked something like this — hidden in otherwise legitimate PHP files:
// Legitimate OJS code above...
// Injected at the bottom of multiple files:
if(isset($_GET['cmd'])){
$cmd = $_GET['cmd'];
// ...redirect to gambling sites
}Some files had the injection at the very end of the file, invisible when casually browsing the code. Others had it encoded in base64 to avoid simple text searches.
How I Cleaned It
Step 1: Identify the scope
First, I needed to understand how widespread the injection was. I searched for common injection patterns across the entire OJS file system:
grep -r "base64_decode" /var/www/html/ojs --include="*.php" -l
grep -r "eval(" /var/www/html/ojs --include="*.php" -l
grep -r "judi\|slot\|togel" /var/www/html/ojs --include="*.php" -lThe results were worse than I expected. Dozens of files were compromised.
Step 2: Restore from backup
Fortunately, we had backups. I restored the core OJS files from a known-clean version. For custom theme files and plugins that weren't in the backup, I manually reviewed and cleaned each one.
Step 3: Close the entry point
The attacker got in through an outdated OJS plugin that had a known file upload vulnerability. I removed the vulnerable plugin, updated OJS to the latest version, and audited every other installed plugin.
Step 4: Check for backdoors
After cleaning the injection, I had to make sure there were no backdoors left behind. I checked for web shells — files that would let the attacker regain access:
find /var/www/html -name "*.php" -newer /var/www/html/ojs/index.php -ls
find /var/www/html -name "*.php" | xargs grep -l "passthru\|shell_exec\|system(" 2>/dev/nullFound two. Deleted both.
Building the Monitoring System
Cleaning up was one thing. Making sure this never goes undetected again was the real challenge.
I built an automated monitoring system with four components, all sending alerts to a Telegram bot I set up:
1. Real-time health check (every 15 minutes)
A cron job that checks if the site is responding correctly and if any core files have been modified since the last check. If a PHP file changes unexpectedly, I get a Telegram message immediately.
*/15 * * * * /home/ubuntu/scripts/health_check.sh2. Daily security audit (every 24 hours)
Runs a deeper scan looking for known injection patterns, suspicious file permissions, and new PHP files in unexpected locations. Results are logged and summarized in a daily Telegram report.
3. Automated full backup
Every night at 2 AM, a full backup runs — the complete OJS database plus all file storage. Backups are compressed and stored offsite. If something goes wrong, recovery is fast.
4. Integrity checker
Compares checksums of core OJS files against a known-good baseline. Any deviation triggers an alert.
The result: I now get a Telegram notification within 15 minutes if anything unusual happens on the server — even at 2 AM, even when I'm not at my desk.
The OJS 2 → OJS 3 Migration
The security incident accelerated a migration we were already planning. Our old platform ran on OJS 2, which is no longer actively maintained. We moved to OJS 3 — a completely different codebase with better security architecture.
The migration involved:
Setting up a fresh OJS 3 server environment
Migrating journal metadata and submission history for 80+ active journals
Creating new user accounts for hundreds of editors, reviewers, and authors
Customizing themes for individual journals that had their own branding requirements
Running parallel systems during the transition to avoid downtime for active submissions
The hardest part wasn't the technical migration — it was the user management. OJS 3 handles user roles differently from OJS 2, so I had to map old roles to new ones carefully and communicate the changes to journal editors who weren't very technical.
What I'd Do Differently
Monitor from day one. The monitoring system I built after the incident should have been in place from the start. It's not complicated — a few cron jobs and a Telegram bot — but it provides visibility that would have caught this attack much earlier.
Update dependencies aggressively. The vulnerability that let attackers in was in an outdated plugin. In a production academic system with real researchers depending on it, keeping everything updated isn't optional.
Principle of least privilege. After the incident, I audited all file permissions and user roles on the server. Several things had more permissions than they needed. Tightening this reduces the blast radius if something does get compromised.
Running a production server for 80+ academic journals has taught me more about security than any course. When real researchers are depending on the platform for their journal submissions, you take uptime and integrity seriously.
The system has been clean since. The Telegram alerts keep coming — mostly just routine health confirmations. But every morning when I see that green status message, I'm glad I built it.