
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
| Command | Behavior | Platform |
|---|---|---|
ping / info | Returns system info (OS, hostname, PHP version) to C2 | All |
cmd | Executes arbitrary shell commands | All |
powershell | Spawns PowerShell process | Windows |
run | Background shell execution (fire-and-forget) | All |
screenshot | Captures screen via imagegrabscreen() | Windows |
download | Pulls file from C2 to host filesystem | All |
upload | Exfiltrates file to C2 | All |
chmod 777 | Sets file permissions post-write | Linux/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 Stage | MITRE ATT&CK | Detection Method |
|---|---|---|
| Package publication (clean) | T1195.001 | Monitor new Packagist contributors; SBOM baseline comparison |
| Malicious update pushed | T1195.001 | composer.lock diff alerts; hash verification on install |
| Service provider autoload | T1546 (Boot/Logon Autostart) | Audit config/app.php providers on deploy; compare against baseline |
disable_functions probe | T1082 (System Info Discovery) | PHP error logs; anomalous reflection calls |
| Shell execution attempt | T1059.004 / T1059.001 | Process 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 exfiltration | T1041 | Network DLP; baseline outbound volume per process |
.env / credential access | T1552.001 | File 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 Control | NIST CSF Function | CIS Control | Implementation |
|---|---|---|---|
Dependency hash verification (composer.lock) | Protect | CIS 2.3 | Commit composer.lock; use --no-dev in production |
| SBOM generation and tracking | Identify | CIS 2.1 | Generate SBOM on every build; diff against known-good |
| Private mirror / artifact proxy | Protect | CIS 2.5 | Route Packagist through Nexus or Artifactory with scan policy |
| Outbound TCP filtering (non-HTTP/S) | Protect | CIS 12.4 | Block all non-443/80 outbound from app servers by default |
File integrity monitoring on .env | Detect | CIS 8.5 | Alert on .env reads outside deployment pipeline |
| Secrets rotation post-incident | Respond | CIS 5.4 | Rotate DB creds, API keys within RTO window |
PHP disable_functions hardening | Protect | CIS 4.1 | Disable: 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:
- Remove packages —
composer remove <package-name>and redeploy - Rotate all secrets in
.env— database passwords, API keys, S3 credentials, OAuth secrets - Revoke and reissue database credentials — assume they were read by the RAT on first boot
- Pull network logs for outbound TCP:2096 — check firewall, VPC flow logs, or NSG logs for connections to
helper.leuleu[.]net - 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 logsnetstat -antpoutput (or equivalent) for open connections- Process tree snapshot (
ps auxfon 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.lockagainst 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[.]netor 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 list —
exec,shell_exec,system,passthru,popen, andproc_openmust 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.
Enjoyed this article?
Subscribe for more cybersecurity insights.
