CybersecurityMarch 29, 202611 min read

Malicious Packagist Packages Deploying Cross-Platform RATs in Laravel Ecosystems

SI

Secured Intel Team

Editor at Secured Intel

 Malicious Packagist Packages Deploying Cross-Platform RATs in Laravel Ecosystems

In early 2025, security researchers identified a calculated supply chain attack targeting PHP developers through Packagist, Composer's default package repository. Three packages — lara-helper, simple-queue, and lara-swagger — were published under the account nhattuanbl, each crafted to impersonate legitimate Laravel utilities. The attacker's approach was methodical: publish clean, functional versions first to build credibility and bypass automated trust checks, then push poisoned updates that silently chain-load a cross-platform Remote Access Trojan (RAT) across Windows, macOS, and Linux. For any Laravel application running in production, this means an attacker with C2 connectivity could exfiltrate .env secrets, database credentials, and arbitrary files without triggering a single WAF rule. This post breaks down the attack chain, detection logic, and what security teams operating PHP environments should do now.


How the Attack Chain Works: From Dependency Resolution to Full Remote Access

The Credibility Play: Publishing Clean Packages First

The attacker didn't start with malicious code. nhattuanbl published working, benign versions of each package first — a technique directly mapped to MITRE ATT&CK T1195.001 (Compromise Software Supply Chain). Packagist has no code-signing requirement and no mandatory review process, so positive install counts and lack of initial complaints create a false sense of safety.

Once Composer caches or pins a version, developers rarely re-examine the package's source on the next composer update. That trust gap is precisely what the attacker exploited. The malicious payload arrived as a routine dependency update.

Dependency Chaining and Autoload Activation

The core payload lives in src/helper.php. Rather than require a developer to invoke anything manually, the package registers itself through Laravel's service provider autoload mechanism or Composer's autoload directive. Every time the application boots — on every web request — the RAT initializes and attempts to phone home. It inherits the web server process's permissions, which in most Laravel deployments means read access to .env, the database, storage, and potentially write access to public directories.

This activation model means the compromise happens silently, at framework boot, with no visible error or log entry in a standard Laravel installation.


Inside the Payload: Obfuscation, Execution Fallbacks, and C2 Protocol

Control Flow Obfuscation in src/helper.php

The payload uses three obfuscation layers:

  • Encoded strings for domain names and shell commands (base64 or custom encoding), decoded at runtime so static string searches miss the C2 address
  • Randomized variable names on every execution path, complicating manual analysis and signature-based detection
  • Control flow flattening — logic branches through switch-case or ternary chains that defeat simple code-path tracing

The C2 target is helper.leuleu[.]net on TCP port 2096. While the server was offline at time of disclosure, the packages remain downloadable. Any organization that installed them before discovery should treat the environment as compromised.

Execution Fallback Chain (No Single Point of Failure)

Before attempting shell execution, the RAT probes PHP's disable_functions setting and then cycles through every available execution primitive:

popen → proc_open → exec → shell_exec → system → passthru

This mirrors T1059.004 (Unix Shell) and T1059.001 (PowerShell) under MITRE ATT&CK, and makes standard PHP hardening insufficient unless all execution functions are disabled. Many shared hosting environments disable only one or two.

Important: Disabling exec alone does not neutralize this payload. The fallback ladder means the RAT will succeed unless every function in the chain is blocked simultaneously. Confirm your disable_functions setting covers the entire list.

Supported RAT Commands

CommandBehaviorPlatform
ping / infoReturns system info (OS, hostname, PHP version) to C2All
cmdExecutes arbitrary shell commandsAll
powershellSpawns PowerShell processWindows
runBackground shell execution (fire-and-forget)All
screenshotCaptures screen via imagegrabscreen()Windows
downloadPulls file from C2 to host filesystemAll
uploadExfiltrates file to C2All
chmod 777Sets file permissions post-writeLinux/macOS

The C2 check-in loop retries every 15 seconds over a raw TCP socket on port 2096. This is not HTTP — standard application-layer proxies and web traffic analyzers won't capture it without explicit TCP-level inspection.


Attack Lifecycle and Detection Mapping

The table below maps each attack stage to detection opportunities and relevant MITRE technique IDs.

Attack StageMITRE ATT&CKDetection Method
Package publication (clean)T1195.001Monitor new Packagist contributors; SBOM baseline comparison
Malicious update pushedT1195.001composer.lock diff alerts; hash verification on install
Service provider autoloadT1546 (Boot/Logon Autostart)Audit config/app.php providers on deploy; compare against baseline
disable_functions probeT1082 (System Info Discovery)PHP error logs; anomalous reflection calls
Shell execution attemptT1059.004 / T1059.001Process trees spawned by PHP-FPM or Apache; auditd rules
C2 beacon (TCP:2096)T1095 (Non-Application Layer Protocol)Outbound firewall blocks on non-standard ports; SIEM alert on port 2096
File exfiltrationT1041Network DLP; baseline outbound volume per process
.env / credential accessT1552.001File integrity monitoring on .env; secrets rotation alert

Pro Tip: PHP process trees are your best early warning signal

In a healthy Laravel deployment, the PHP-FPM master process should never spawn child processes like bash, sh, powershell.exe, or python. Configure your EDR or auditd to alert on any execve call where the parent process is php-fpm, php, or httpd. This catches the RAT's command execution even if network-level indicators are missed.


Framework Controls: Hardening Composer Environments Against Supply Chain Attacks

NIST CSF and CIS Controls Mapping

Security ControlNIST CSF FunctionCIS ControlImplementation
Dependency hash verification (composer.lock)ProtectCIS 2.3Commit composer.lock; use --no-dev in production
SBOM generation and trackingIdentifyCIS 2.1Generate SBOM on every build; diff against known-good
Private mirror / artifact proxyProtectCIS 2.5Route Packagist through Nexus or Artifactory with scan policy
Outbound TCP filtering (non-HTTP/S)ProtectCIS 12.4Block all non-443/80 outbound from app servers by default
File integrity monitoring on .envDetectCIS 8.5Alert on .env reads outside deployment pipeline
Secrets rotation post-incidentRespondCIS 5.4Rotate DB creds, API keys within RTO window
PHP disable_functions hardeningProtectCIS 4.1Disable: exec,shell_exec,system,passthru,popen,proc_open

ISO 27001 and Compliance Implications

For organizations under GDPR, a successful RAT deployment triggering .env or database exfiltration constitutes a personal data breach under Article 33 — 72-hour notification obligations apply from the moment of discovery, not the moment of initial infection. Under PCI DSS v4.0, compromise of a web application with cardholder data in scope triggers immediate incident response under Requirement 12.10. SOC 2 Type II engagements require documented evidence that supply chain risks were assessed and controlled — these packages would represent a gap finding.

Important: If these packages are present in any environment that processes regulated data, treat this as a potential reportable breach and engage your legal and compliance teams immediately, regardless of whether C2 connectivity was confirmed.


Incident Response Priorities for Affected Environments

Immediate Actions (0–4 Hours)

If lara-helper, simple-queue, or lara-swagger appear in your composer.json or composer.lock:

  1. Remove packagescomposer remove <package-name> and redeploy
  2. Rotate all secrets in .env — database passwords, API keys, S3 credentials, OAuth secrets
  3. Revoke and reissue database credentials — assume they were read by the RAT on first boot
  4. Pull network logs for outbound TCP:2096 — check firewall, VPC flow logs, or NSG logs for connections to helper.leuleu[.]net
  5. Inspect running PHP processes — look for orphaned shells or unexpected child processes

Forensic Evidence Preservation

Before wiping or redeploying instances, capture:

  • /var/log/php-fpm/ and web server access logs
  • netstat -antp output (or equivalent) for open connections
  • Process tree snapshot (ps auxf on Linux)
  • Laravel log file (storage/logs/laravel.log) for anomalous boot events

DevSecOps Integration: Preventing the Next Packagist Supply Chain Attack

Dependency Scanning in CI/CD

Integrate the following controls into your build pipeline:

  • Composer Audit (composer audit) — checks installed packages against security advisory databases on every build
  • Enlightn Security Checker or Local PHP Security Checker — scans composer.lock against known CVEs
  • Trivy or Grype — container-level dependency scanning that catches PHP packages inside Docker images
  • SBOM generation with CycloneDX for PHP — produces a machine-readable bill of materials that can be diffed across releases to detect unexpected additions

Network-Level Hunting Queries

For teams with SIEM visibility, start with these searches:

  • Outbound TCP connections to port 2096 from any application server
  • DNS queries resolving leuleu[.]net or subdomains
  • PHP process spawning shell interpreters (bash, sh, cmd.exe, powershell.exe)
  • Anomalous outbound data volume from PHP-FPM processes during off-peak hours

Key Takeaways

  • Remove and rotate immediately — if any of the three packages are installed, treat credentials as compromised regardless of C2 availability
  • Disable the full execution function listexec, shell_exec, system, passthru, popen, and proc_open must all be disabled; blocking one is not sufficient
  • Block outbound non-HTTP/S TCP from app servers — port 2096 should never reach the internet from a production Laravel host
  • Commit and verify composer.lock — hash-pinned dependency files are your primary defense against update-time injection
  • Generate SBOMs on every build — supply chain visibility only exists if you baseline what's installed and diff every change
  • Treat this attack as a template — the npm ecosystem saw this pattern years before PHP; Composer is now a confirmed target

Conclusion

This campaign follows a pattern well-documented in npm and PyPI ecosystems: credible package identity, clean initial release, then malicious payload on update. The PHP ecosystem's relative immaturity around supply chain controls made Packagist an appealing target, and nhattuanbl's packages demonstrate that attackers are now applying those established playbooks to Composer. The RAT's cross-platform design, resilient execution fallbacks, and boot-time persistence make it a serious threat for any Laravel application running in production — not just an interesting proof-of-concept. The C2 server being down provides no protection; the persistence mechanism remains active and the packages are still available for download. The practical next step is a direct audit: run grep -r "lara-helper\|simple-queue\|lara-swagger" composer.json composer.lock across every repository in your organization, and escalate any match through your incident response process today.


Frequently Asked Questions

Q: The C2 server is offline — are we still at risk if we have one of these packages installed? Yes. The persistence mechanism (service provider or autoload registration) remains active and the RAT retries C2 every 15 seconds. If the server comes back online — or if the attacker redirects DNS to a new IP — the connection will succeed automatically. Remove the packages and rotate credentials regardless of current C2 status.

Q: How do we know if the RAT actually connected and exfiltrated anything? Check outbound firewall or VPC flow logs for any TCP connections to port 2096, specifically to helper.leuleu[.]net or its resolved IP. If logs show a successful TCP handshake (not just SYN attempts), assume data was exchanged. In the absence of network logs, treat .env contents and any secrets accessible to the web process as compromised by default.

Q: Does Packagist vet packages before they're available for install? No. Packagist is a community registry with no mandatory code review or signing requirement. Any account can publish packages, and the trust model relies on community reporting and automated advisories — both of which lag behind initial publication. This is why internal controls (SBOM diffing, private mirrors with scan policies, composer audit in CI) are the only reliable prevention layer.

Q: We use Laravel Forge / Envoyer for deployments. Does that change our exposure? No — the deployment tool doesn't affect the attack surface. If composer install pulls a malicious package onto the server, the service provider registers at runtime regardless of how the deployment was triggered. The fix is upstream: prevent the package from being installed, not downstream in the deployment tooling.

Q: Should we report this to Packagist? Yes. Report malicious packages via Packagist's abuse reporting form. Also submit IOCs to your threat intelligence sharing communities (ISACs, OpenCTI instances, MISP feeds). The C2 domain helper.leuleu[.]net and the package names should be added to your internal blocklists and shared with peers in the PHP community to accelerate takedown and reduce further installations.

Secured Intel

Enjoyed this article?

Subscribe for more cybersecurity insights.

Subscribe Free