Skip to content

Cross-Team IDOR: Livewire Components Accept Unscoped server_id and destination_uuid — Deploy to Other Teams' Servers

Critical
andrasbacsai published GHSA-725v-f5gh-22q9 Jun 28, 2026

Package

composer coollabsio/coolify (Composer)

Affected versions

< v4.0.0-beta.474

Patched versions

v4.0.0-beta.474

Description

Summary

Coolify's API controllers consistently validate server ownership with Server::whereTeamId($teamId) before any operation. However, multiple Livewire web UI components accept server_id and destination_uuid from URL query parameters without any team ownership validation, allowing cross-team resource deployment.

Finding 1: Cross-Team Resource Deployment via Unscoped Destination/Server Lookup

CVSS: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H (8.5)

Safe wrapper — Used consistently in API controllers:

$server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first();

Also correctly scoped in ResourceOperations.php (clone operation):

$new_destination = StandaloneDocker::whereHas('server', fn($q) => $q->where('team_id', currentTeam()->id))->find($destination_id);

Missing team checkapp/Livewire/Project/Resource/Create.php lines 82-89:

$destination = StandaloneDocker::whereUuid($destination_uuid)->first(); // NO team check
$service_payload = [
    'server_id' => (int) $server_id, // from query param — NO team check
    'destination_id' => $destination->id,
];
$service = Service::create($service_payload);

Same pattern in 6+ more Livewire components (DockerCompose.php, DockerImage.php, GithubPrivateRepository.php, etc.) and all create_standalone_* helper functions.

The project_uuid and environment_uuid ARE validated against currentTeam() (line 24), but server_id and destination_uuid are NOT.

PoC:

GET /project/{attacker_project}/{attacker_env}/new?type=one-click-service-wordpress&server_id=VICTIM_SERVER_ID&destination=VICTIM_DESTINATION_UUID

Finding 2: Unscoped Server Lookup in Boarding Flow

CVSS: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:L (6.5)

Same file, line 121 correctly uses Server::ownedByCurrentTeam(). But line 124:

$this->createdServer = Server::find($this->selectedExistingServer);
// selectedExistingServer is integer from #[Url] — directly from URL

Then validateServer() calls instant_remote_process(['ls /'], $this->createdServer, true) — SSH command on unscoped server. Server IDs are auto-increment integers.

Impact

  • Cross-team container deployment to another team's server
  • Remote command execution on unscoped server via boarding flow
  • API is correctly protected — Livewire web UI only

Remediation

// Replace all occurrences of:
StandaloneDocker::whereUuid($destination_uuid)->first();
// With:
StandaloneDocker::whereHas('server', fn($q) => $q->where('team_id', currentTeam()->id))->where('uuid', $destination_uuid)->first();

// Boarding flow:
Server::ownedByCurrentTeam()->find($this->selectedExistingServer);

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N

CVE ID

CVE-2026-57498

Weaknesses

Authorization Bypass Through User-Controlled Key

The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data. Learn more on MITRE.

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.

Credits