logo

Zeabur October 2026 security incident

An attacker used leaked internal credentials to break into the shared cluster and GCP Dedicated Servers, and then read the environment variables stored in the Zeabur platform database. This post covers what happened, which data was affected, how we responded, and what you need to do.

Zeabur TeamZeabur Team

This article is also available in 繁體中文, 简体中文, 日本語, Español, and Bahasa Indonesia.

All times in this post are in Coordinated Universal Time (UTC).

On October 15, 2026, at 13:56, a user report led us to start an investigation. When we went back through the logs, we found that the attacker had started using Zeabur's internal credentials on October 14 at 01:36, about 36 hours before we noticed. Throughout that time, the attacker used valid credentials over Zeabur's official access channels, and no alert fired.

The attacker came in through two paths that met inside Zeabur's internal network on October 15, and from there read the platform database. None of the credentials used this time were covered by the rotation after the August incident.

Summary
ItemDetails
StatusFinal
What you need to doReplace every password and key stored on Zeabur, including those you replaced after the August incident. See What you need to do
Incident windowOctober 14, 01:36 to October 15, 21:05 (the attacker's last activity in the logs)
DetectedOctober 15, 13:56, from a user report, about 36 hours after the attacker first used the credentials. See Detection
ContainedOctober 15: attacker's internal network node removed at 17:58, public SSH to GCP Dedicated Servers blocked at 20:36, all users signed out
ImpactEnvironment variables and account data in the platform database, services and databases on the shared cluster, and 101 GCP Dedicated Servers. Environment variables that had been moved to users' own servers after August were outside the scope of the read. See What information was involved
Most likely causeZeabur's private configuration repository leaked; how it leaked cannot be confirmed. The repository held long-lived credentials in plaintext, and the cluster admin interface was open to the public internet. See Root causes
Terms used in this post
TermMeaning
ConfirmedA log records it directly
AssessedThe timing and content match, but there is no direct record
Cannot be confirmedThere are no records to check, and we treat it as leaked
HypothesisA possible explanation backed only by indirect evidence, collected in Unconfirmed hypotheses

What you need to do

Do this now, without waiting for a notification email. Leaked data can still be misused, and credentials you replaced after the August incident need to be replaced again.

All users

  1. Replace every credential in your environment variables. This includes API keys, cloud keys, database passwords, and signing and encryption keys. Revoke the old ones with the service provider first, then create new ones and redeploy. If your project moved its environment variables to your own Dedicated Server after August, those variables were outside this read of the platform database. If the host itself was compromised, you still need to replace the credentials stored on it.
  2. Sign in again and create new Access Tokens. All old platform Access Tokens have been revoked. Create new tokens on the Access Token management page, and check for requests you don't recognize.
  3. zSend users: Create new zSend API keys.
  4. GitHub users: In your GitHub audit log, check actions by the Zeabur GitHub App on October 15 from 15:05 to 18:54. Look for:
    • Unexpected clones or pushes
    • New deploy keys
    • Modified webhooks
  5. Check your bills. Review usage and billing for each service from October 14, 2026, onward. GCP Dedicated Server users should go back to September 1. October 14 is the earliest time we know the attacker used the credentials, but they may have leaked earlier, so if you have older records, check those too.

Shared cluster users

  1. Change the passwords inside your databases. Changing environment variables on Zeabur leaves the password inside the database unchanged. A new password also does nothing about data that has already leaked. If your database stores your users' password hashes, login sessions, or tokens, revoke and regenerate those as well.
  2. n8n: Replace the n8n encryption key, and create all third-party credentials in n8n again.
  3. new-api/one-api: Revoke the upstream AI service keys and generate new access tokens.
  4. PostgreSQL: Check for roles, functions, triggers, scheduled jobs, or extensions you don't recognize. If in doubt, create a new database and import only the data.
  5. Taipei (tpe1) and Hong Kong (hkg1) regions: Treat the data in your database backups as leaked.

GCP Dedicated Server users

Treat your server as compromised:

  1. Back up your data and rebuild the server. This is the most reliable way to remove backdoors.
  2. If you can't rebuild yet, at minimum:
    • Remove keys in /root/.ssh/authorized_keys with the comment @zeabur-pentest, and any keys you don't recognize.
    • Check the google-sudoers group, system accounts, crontab, systemd services, and k3s workloads.
    • Change the root password and turn off password login for SSH.
  3. Replace every credential on the server, including your own cloud credentials, and check the related bills.
  4. Check sign-in records against the indicators of compromise, going back to September 1, 2026.

Hetzner and DigitalOcean Dedicated Server users

In the provider's console, check whether the root password was reset or the system was reinstalled after October 14. Then go through the GCP checks above to make sure there is no sign-in method you don't recognize.

What information was involved

SystemAffected dataScopeCertainty
Platform databaseService environment variables stored on Zeabur (names and values)3,218,965 recordsConfirmed
Platform databaseAccounts (email, username, phone number, and so on), project, environment, and service data321,810 accountsConfirmed
Platform databasezSend API keys (plaintext)566 keys, revokedConfirmed
Platform databasePlatform Access Tokens11,536 tokens, stored only as hashes that cannot be reversed; all revokedConfirmed
Shared clusterEnvironment variables and secrets of all services9 regionsPartly confirmed; the rest cannot be confirmed
Shared clusterDatabase contents865 database servicesAssessed (see below)
GCP Dedicated ServersAll data on the servers101 servers, 92 usersConfirmed (sign-ins)
Hetzner and DigitalOcean Dedicated ServersAdmin access to the servers359 and 48 serversCannot be confirmed (use)
GitHub AppAccess to the GitHub repositories users authorized10/15 15:05 to 18:54Cannot be confirmed (use)
User database backupsBackups in the Taipei (tpe1) and Hong Kong (hkg1) regionsTwo regionsCannot be confirmed (download)

The details for each system follow.

Platform database

From 16:10 to 16:47 on October 15, the attacker read the data listed in the table above. Account data includes the information on your profile page, such as email, username, and phone number. For the two-factor authentication and passkey tables, the attacker queried only statistics, and there is no record of their contents being read. Platform Access Tokens are stored only as hashes, and we revoked all of them anyway.

The attacker was mainly looking for API keys they could use right away, especially keys for AI services. They searched environment variables and config files for API keys and private keys, read credentials from n8n and new-api/one-api in bulk, and tried to call Amazon Bedrock with Zeabur's AWS keys. We drew this from the commands they ran and what they did afterward. The attacker also read account data, and we treat everything they read as leaked.

After the August incident, Zeabur gave Dedicated Server users guidance and a one-click migration: rotate credentials, delete the environment variables and config files stored on Zeabur, and keep them on your own server instead. For projects that completed the migration, this data exists only on the user's server and was outside the scope of this read. For projects that had not migrated, environment variables were still stored on Zeabur and were read this time. Leaks from a compromised host are a separate matter, and compromised GCP Dedicated Servers still need the steps in What you need to do.

Account impersonation

From 16:12 to 17:19 on October 15, the attacker called the Zeabur API as two accounts and read account and project lists. One of the two was a Zeabur employee's account. These requests did not modify any resources.

The attacker read the key used to sign login credentials, and we assess that they forged login credentials with it. Platform Access Tokens are stored only as hashes, and the attacker did not read them in bulk until 16:47, so the tokens cannot explain impersonation that started at 16:12. At 20:25, we rotated the signing key and all users were signed out.

Shared cluster databases

The attacker ran read commands inside the containers of 865 databases. The audit logs record commands but not their output, and some commands failed with errors, so we cannot confirm how much was actually taken from each database. The table below is our judgment based on the commands:

TargetCountData read
n8n using PostgreSQL736Third-party credentials, users, projects, variables, and workflows
PostgreSQL (read table by table)211Up to 50,000 rows per table; none in the Taipei (tpe1) region
new-api/one-api10AI service keys, access tokens, and users
Redis1Entries whose names contain words such as token or secret
MongoDB, MariaDB1 eachTaipei (tpe0) has no command contents; treated as fully leaked

We checked 607 of the n8n instances using PostgreSQL, and 602 of them had their encryption key in an environment variable. For those, the third-party credentials stored in n8n were effectively exposed in plaintext. These reads happened inside the containers, so rotating database passwords after the August incident gave no protection against them.

Separately, at least 2 PostgreSQL databases in the Tokyo (hnd1) region had a program planted from October 14 at 01:51 that connected back to an outside host. One of them was still connecting back on October 15 at 16:24. Whether the way in was a password leaked in August cannot be confirmed. When we found this, we told the affected users to replace their credentials.

GCP Dedicated Servers

The attacker signed in to 101 servers with root privileges, most recently on October 15 at 20:36. On 50 of them, the attacker added SSH public keys they controlled, so they could sign in again later. They also created 5 virtual machines in Zeabur's GCP project and attached copies of 16 customer server disks to them.

We treat the data on all 101 servers as possibly leaked, and the servers as possibly backdoored. We have emailed the affected users and reset the root passwords of these servers.

Hetzner, DigitalOcean, Linode, and Tencent Cloud

The tokens that manage customer servers on Hetzner and DigitalOcean were written in internal code and environment variables, and leaked with the platform database. Anyone holding them could reset passwords, reinstall systems, or copy disks. We revoked all of these tokens on October 16 at 01:09. Neither provider offers an API for operation audit logs, so we cannot confirm whether the tokens were used. We ruled out one thing: the password resets and image creation on Hetzner on October 15 were done by the customers themselves and by scheduled system jobs. We have no comparable result for DigitalOcean.

Of 890 Linode servers, the 847 that have records show no sign-ins by the attacker. There is no record of Tencent Cloud's admin credentials being used.

GitHub App

On October 15 at 15:05, the attacker read the private key of the Zeabur GitHub App. With this key, anyone can access the repositories users have authorized Zeabur to access. We rotated the key at 18:54. Whether the key was used cannot be confirmed: we did not get the GitHub organization's full audit logs, and GitHub Support never replied to our request for them.

Zeabur internal systems

The attacker used several of Zeabur's AWS keys. Most requests were denied, but the attacker listed the user database backups in two regions and the contents of source code uploads. Whether they downloaded any of it cannot be confirmed. Every attempt to call Amazon Bedrock failed, and none of them incurred charges.

Service impact

FeatureImpact period (UTC)Status
Sign-inSign-in required again from 10/15 20:25Normal
zSend and system emails (including login links)10/15 20:00–22:02Restored; zSend keys must be created again
GitHub sign-in and deployments10/15 18:54–22:49Restored
Builds10/15 20:05–22:56; China regions until 10/16 01:44Restored
Database backups10/15 20:33–22:13Restored
GCP Dedicated Server management10/15 17:31–20:35Restored; web SSH only
Dedicated Server auto-renewalTencent Cloud 10/15 22:55–10/16 01:08, 154 failuresRestored; charges refunded as account balance
Tencent Cloud password reset and SSH login recordsFrom 10/15Restored
Linode dedicated Kubernetes clustersFrom 10/15Restored

What happened

The attacker took two paths. Path A went from the shared cluster into Zeabur's AWS account and secrets management service. Path B got root access to GCP Dedicated Servers. On October 15, the two paths met inside Zeabur's internal network. The MITRE ATT&CK technique for each step is in the appendix.

Incident path
The two paths met in the internal network. Next to each arrow is the credential or gap the attacker used at that step.
Path A: Shared cluster and AWS
Starting point
Already held AWS keys and cluster admin tokens
How they were obtained cannot be confirmed
Admin tokens had no expiry, and the admin interface was open to the public internet
Shared cluster in 9 regions
Environment variables of all services, 865 database containers
Assessed: an over-privileged key read an old cluster backup
AWS
Key to the secrets management service
Confirmed: production secrets read
Zeabur secrets management service
About 53 production secrets
Including internal network admin credentials and database accounts
Path B: GCP Dedicated Servers
Starting point
An old GCP service account key
The source of the leak cannot be confirmed
Assessed: root passwords read from initialization settings
GCP Dedicated Servers
Root access to 101 servers
Joined a node from a compromised server using internal network admin credentials
Zeabur internal network
A node with production access
Internal network rules allowed the connection; signed in with an internal read-only account
Zeabur platform database
Environment variables, account, and project data stored on Zeabur
Figure 1: The attacker joined the node from a server compromised through Path B, using internal network credentials from Path A. Joining the internal network did not actually require compromising a customer server first.

Path A: From the shared cluster to the secrets management service

  1. Getting the credentials. On October 14 at 01:36, the attacker started using Zeabur's internal AWS keys, and at 01:40, the shared cluster's admin tokens. Both were stored in plaintext in Zeabur's private configuration repository, in the same settings as each cluster's connection address. How the attacker got them cannot be confirmed. Our hypotheses are in Unconfirmed hypotheses.
  2. Getting into the shared cluster. The admin tokens had no expiry, and the cluster admin interface was open to the public internet at the time, so the attacker could reach the shared cluster in 9 regions from outside. From 01:51, they read some system secrets. From 12:39, they widened this to the settings and secrets of every service, including environment variables. In regions with complete read logs, we confirmed the content was read. We treat all other regions as leaked.
  3. Reading user databases. From 13:40, the attacker ran read commands inside user database containers. They began with a few new-api/one-api instances, read n8n in bulk from 15:41, and read some PostgreSQL databases table by table from 16:50. The last command ran at 17:04. In all, 865 database services were involved, and not every command succeeded.
  4. Getting the secrets management key from a backup. With another over-privileged key found in the cluster, the attacker read a backup of an old cluster (Assessed). The backup held a key that could read Zeabur's secrets management service.
  5. Reading production secrets. From 14:52 to 16:51 on October 15, the attacker read about 53 production secrets (Confirmed), including the internal network admin credentials, platform database accounts, and the GitHub App private key.

Path B: Root access to GCP Dedicated Servers

  1. Signing in to the servers. From October 14 at 13:40, the attacker signed in as root to the Dedicated Servers that Zeabur runs on GCP, 101 in all. When these servers were created, the root password was written in plaintext into the initialization settings and was not removed after setup. The platform itself signs in to the servers with this password, so it was kept in plaintext, not as a hash. The old GCP key the attacker held could read these settings, and we assess that this is how they got the passwords.
  2. Adding SSH keys. From October 15 at 14:26, the attacker used the old GCP key to change server settings and add their own SSH key. At 15:05, they also got a production GCP key from the secrets management service.
  3. Switching keys and trying again. We disabled the old key at 17:17, and 11 minutes later the attacker added the SSH key again with the newly obtained key. We disabled that one at 17:31.

These sign-ins happened on October 14, more than a day before Path A's earliest known read of production secrets (October 15 at 14:52). The secrets read through Path A therefore cannot explain how Path B started. Between September 1 and 5, an unknown source also signed in to the same servers with the root password, reaching about 98 servers on September 5 alone. Whether this was the same attacker cannot be confirmed.

Where the two paths met

  1. Joining the internal network. On October 15 at 15:30, on a compromised GCP server, the attacker used the internal network admin credentials from Path A to join a node to Zeabur's internal network.
  2. Reading the platform database. Internal network rules let this kind of node connect to the platform database. From 16:10 to 16:47, the attacker read the platform database with an internal read-only account (Confirmed).
  3. Removing the node. At 17:58, we removed the node.

Our conclusion that the platform database was read comes from connection records at both ends of the internal network and from the database logs. All 29 database connections in this period map to this node, and we can trace each sign-in and query within them, 8,903 operation records in total.

Detection

On October 15 at 13:56, a user report led us to start the investigation. By then, about 36 hours had passed since the attacker first used the credentials (October 14 at 01:36), and the attack activity on October 14 alone had run for more than 16 hours.

No alert fired during this time:

  • The attacker used valid credentials over Zeabur's official access channels. In the logs, their actions looked the same as normal admin work.
  • We had no alerts for this kind of activity, such as unusual use of admin access, commands run inside containers, bulk reads of secrets, new nodes joining the internal network, or cloud keys used from unfamiliar sources.

Response

Containment

About 4 hours after we started the investigation, we removed the attacker's internal network node. Our actions on October 15 and 16:

Time (October 15)Action
14:43–15:56Allowed only Zeabur systems to connect to the shared cluster's admin interface
15:32Revoked the cluster admin tokens that were used; rotated again at 21:47
17:17, 17:31Disabled the two GCP keys the attacker used
17:58Removed the attacker's internal network node and revoked the credential that created it
18:51Deleted the cluster backup access key
18:54Rotated the GitHub App private key, payment service key, and GitHub OAuth secret
18:55Removed root passwords from GCP server initialization settings
20:05–21:22Disabled 24 AWS keys that had leaked or could not be ruled out, and replaced them with new keys with narrower permissions
20:25Rotated the login signing key; all users were signed out
20:34–20:36Removed permissions from the GCP servers' default service account; blocked public SSH to GCP servers
20:47Revoked all 566 zSend API keys
21:46Allowed only Zeabur systems to connect to Zeabur-managed dedicated clusters
10/16 01:09Revoked the leaked Hetzner and DigitalOcean tokens

Keys rotated during the incident were used again

Some of the credentials we put in place on the day of the response were later used by the attacker. The cause was the cluster's daily backup. It covers every resource in the cluster, secrets included, and on October 15 at 15:04 the attacker read the access key for those backups:

  1. At 15:40, we put a new key in place.
  2. At 16:00, the daily backup captured the new key.
  3. At 18:51, we deleted the backup access key.
  4. At 20:30, the attacker used the new key.
  5. At 21:47, we rotated the affected credentials again.

That the backup captured the new key and that the key was later used are both confirmed. That the attacker downloaded this backup is assessed. The key used at 20:30 may also have come from a backup the attacker had already obtained before 18:51.

Eradication and recovery

  • Removed the attacker's SSH keys from the GCP Dedicated Servers, reset the root passwords, and emailed the affected users.
  • Deleted the disk copies, snapshots, and test resources the attacker left behind.
  • Revoked all users' platform Access Tokens.
  • Disabled the Tencent Cloud, Alibaba Cloud, and Linode credentials hardcoded in our code.
  • Rotated the remaining production secrets that were read, including the platform database account passwords, and revoked other leaked credentials.
  • Preserved the audit logs from each system.
  • Restored every affected feature. The impact period for each is in Service impact.

Root causes

From the start, the attacker held Zeabur's cluster admin tokens and several cloud keys. These credentials were kept in Zeabur's private configuration repository, in plaintext, as had long been the practice. The most likely explanation is that this repository leaked, but how it leaked cannot be confirmed. Our hypotheses are in Unconfirmed hypotheses.

Gaps that let the attacker reach the platform database

  • Admin tokens had no expiry, and the admin interface was open to the public internet. The tokens were created in January 2024 and never rotated, and the rotation after the August incident did not include them.
  • Secrets were stored in plaintext. Plaintext keys existed in the configuration repository, in code, and in shared cluster service settings and build jobs. GCP server initialization settings stored root passwords. We found this on September 3 and had not fixed it before the incident. A secrets migration that started on October 3 had not been merged into the main branch when the incident happened.
  • Backups held every secret, and the backup key sat alongside the other secrets. This let the attacker get more credentials from the backups, and later backups captured some of the credentials we rotated during the incident.
  • Permissions were broader than needed.
    • A key that only needed to read one storage bucket could read every bucket in the AWS account.
    • The default service account on GCP servers had project-level write permissions.
    • Internal network rules let nodes with a certain tag connect to the platform database, and the internal network admin credentials could give a new node that tag directly.
    • The internal read-only database account could read all data.
    • A deploy key for a decommissioned service was still valid four months after the service was shut down.
  • Internal services and user services ran on the same cluster. When the attacker read user settings, they got the credentials of internal services too.
  • Detection and logging fell short. The attack ran for about 36 hours without triggering an alert; see Detection. Access logs for storage, GCP, and GitHub were incomplete, so we could not determine some of the access and the full scope of impact.

Relation to the August incident

This is Zeabur's second security incident in less than two months. Many users changed their setup following our guidance after August, and want to know why this still happened and whether the two incidents are related.

What we did after August. We rotated the internal keys confirmed as leaked at the time and disabled Legacy API Keys. In the product, projects on Dedicated Servers can move environment variables and config files to their own servers, delete the SSH credentials Zeabur stores, and view SSH login records. Our notices and announcements about the August incident also advised users to rotate credentials, delete the environment variables and config files stored on Zeabur, and move them to their own servers.

Why it still happened. The August rotation did not cover the cluster admin tokens and AWS keys stored in plaintext in the configuration repository, and the root passwords in GCP server initialization settings had not been fixed. Those are the credentials the attacker used this time. The August incident report said that "all internal admin keys have been disabled or rotated." That statement was incomplete, and we are correcting it here.

Lessons learned

What went well

  • For projects that completed the migration as advised in August, environment variables and config files existed only on the users' own servers and were not in the platform database data read this time.
  • About 4 hours after we started the investigation, we removed the attacker's internal network node. The same day, we blocked public access to the cluster admin interface and to GCP Dedicated Servers.
  • Connection records at both ends of the internal network and the database logs were complete, so we could confirm the platform database read record by record.

What went wrong

  • The rotation after the August incident did not cover the credentials used this time, and the August incident report was incomplete. See Relation to the August incident.
  • We found the root passwords in GCP server initialization settings on September 3 and did not fix this before the incident.
  • The daily backup captured keys we put in place on the day of the response, and some were later used. See Keys rotated during the incident were used again.
  • The attack ran for about 36 hours, and we found out only from a user report. See Detection.

Action items

Zeabur will shut down on December 31, 2026, so this list includes only improvements we have already made.

ItemStatus
Revoke the cluster admin tokens that were used; allow only Zeabur systems to connect to the admin interfaceDone
Remove root passwords from GCP server initialization settings; block public SSHDone
Replace AWS keys with new keys with narrower permissionsDone
Allow only specific services to connect to the platform databaseDone
Stop storing plaintext secrets in cluster backups; keep backup keys separate from other secretsDone
Add alerts for unusual use of admin access, commands run inside containers, bulk reads of secrets or databases, new nodes joining the internal network, and cloud keys used from unfamiliar sourcesPartly done

Open questions

What we could not determine

With the records available, we still could not determine the following when we finalized this report. We treat any data and credentials where access cannot be ruled out as leaked.

  • Where the initial credentials came from. GitHub audit logs record archive downloads but not clones. They show that someone downloaded the configuration repository with an employee's GitHub authorization on August 30 and September 2, but we cannot confirm who. We asked GitHub Support for complete records and never received a reply.
  • Where the old GCP key leaked from. We searched the git history, the secrets management service, and old cluster backups, and found no leak point. We also reached no conclusion on whether 31.57.201.201, which signed in to GCP Dedicated Servers with the root password in early September, was the same attacker.
  • How much was actually taken from shared cluster databases. The audit logs kept the commands but not their output, so we know only which commands the attacker ran.
  • What was downloaded from backups and storage. Evidence on a compromised host confirms that on October 15 at 15:42, the attacker got a customer database backup through a presigned download URL. This is a different thing from the cluster backup that contained internal credentials. For the database backups in Taipei (tpe1) and Hong Kong (hkg1), and the storage for source code uploads, we can confirm only that the object lists were read. Object access logging was not enabled at the time, so whether anything was downloaded cannot be confirmed. There is also no direct evidence that the old cluster backup was read, so that remains Assessed.
  • What was read from GCP resources. Data access audit logs were not enabled in the GCP project at the time. Only write operations were logged, and there was no other way to check afterward.
  • Whether the Hetzner and DigitalOcean tokens were used. Neither provider offers an API for operation audit logs. Apart from the actions on Hetzner on October 15 that we confirmed were done by the customers themselves and by scheduled system jobs, this cannot be confirmed.
  • Whether the GitHub App private key was used. We did not get the GitHub organization's full audit logs, and GitHub Support did not reply.
  • What the platform database queries returned. We have confirmed which queries the attacker ran, how many records 7 of the main queries returned, and that 14 collections returned data. The main queries all read through to the last record. The database logs do not record what was returned, and we cannot know how the attacker later stored or used the data.
  • Activity on some clusters. Audit logs for the Taipei (tpe0) cluster and for the dedicated clusters where the attacker created resources are incomplete, and we did not investigate them further.
  • What happened before October 14. Shared cluster and AWS records show no activity with valid credentials before October 14 at 01:36. Some IPs later used to abuse the credentials appear in Zeabur API access logs from October 10 on, without signing in. These IPs may be proxy servers shared by many people, so a matching IP alone does not confirm the same person. On October 13 there were also SSH sign-in attempts against Dedicated Servers, and none succeeded. Whether any data leaked during this period cannot be confirmed.

Unconfirmed hypotheses

These are the explanations we consider most likely. Indirect evidence supports them, but there are no direct records, so do not cite them as fact. As of this final report, none has been proven or disproven.

Hypothesis 1: The initial credentials came from the August incident

  • Hypothesis: The attacker used the GitHub App private key leaked in the August incident to get the contents of Zeabur's private configuration repository, including the AWS keys and cluster admin tokens used on October 14. No evidence directly proves this path. The points below are indirect clues that fit in timing and capability.
  • What was replaced and what was not:
    • The GitHub App private key was replaced. In the August incident, this key was read on August 27. GitHub audit logs and the version timestamps in the secrets management service show it was replaced on August 31, leaving about 4 days in which it could be used. The App is installed on Zeabur's GitHub organization, and the private key can read every repository in the organization.
    • The plaintext credentials in the configuration repository were not replaced. The AWS keys and cluster admin tokens the attacker first used on October 14 were already in plaintext in the configuration repository on August 27. After the August incident, we replaced only the private key, not these credentials, and they stayed active until our response on October 15. So even with the private key replaced, anyone who got the repository contents between August 27 and 31 would have held credentials that still worked in October.
  • Other supporting clues:
    • Some internal secret names the attacker tried to read appear only in another internal repository. The attacker may have held the contents of both repositories, which fits the App private key's organization-wide access.
    • Before October 14 at 01:36, there is no record of valid credentials being used on the shared cluster or AWS, and the IPs later used to abuse the credentials never signed in to the Zeabur API. This fits an attacker who held only the credentials from the repository.
  • Clues against it: The attacker IPs in the August incident and in this incident do not overlap.
  • Why it cannot be proven: We asked GitHub Support for the access tokens this App issued between August 27 and 31, the clone and download records for the configuration repository, and the source of the August 30 and September 2 downloads made with an employee's authorization. GitHub Support never replied. The downloads made with an employee's authorization could also be a separate way the credentials were obtained.

Hypothesis 2: The probing before October 14 was reconnaissance by the same attacker

  • Hypothesis: The anonymous probing and SSH attempts from October 10 to 13 were the same attacker scouting before using the credentials.
  • Supporting clues:
    • 103.237.102.191: browsed Zeabur without signing in on October 10 at 18:27, and accessed the shared cluster with an admin token on October 14.
    • 23.106.46.239: made a large number of unauthenticated attempts against the Zeabur API from October 12 at 12:52, attempted SSH sign-ins to Dedicated Servers on October 13, and first used the leaked AWS key on October 14 at 01:36.
  • Why it cannot be proven: The attacker made heavy use of proxy servers, and different people may share one IP, so a matching IP alone does not confirm the same person.

Hypothesis 3: The deploy key access on October 15 was the attacker

  • Hypothesis: On October 15 at 15:02, a deploy key belonging to a decommissioned service read the configuration repository. We assess this was the attacker.
  • Supporting clues: The access happened while the attacker was reading production secrets (14:52 to 16:51), and the key's service had been decommissioned for four months, so there should have been no legitimate access.
  • Why it cannot be proven: No record links this access directly to the attacker.

Appendix

Timeline

Attacker activity and our response
Attacker activity on each system and our actions on October 14 and 15, plotted in UTC
Attacker activity
Attacker had access, no continuous record
Our response
Figure 2: For each system, attacker activity is shown above and our actions below. In the roughly 36 hours from the first known credential use (10/14 01:36) to the start of the investigation, none of the access triggered an alert. About 4 hours after the investigation started, the attacker's internal network node had been removed.
Time (UTC)Event
January 10, 2024Shared cluster admin token created with no expiry
January 20, 2026Admin token written in plaintext to the configuration repository
August 27GitHub App private key read during the August incident; its link to this incident is a hypothesis
August 30, September 2Configuration repository downloaded as an archive with an employee's GitHub authorization; who downloaded it cannot be confirmed
August 31GitHub App private key replaced; plaintext credentials in the configuration repository not rotated
September 1–5An unknown source signed in to about 98 GCP Dedicated Servers with the root password
September 3Root passwords found in GCP server initialization settings; not fixed before the incident
October 10, 18:27An IP later used to abuse credentials first appeared in Zeabur API access logs (not signed in)
October 13, 21:27Attacker IP attempted SSH sign-ins to 3 Dedicated Servers until 23:04; all failed
October 14, 01:36Attacker first used a Zeabur AWS key
October 14, 01:40Attacker first accessed the shared cluster with an admin token
October 14, 12:39Attacker read the settings and secrets of all services on the shared cluster
October 14, 13:40Attacker began reading data inside database containers and began signing in to GCP Dedicated Servers
October 14, 17:04Last command inside a database container
October 15, 13:56Investigation started after a user report
October 15, 14:43Began restricting connection sources for the shared cluster admin interface
October 15, 14:52Attacker began reading about 53 production secrets, until 16:51
October 15, 15:02A deploy key for a decommissioned service read the configuration repository (hypothesized to be the attacker)
October 15, 15:30Attacker joined a node to Zeabur's internal network
October 15, 15:42Attacker downloaded user database backups from Zeabur storage
October 15, 16:06Attacker searched the obtained data for API keys, until 18:13
October 15, 16:10Attacker began reading the platform database, until 16:47
October 15, 16:12Attacker began calling the API as impersonated accounts, until 17:19
October 15, 17:58Attacker's internal network node removed
October 15, 19:50Attacker's second wave of sign-ins to GCP Dedicated Servers, until 20:36
October 15, 20:25Login signing key rotated; all users signed out
October 15, 20:36Public SSH to GCP Dedicated Servers blocked; attacker sign-ins stopped
October 15, 20:48Status page updated to show the platform database and Dedicated Servers were also affected
October 15, 21:01Attacker attempted to access shared cluster nodes and was denied
October 15, 21:05Last attacker activity observed (AWS)
October 16, 01:09Hetzner and DigitalOcean tokens revoked

Indicators of compromise

If you find any of these indicators in your systems or in your service providers' logs, treat the related credentials as leaked and contact us. The attacker made heavy use of rotating proxy servers, so the table lists only representative addresses. Confidence uses the STIX 0–100 scale: Confirmed is 85, Assessed is 60, and Cannot be confirmed is 30. A machine-readable version is in the STIX 2.1 file, where the IP indicators are valid until January 13, 2027.

TypeIndicatorSeen inConfidence
IP23.106.46.239Cluster access, cloud APIs, SSH sign-ins85
IP102.244.78.57Cloud APIs, GCP settings changes85
IP103.237.102.191Cluster access, cloud APIs85
IP103.112.139.162First use of a cluster admin token85
IP45.146.232.28AWS key validation85
IP216.98.11.198, 45.58.38.175Dedicated Server SSH sign-ins85
IP52.220.72.188Callback address of the program planted in PostgreSQL85
IP31.57.201.201Root password sign-ins to GCP Dedicated Servers in early September; link to this incident cannot be confirmed30
SSH keyComment *@zeabur-pentest, fingerprint SHA256:EdBc0uephoOMemIRKK0SRV2TRR45n52tJm1yErVU1iMKey the attacker added to Dedicated Servers85
User-AgentContains both Boto3/1.43.111 and os/linux#6.12.86+deb13-amd64Client the attacker used with AWS keys. Matching on this alone gives false positives, so combine it with the source IP and the key60

MITRE ATT&CK mapping

Ordered by MITRE ATT&CK tactic.

TacticTechniqueSection
Initial AccessT1078.004 Valid Accounts: Cloud AccountsPath A, step 1
Initial AccessT1133 External Remote ServicesWhere the two paths met, step 1
ExecutionT1609 Container Administration CommandPath A, step 3
PersistenceT1098.004 Account Manipulation: SSH Authorized KeysPath B, steps 2 and 3
Defense EvasionT1578.002 Modify Cloud Compute Infrastructure: Create Cloud InstanceGCP Dedicated Servers
Defense EvasionT1578.001 Modify Cloud Compute Infrastructure: Create SnapshotGCP Dedicated Servers
Credential AccessT1552.001 Unsecured Credentials: Credentials In FilesPath A, step 1
Credential AccessT1552.007 Unsecured Credentials: Container APIPath A, step 2
Credential AccessT1552 Unsecured CredentialsPath B, step 1
Credential AccessT1555.006 Credentials from Password Stores: Cloud Secrets Management StoresPath A, step 5
Credential AccessT1606 Forge Web CredentialsAccount impersonation
Credential AccessT1110 Brute Force (hypothesis)SSH attempts before October 14
DiscoveryT1580 Cloud Infrastructure DiscoveryZeabur internal systems
Lateral MovementT1021.004 Remote Services: SSHPath B, step 1
CollectionT1530 Data from Cloud StoragePath A, step 4
CollectionT1213.006 Data from Information Repositories: DatabasesWhere the two paths met, step 2
ImpactT1496.004 Resource Hijacking: Cloud Service Hijacking (unsuccessful)Zeabur internal systems

Glossary

  • Shared Cluster: A Kubernetes cluster where services from many users run together. We announced its retirement in February 2026, and all affected services were deployed before that announcement.
  • Dedicated Server: A server Zeabur manages that runs services for a single user only.
  • Admin token: A credential that can manage an entire cluster. Whoever holds it can read the settings of every service and run commands in any container.
  • Secrets management service: The AWS service Zeabur uses to keep production secrets in one place.
  • Internal network: The private network (Tailscale) that connects Zeabur's production services. The platform database accepts connections only from this network.
  • Platform database: Zeabur's production database, which stores all accounts, projects, services, and environment variables.

Support and costs

If you need help, or notice unusual access or charges you can't explain, contact us through Zeabur Support. Before you contact us, revoke the related credentials and share only masked information. Don't post keys or passwords that are still valid in tickets or public channels.

Leaked credentials can be misused to run up charges on your accounts, and we are deeply sorry for any costs this causes you. Given its current finances, Zeabur cannot pay third-party service costs that result from this incident. Most cloud and API providers have a dispute or waiver process, and you can attach this report to explain what happened. If a provider asks for other evidence, tell us in a ticket and we will do our best to provide it. Account balance refunds are not affected by this incident; see Zeabur follow-up on the October 2026 security incident.

Zeabur has announced that it will shut down on December 31, 2026, which is an operational decision. Until then, we will keep handling support requests related to this incident.

We learned of this incident from a user report about 36 hours after the attacker started using the credentials. Protecting the credentials you stored on Zeabur was our responsibility, and we failed to do it. We apologize to every affected user.

The Zeabur team