I made a rookie mistake.

I built a Telegram bot that fetches top stories from Lobste.rs — ones scoring above a threshold I set. Straightforward enough. The problem was deployment.

I’m not great at setting up servers, so I turned to AI for help. I don’t usually accept AI output blindly, but this time I did, and it gave me this:

# docker-compose.yml
services:
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: ${MYSQL_DB:-lobster_bot}
      MYSQL_USER: ${MYSQL_USER:-lobster}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-lobsterpassword}
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"

The bot ran fine for two days. Then the Telegram notifications stopped.

I assumed it was a bug in my code. It was a busy week, so I didn’t get around to investigating until today.


The Discovery

I SSHed into the server and checked the logs. The app was throwing a database connection error — the database didn’t exist. I jumped into the Docker container to take a look, and the database was gone. In its place:

Database
RECOVER_YOUR_DATA
information_schema
mysql
performance_schema
sys

I opened RECOVER_YOUR_DATA and found a ransom note:

All your data was backed up. You must pay 0.0101 bitcoin to
bc1qtjukj94f5ph89n3uwryv55s3n8qugfn37c6cf9 or in 48 hours,
your data will be publicly disclosed and deleted.

After payment, send mail to ak+23jka@onionmail.org and we will
provide a link to download your data. Your DATAID is: 23JKA

I had been hacked.


What Actually Happened

I dug into it with some online research and had Claude run an audit against the server logs. The summary:

A publicly exposed MySQL instance on a VPS was compromised twice by an automated ransomware bot targeting internet-facing MySQL servers. The attacker brute-forced the root account (password: root) via the exposed port 3306, dropped the lobster_bot database, and replaced it with a ransom demand. This was not a targeted attack — it was an opportunistic, fully automated campaign known as “Akira MySQL Ransomware”. No OS-level compromise occurred. The attacker’s claim of holding a backup is almost certainly false.

“Akira MySQL Ransomware” isn’t ransomware in the traditional sense, there’s no file encryption. It’s a fully automated bot that:

  • Scans the internet for exposed MySQL servers
  • Logs in with weak or default credentials
  • Drops all databases
  • Leaves a ransom note

They don’t have your data. They just delete it and bluff to get paid.


Timeline

The audit reconstructed exactly what happened:

Apr 18, 2026 — 00:13 UTC
  └─ MySQL container starts. Port 3306 bound to 0.0.0.0 (internet-exposed).

Apr 18 → Apr 21, 2026
  └─ Automated scanner finds port 3306.
     Brute-force on root@% with password "root" succeeds.
     Attacker runs:
       DROP DATABASE lobster_bot;
       CREATE DATABASE RECOVER_YOUR_DATA;
       INSERT INTO RECOVER_YOUR_DATA ... (ransom note);

Apr 21, 2026 — 13:50 UTC
  └─ Server rebooted by owner.

Apr 21, 2026 — 14:17 UTC
  └─ Owner recreates Docker stack (docker compose up -d).
     ⚠ Port 3306 still exposed. Root password still "root".

Apr 24, 2026 — 00:50 UTC
  └─ Bot strikes again on the new container.
     lobster_bot dropped. RECOVER_YOUR_DATA re-inserted.

Apr 24, 2026 — 05:46 UTC
  └─ App starts logging error 1049: "Unknown database 'lobster_bot'".
     All 23 story fetches fail with OperationalError.

Apr 25, 2026 — 06:02 UTC
  └─ Incident discovered.

Notice that when I rebooted and brought the stack back up, I fixed nothing — port 3306 was still exposed and the root password was still root. The bot hit me again within 3 days.


The Fix

The data loss didn’t matter much. The bot only stored story_id and a published flag, nothing sensitive or irreplaceable.

I tore down the container and rebuilt it properly:

# docker-compose.yml
services:
  db:
    image: mysql:8.0
    container_name: lobster_db
    restart: always
    env_file:
      - .env
    environment:
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
      MYSQL_DATABASE: "${MYSQL_DB}"
      MYSQL_USER: "${MYSQL_USER}"
      MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
    volumes:
      - mysql_data:/var/lib/mysql
    # Port 3306 no longer exposed to the host

Key changes: credentials moved to .env, strong passwords, and most importantly port 3306 is no longer exposed.

I also rotated my VPS password after spotting some unusual login session entries. SSH wasn’t the attack vector though, but the audit confirmed it:

  • All successful SSH logins came from my own IPs (157.51.x.x, 122.167.x.x)
  • Brute-force SSH attempts from 2.57.121.x, 2.57.122.x, 80.94.92.x were all rejected

The lesson isn’t really “don’t use AI.” It’s: don’t expose a database port to the internet with a default password. That’s day-one ops knowledge, and I skipped it.

So yeah, review AI-generated config codes before running it. I never was. I never will.