
When AI-Generated Deployment Breaks Everything
Date Published
January 3, 2026: Monero Miner Incident
vibelie.com production server
A Monero cryptocurrency miner was discovered running on the production server:
/tmp/.x/m -o gulf.moneroocean.stream:10128 \
-u 43yiB8RenFLGQdK97HGVpLjVeQaCSWDbaec2ZQcav6e7a3QnDEmKq3t3oUoQD9HgwXAW8RQTWUdXxN5WGtpStxAtRrH5Pmf \
--cpu-max-threads-hint=75 -B --donate-level=0
š Root Cause Analysis
Payload CMS was deployed without running database migrations, leaving the database completely empty (no tables, no users). The authentication system's canAccessAdmin utility allows access when no users exist for initial setup ā but with an empty database, this created a permanent authentication bypass.
š The Timeline
Commit 375693a: Added prodMigrations to Payload config
Commit 508b37d: Created migrations/index.ts with empty array to fix TS errors
App deployed without migrations running
Monero miner discovered running as user 1001
ā ļø What Went Wrong
1. Empty migrations array: The migrations/index.ts file exported an empty array instead of running Payload CMS initialization
2. Silent deployment failure: The deploy script used || { warning ... } to continue even if migrations failed
3. No health check for auth: The app responded to health checks but had no functioning authentication
4. SSRF vulnerability: The fetchFileByURL() function in the seed endpoint could download arbitrary files
šÆ The Exploit Chain
Empty Database
No users table exists
Auth Bypass
canAccessAdmin allows access when no users
Access Endpoints
Attacker reaches protected routes
RCE
Download and execute miner via SSRF
ā Lessons Learned
Deployment Safety
- Never suppress migration failures in production
- Verify database schema exists before deploying
- Add health checks that verify auth functionality
- Run
payload migrateexplicitly during deployment
Code Review
- Empty arrays in critical paths are red flags
- AI-generated deployment scripts need review
- "Fixing TS errors" shouldn't disable functionality
- Test authentication before exposing to internet
š”ļø Implemented Solution
Added a Docker entrypoint script that checks database initialization before starting the application.
# docker-entrypoint.sh checks:
ā Database connection is accessible
ā Payload CMS tables exist (users, payload_media, etc.)
ā At least one user is registered (warns if not)
ā Fails startup with clear error if any check fails
The app now refuses to start with an uninitialized database, displaying:
ā ERROR: Payload CMS database is not initialized!
The database is missing required tables.
To fix: docker compose run --rm vibelie npx payload migrate