Development platform Zeabur has announced it is shutting down following its second security breach in two months — an incident in which an attacker leveraged leaked internal credentials stored in plaintext to compromise production infrastructure and exfiltrate over 3.2 million environment variable records and data belonging to 321,810 user accounts.
Let that sink in. A platform whose entire value proposition was hosting developers' applications and their configuration — including the API keys, database connection strings, and third-party service tokens that live in environment variables — lost the crown jewels because its own credentials were sitting in plaintext where an attacker could find them. The blast radius doesn't stop at Zeabur: every customer whose secrets were stored on the platform must now assume those secrets are in hostile hands.
In 15+ years of incident response, I've watched this exact failure pattern repeat across organizations of every size: secrets treated as configuration instead of as credentials, internal credentials committed to repos or wikis "temporarily," and no detection coverage on who is reading sensitive files or how much data is leaving the environment. Zeabur is the latest — and one of the most terminal — examples. This post breaks down the attack pattern and gives your SOC concrete detection logic and your engineering teams a hardening playbook.
Technical Analysis: How the Attack Unfolded
The Attack Chain
Based on Zeabur's disclosure, the intrusion followed a depressingly common pattern:
-
Credential exposure (initial access): Internal credentials were stored in plaintext — the cardinal sin of secrets management. Whether in a config file, a repository, a wiki page, or a CI/CD artifact, plaintext secrets convert any minor exposure (a misconfigured bucket, a compromised developer laptop, an overly broad repo permission) into full infrastructure access. This maps to MITRE ATT&CK T1552 (Unsecured Credentials), specifically T1552.001 (Credentials In Files) and T1552.004 (Private Keys).
-
Infrastructure compromise (valid accounts): The attacker used the leaked credentials to authenticate into Zeabur's infrastructure as a legitimate identity — T1078 (Valid Accounts). This is why credential-based intrusions are so hard to catch: there is no exploit, no malware, and no failed logon storm. Authentication logs look normal unless you baseline behavior.
-
Data collection and exfiltration: The attacker accessed and extracted 3.2 million environment variable records — meaning database reads against the platform's secret/configuration store at massive scale — plus account records for 321,810 users. Bulk reads of a secrets store followed by egress is T1530 (Data from Cloud Storage) / T1567 (Exfiltration Over Web Service) territory.
Why Environment Variables Are the Real Prize
Environment variables on a hosting platform are a secrets aggregation point. A single record can contain a production database URI, an AWS access key, a Stripe secret, or an OpenAI API key. Stealing 3.2 million of them is functionally equivalent to breaching thousands of downstream companies simultaneously — a supply-chain multiplier. Every Zeabur customer must treat this as their own incident, because their credentials are now attacker property.
Exploitation Status
This is a confirmed, executed breach — not theoretical. No CVE is associated with this incident; the root cause is operational (plaintext credential storage and insufficient access controls/detection), not a patchable software flaw. That makes it more dangerous as a lesson: you cannot patch your way out of this one.
The Second-Breach Signal
Two breaches in two months tells defenders something critical: post-incident remediation either failed or was never completed. After breach #1, credentials evidently were not comprehensively rotated, persistence was not fully evicted, or the same architectural weakness (plaintext secrets) remained. For your organization, the takeaway is that incomplete remediation is indistinguishable from no remediation.
Detection & Response
The following detections target the observable behaviors in this attack pattern: unauthorized access to plaintext secret files, anomalous bulk reads of secrets/configuration stores, and credential-based access from unexpected contexts. These are tuned to be high-fidelity — deploy, baseline, and adjust thresholds to your environment.
Sigma Rules
---
title: Suspicious Process Access to Environment Variable and Secrets Files
id: 3f8c2a71-9b4d-4e6a-b2c1-7d5e9f0a1b2c
status: experimental
description: Detects non-standard processes reading .env files, credential files, or secrets directories — a hallmark of attackers hunting for plaintext credentials as seen in the Zeabur breach.
references:
- https://www.bleepingcomputer.com/news/security/zeabur-shutting-down-after-second-security-breach-in-two-months/
- https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/01/14
tags:
- attack.credential_access
- attack.t1552.001
logsource:
category: file_event
product: linux
detection:
selection_target:
TargetFilename|contains:
- '/.env'
- '.env.local'
- '.env.production'
- '/credentials'
- '/.aws/credentials'
- '/.config/gcloud'
- 'secrets.yml'
- 'secrets.yaml'
- '/etc/secrets/'
filter_known_readers:
Image|endswith:
- '/node'
- '/python'
- '/python3'
- '/ruby'
- '/java'
- '/dotnet'
- '/php'
- '/nginx'
- '/apache2'
condition: selection_target and not filter_known_readers
falsepositives:
- Application runtimes legitimately loading .env files at startup (filtered above — extend with your own runtime paths)
- Backup agents and configuration management tools (Ansible, Chef)
level: high
---
title: Mass Read or Archive Staging of Credential and Configuration Files
id: 8a1d4e62-3c7b-4f5d-9e2a-6b8c0d1e2f3a
status: experimental
description: Detects shell commands enumerating, concatenating, or archiving environment files and credential stores at scale — consistent with pre-exfiltration staging of secrets as occurred in the Zeabur breach.
references:
- https://www.bleepingcomputer.com/news/security/zeabur-shutting-down-after-second-security-breach-in-two-months/
- https://attack.mitre.org/techniques/T1560/001/
author: Security Arsenal
date: 2026/01/14
tags:
- attack.collection
- attack.t1560.001
- attack.credential_access
- attack.t1552
logsource:
category: process_creation
product: linux
detection:
selection_enum:
CommandLine|contains:
- 'find / -name *.env'
- 'find / -iname *.env*'
- 'grep -r PASSWORD'
- 'grep -r API_KEY'
- 'grep -r SECRET'
- 'grep -r PRIVATE_KEY'
- 'cat ~/.aws/credentials'
- 'cat /etc/secrets/'
selection_archive:
Image|endswith:
- '/tar'
- '/zip'
- '/7z'
- '/gzip'
CommandLine|contains:
- '.env'
- 'secret'
- 'credential'
condition: 1 of selection_*
falsepositives:
- Developer debugging of local environment configuration (rare on production hosts — scope the rule to prod)
- Secrets-scanning tooling ( trufflehog, gitleaks ) run by authorized security staff
level: high
---
title: Cloud Secrets Store Bulk Retrieval via CLI
id: c5e9b3f8-2d6a-4c1e-8f7b-4a9d0e3c5b71
status: experimental
description: Detects bulk or enumerate-style retrieval operations against cloud secrets managers (AWS Secrets Manager, SSM Parameter Store, GCP Secret Manager) from CLI — a strong indicator of secrets harvesting with compromised valid credentials.
references:
- https://www.bleepingcomputer.com/news/security/zeabur-shutting-down-after-second-security-breach-in-two-months/
- https://attack.mitre.org/techniques/T1552/
author: Security Arsenal
date: 2026/01/14
tags:
- attack.credential_access
- attack.t1552
- attack.collection
logsource:
category: process_creation
product: linux
detection:
selection_aws:
CommandLine|contains:
- 'secretsmanager list-secrets'
- 'secretsmanager get-secret-value'
- 'ssm get-parameters'
- 'ssm get-parameters-by-path'
selection_gcp:
CommandLine|contains:
- 'gcloud secrets list'
- 'gcloud secrets versions access'
filter_automation:
ParentImage|endswith:
- '/sshd'
- '/containerd'
- '/kubelet'
condition: (selection_aws or selection_gcp) and not filter_automation
falsepositives:
- CI/CD pipeline runners and orchestration containers legitimately fetching secrets (filtered by parent process — tune to your pipeline identities)
- Break-glass administrative retrieval (should be rare and ticketed)
level: medium
KQL — Microsoft Sentinel / Defender Hunting
This query hunts for anomalous access to secret-bearing files and bulk secret retrieval behavior, correlating endpoint file access with unusual volume — the pattern that preceded Zeabur's 3.2M-record theft. It assumes Linux syslog/CEF ingestion and/or Defender for Endpoint coverage.
// Hunt: Anomalous access to .env / credential files and bulk secrets retrieval
// Deploy against production hosts and secrets-store infrastructure
let lookback = 7d;
let sensitivePaths = dynamic([".env", "/etc/secrets", "credentials", "secrets.yml", "secrets.yaml", "id_rsa", ".aws/credentials"]);
let baseline =
DeviceFileEvents
| where TimeGenerated > ago(30d) and TimeGenerated < ago(lookback)
| where FileName has_any (sensitivePaths) or FolderPath has_any (sensitivePaths)
| summarize makeset(InitiatingProcessAccountName) by DeviceName, InitiatingProcessFileName;
DeviceFileEvents
| where TimeGenerated > ago(lookback)
| where FileName has_any (sensitivePaths) or FolderPath has_any (sensitivePaths)
| summarize AccessCount = count(),
Files = makeset(FileName),
Accounts = makeset(InitiatingProcessAccountName)
by DeviceName, InitiatingProcessFileName, bin(TimeGenerated, 1h)
| where AccessCount > 50
// Flag processes not seen touching these paths in the prior 30-day baseline
or not (toscalar(baseline) has InitiatingProcessFileName)
| project TimeGenerated, DeviceName, InitiatingProcessFileName, AccessCount, Files, Accounts
| order by AccessCount desc
;
// Companion: cloud control-plane bulk secret reads (AWS CloudTrail via CEF/Syslog ingestion)
CommonSecurityLog
| where TimeGenerated > ago(lookback)
| where DeviceVendor =~ "Amazon" or AdditionalExtensions has "cloudtrail"
| where RequestURL has_any ("secretsmanager", "ssm")
and (RequestURL has "GetSecretValue" or RequestURL has "GetParameters" or RequestURL has "ListSecrets")
| summarize Reads = count(), Targets = dcount(DestinationHostName) by SourceUserName, SourceIP, bin(TimeGenerated, 1h)
| where Reads > 100 or Targets > 20
| order by Reads desc
Tune the AccessCount and Reads thresholds against your own baselines. The high-value signal is the deviation from baseline: a process or identity that has never touched secret material suddenly reading hundreds of files or making bulk API calls.
Velociraptor VQL — Endpoint Hunt
Use this artifact to sweep fleets for plaintext secrets at rest (the root cause) and for processes with live handles on secret files (the active theft).
-- Hunt: Plaintext secret files on disk + processes accessing them
-- Addresses the root cause of the Zeabur breach: credentials stored in plaintext
-- Part 1: Find plaintext secret material outside sanctioned vault paths
SELECT FullPath, Size, Mtime,
read_file(filename=FullPath, length=2048) AS Preview
FROM glob(globs=['/**/.env', '/**/.env.*', '/**/secrets.yml', '/**/secrets.yaml',
'/**/*credentials*', '/**/.aws/credentials'],
root='/')
WHERE FullPath !~ 'node_modules|/proc/|/sys/|vault|secretsmanager'
AND Preview =~ '(?i)(password|api[_-]?key|secret|token|private[_-]?key)\s*[=:]'
-- Part 2: Live processes with command lines referencing secret stores or retrieval
SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ '(?i)(secretsmanager|get-secret|ssm get-param|gcloud secrets|\.env|/etc/secrets|cat .*credential)'
AND Name !~ '(?i)(velociraptor|sshd|containerd|kubelet)'
-- Part 3: Outbound connections from shells/scripting engines (exfil staging indicator)
SELECT Pid, Name, Path, RemoteAddr, RemotePort, State
FROM netstat()
WHERE Name =~ '(?i)(bash|sh|python|perl|curl|wget|nc|ncat)'
AND RemotePort in (443, 80)
AND State =~ 'ESTABLISHED'
AND RemoteAddr !~ '^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)'
Remediation & Verification Script
Run this Bash script across Linux production hosts and CI runners to (a) locate plaintext secrets at rest, (b) verify secrets aren't world-readable, and (c) confirm no active listeners/shells are staging exfiltration. It is audit-mode (read-only) — pipe findings to your IR ticketing system.
#!/usr/bin/env bash
# secrets-hygiene-audit.sh — Audit for plaintext credentials (Zeabur-pattern root cause)
# Run as root. Read-only. Outputs findings to /var/log/secrets-audit-<date>.log
LOG="/var/log/secrets-audit-$(date +%F).log"
echo "[$(date -Is)] Starting secrets hygiene audit" | tee -a "$LOG"
# 1. Find plaintext secret files outside sanctioned locations
# Adjust EXCLUDE for your vault/agent paths
echo "=== [1] Plaintext secret files on disk ===" | tee -a "$LOG"
EXCLUDE="/proc|/sys|/var/lib/vault|/run/secrets|node_modules"
find / -xdev \( -name ".env" -o -name ".env.*" -o -name "secrets.yml" \
-o -name "secrets.yaml" -o -name "*credentials*" -o -name "id_rsa" \) \
-type f 2>/dev/null | grep -vE "$EXCLUDE" | while read -r f; do
if grep -qiE '(password|api[_-]?key|secret|token|private[_-]?key)\s*[=:]' "$f" 2>/dev/null; then
echo " [FINDING] Plaintext secret material: $f" | tee -a "$LOG"
fi
done
# 2. Check permissions on sensitive files — anything group/world-readable is a finding
echo "=== [2] Overly permissive secret files ===" | tee -a "$LOG"
find / -xdev \( -name ".env*" -o -name "*credentials*" -o -name "id_rsa" \) \
-type f -perm /044 2>/dev/null | grep -vE "$EXCLUDE" | while read -r f; do
echo " [FINDING] World/group-readable secret: $f ($(stat -c '%a %U:%G' "$f"))" | tee -a "$LOG"
chmod 600 "$f" && echo " [FIXED] Permissions tightened to 600 on $f" | tee -a "$LOG"
done
# 3. Git repos with committed secrets (common leakage vector to wikis/runners)
echo "=== [3] Secrets committed to git repositories ===" | tee -a "$LOG"
find /home /srv /opt /var/www -xdev -name ".git" -type d 2>/dev/null | while read -r repo; do
dir=$(dirname "$repo")
if git -C "$dir" grep -liE '(aws_secret_access_key|api[_-]?key|password)\s*[=:]' \
$(git -C "$dir" rev-list --all 2>/dev/null | head -50) 2>/dev/null | head -5 | grep -q .; then
echo " [FINDING] Possible committed secrets in repo: $dir" | tee -a "$LOG"
fi
done
# 4. Active outbound connections from shells/interpreters (exfil indicator)
echo "=== [4] Suspicious outbound connections from shells/interpreters ===" | tee -a "$LOG"
ss -tnp 2>/dev/null | grep -E 'users:\(\("(bash|sh|python|perl|curl|wget|nc|ncat)' \
| grep -vE '127\.0\.0\.1|::1' | tee -a "$LOG"
echo "[$(date -Is)] Audit complete. Review $LOG and rotate any exposed credentials immediately." | tee -a "$LOG"
Remediation: The Defensive Playbook
If You Were a Zeabur Customer (Immediate — Treat as Your Own Incident)
- Rotate every secret stored on the platform — now. All 3.2M environment variable records must be assumed compromised. Rotate database passwords, cloud IAM keys, API tokens (Stripe, OpenAI, SendGrid, etc.), OAuth client secrets, and webhook signing secrets. Do not prioritize — rotate everything, then revoke the old values. Rotation without revocation leaves the old keys live.
- Audit access logs on downstream services. Your AWS CloudTrail, database audit logs, and SaaS admin logs will show whether rotated secrets were used from anomalous IPs/ASNs before you rotated them. Look for access from non-business geographies, VPS/provider ASNs, and unusual user agents.
- Rotate the account credentials for your Zeabur account itself and any place that password was reused (321,810 account records were taken — credential stuffing against your other services is the obvious next move).
- Plan migration off the platform — it is shutting down. Treat data export as a forensic exercise: verify integrity of what you pull, and do not reuse any configuration that embeds old secrets.
For Every Organization (Structural Fixes)
- Eliminate plaintext secrets. Move all credentials into a dedicated secrets manager (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Doppler, 1Password Secrets Automation). Environment variables injected at runtime by an orchestrator are acceptable;
.envfiles on disk and credentials in wikis/repos/tickets are not. - Enforce secrets scanning in CI/CD. Deploy
gitleaksortrufflehogas a pre-commit hook and a pipeline gate. Any commit introducing high-entropy strings or known key formats should block the build and page the security team. - Alert on secrets-store behavior, not just access. Baseline normal read volume per identity against your secrets manager and database-backed config stores. A single identity reading thousands of records (the Zeabur pattern) should be a paging alert. CloudTrail
GetSecretValuevolume anomalies are cheap, high-fidelity detections. - Short-lived credentials over static keys. Prefer IAM roles/instance profiles, OIDC federation for CI/CD, and workload identity over long-lived access keys. A leaked credential with a 1-hour TTL is a nuisance; a leaked static key is a breach.
- Post-incident remediation must be verified, not assumed. Zeabur's second breach in two months is the canonical lesson: after any credential-exposure incident, produce a rotation manifest (every secret, its owner, rotation timestamp, revocation confirmation) and have a second team verify eviction. Assume persistence until proven otherwise.
- Vendor risk: ask your platforms how they store secrets. Zeabur demonstrates that a hosting/PaaS provider's secrets-mismanagement becomes your breach. Add "how are my environment variables encrypted at rest, and who/what can read them" to every vendor assessment.
There are no CVE patches or vendor advisories to apply here — the fix is architectural and procedural. That's precisely why this failure mode keeps ending companies.
Related Resources
Security Arsenal Incident Response Services AlertMonitor Platform Book a SOC Assessment incident-response Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.