Liquidfiles Privilege Escalation

Kurzinfo

Metadaten

  • Original (extern): Link oeffnen
  • Veroeffentlicht: 2026-06-20 12:31 UTC
  • Importiert: 2026-09-26 15:25 UTC
  • CVSS: 5.9
  • Quelle-ID: project_black_blog
  • uid_hash: 780dbe0c7368714406a624fca5e7d753520070fe97d6caf446d696f7a99a52a8

Inhalt

Liquidfiles has a feature called Liquidfiles Domains where a single instance can be used to host multiple virtual instances on separate URLs that are logically separated. Each Domain will have their own users, groups, configuration, certificate, branding and so on. LiquidFiles Domains | LiquidFiles Documentation What are LiquidFiles domains and when to use them? LiquidFiles Documentation

What we found was that it's possible to takeover the 'default domain' and become a sysadmin if you've been provided an admin account in any secondary domain. Admin -> Sysadmin Privilege Escalation 💡 Tracked as CVE-2026-12673.

In LiquidFiles, administrative privilege levels are represented by the following numeric values: 5 -> SysAdmin 4 -> Domain Admin 3 -> Admin

Within the default domain, the highest available privilege level is SysAdmin. In secondary domains, the highest available privilege level is Domain Admin. The following controller logic is intended to prevent administrators from creating or modifying groups with higher privileges than they are authorised to assign. app/controllers/admin/groups_controller.rb

def limit_create_for_admins return if Current.user.sysadmin? if admin_domain.default? return unless params[:group][:sysadmin].to_b || params[:group][:admin_level].to_i == 5 redirect_to new_admin_group_url, alert: 'You are not permitted to add a sysadmin group.' else return unless params[:group][:domain_admin].to_b || params[:group][:admin_level].to_i == 4 redirect_to new_admin_group_url, alert: 'You are not permitted to add a Domain Admin group.' end end

def limit_update_for_admins return if Current.user.sysadmin? if admin_domain.default? return unless params[:group][:sysadmin].to_b || params[:group][:admin_level].to_i == 5 redirect_to edit_admin_group_url(id: params[:id]), alert: 'You are not permitted to change group to a sysadmin group.' else return unless params[:group][:domain_admin].to_b || params[:group][:admin_level].to_i == 4 redirect_to edit_admin_group_url(id: params[:id]), alert: 'You are not permitted to change group to a Domain Admin group.' end end For secondary domains, this logic correctly prevents Admins from creating or modifying groups with the Domain Admin privilege level (admin_level = 4). However, it does not prevent the creation or modification of groups with the SysAdmin privilege level (admin_level = 5). There's also some leakage in the Default Tenant. As a result, an Admin in a secondary domain can create a new group or update an existing group to have SysAdmin privileges. To demonstrate this we can setup 2 Domains: Then create a domain admin and a secondary user that we will escalate to system admin. Login as the Domain Admin. As the domain admin create a Test Group normally and assign our target user to the group. Edit the Test Group and intercept the HTTP Request. Change the group[admin_level] to 5 in the request to POST /admin/groups/groupslug. PATCH requests will also work. Our original Domain Admin will now see this message. Our secondary user is now a sysadmin. The fix they applied looks like this: def limit_create_for_admins return if permitted_admin_level? redirect_to new_admin_group_url, alert: "You are not permitted to add a "
"#{Group.admin_level_name(requested_admin_level)} group." end

def limit_update_for_admins return if permitted_admin_level? redirect_to edit_admin_group_url(id: params[:id]), alert: "You are not permitted to change group to a "
"#{Group.admin_level_name(requested_admin_level)} group." end

Admins must not grant a group a higher privilege level than they hold themselves.

Without this a Domain Admin could escalate a group (and its users) to System Admin,

or an Admin could escalate to Domain Admin. Sysadmins are unrestricted.

def permitted_admin_level? return true if Current.user.sysadmin? requested_admin_level <= Current.user.group.admin_level end If you're building software, having it tested by an independent security team can surface these kinds of vulnerabilities before they're exploited in the wild. An Alternative (Broken) Privilege Escalation Digging a bit deeper, we also found an alternative way to escalate privileges using the GUI. If a custom group has users assigned to it, a Delete button is shown in the UI. If you click the delete button on the group there's a nice option to move those users before deleting the group. As an admin you could create a user, add them to a group, and then escalate them to a Domain Admin using this feature. Unfortunately, the code actually moves the user but then it looks like there's a bug and the deletion of the group deletes the moved users as well. This feature must've never been QA'ed! A very small part of me wanted to report the bug to get the bug fixed, then report it as a security issue after. permitted_parameters :create, move_to_group_id: Parameters.string def create user_count = @group.users.count

--- Loads into memory and caches the users @group.users.each do |user| Applog.debug 'Deleting group, User moved to new group', { user_id: user.slug, user_email: user.email, old_group_id: @group.slug, old_group_name: @group.name, new_group_id: @new_group.slug, new_group_name: @new_group.name }

--- User group updated user.update group_id: @new_group.id end Applog.info %{Group Deleted, #{pluralize(user_count, 'user')} moved to new group}, { old_group_id: @group.slug, old_group_name: @group.name, new_group_id: @new_group.slug, new_group_name: @new_group.name }

--- This deletes the group and cached users in that group @group.destroy redirect_to admin_groups_url, notice: %{#{pluralize(user_count, 'user')} moved to the #{@new_group.name} group. The group #{@group.name} has been deleted.}.mlstrip end

Verknuepfte CVEs

CVE-ID Severity (Quelle) CVSS (Quelle) Severity (CVE.org) CVSS (CVE.org) EPSS EPSS-Pctl Veroeffentlicht (CVE.org)

CVE-2026-12673

MEDIUM

5.9

MEDIUM

5.9

2026-06-20

Quellen-Details

Bezeichnung Name Kategorie Tags Zielgruppe Sprache Feed-URL
Project Black — Research Blog

project_black_blog

threat_intel blog, research - de https://projectblack.io/blog/rss/