Metadata
- Target: Aiven for PostgreSQL (Tier 2)
- Target Location: Aiven for PostgreSQL
- Target Category: Other
- VRT: Server Security Misconfiguration > Database Management System (DBMS) Misconfiguration > Excessively Privileged User / DBA
- Priority: P2 (Suggested)
Title
aiven_gatekeeper Fails to Prevent pghostile-Style Function Shadowing When Shadow Uses Implicitly Castable Argument Types
Summary
The aiven_gatekeeper shared library (Aiven’s PostgreSQL security agent) is designed to prevent privilege escalation attacks where unprivileged users create shadow functions in the public schema that override pg_catalog system functions. However, aiven_gatekeeper only blocks shadow functions with identical argument signatures. It fails to block shadow functions with different but implicitly castable argument types.
For example, pg_catalog.sha256(bytea) is protected, creating public.sha256(bytea) is blocked. But creating public.sha256(text) is allowed, and this shadow function is called INSTEAD of the pg_catalog version when a user calls sha256('string_literal'), because PostgreSQL’s function resolution prefers text→text (exact match) over text→bytea (implicit cast).
Aiven’s own tool pghostile (https://github.com/Aiven-Open/pghostile) identifies ~907 exploitable function overrides using this technique. The gatekeeper was designed to prevent exactly this class of attack but fails for cross-type shadows.
Steps to Reproduce
Step 1: Create shadow function with different argument type
CREATE OR REPLACE FUNCTION public.sha256(text) RETURNS bytea AS $$
BEGIN
RAISE WARNING 'SHADOW FIRED: cu=% su=%', current_user, session_user;
RETURN pg_catalog.sha256($1::bytea);
END;
$$ LANGUAGE plpgsql;
Step 2: Verify the shadow fires
SELECT sha256('test');
-- WARNING: SHADOW FIRED: cu=avnadmin su=avnadmin
The shadow function in public is called instead of pg_catalog.sha256(bytea) because sha256(text) is a better match when the argument is a text literal.
Step 3: Verify gatekeeper would block identical signature
-- This WOULD be blocked by gatekeeper (if attempted via pghostile):
-- CREATE FUNCTION public.sha256(bytea) ...
-- But our text-argument version bypasses the check entirely.
Impact
-
Defeats pghostile protection:
aiven_gatekeeperwas specifically built to prevent the attack class thatpghostileexploits. The implicit cast bypass renders this protection incomplete. -
Superuser code execution via autovacuum: When combined with expression indexes, the shadow function executes in the autovacuum worker context with
session_user=postgres(see related report: Autovacuum Code Execution). -
Broad attack surface: pghostile identifies ~907 exploitable functions. Many of these have variants with different-but-castable argument types that bypass gatekeeper.
Recommended Fix
Extend aiven_gatekeeper to block function creation in public (and other user-accessible schemas) when the function name matches ANY pg_catalog function, regardless of argument types. Alternatively, block functions whose names match pg_catalog functions when the argument types are implicitly castable to the pg_catalog version’s argument types.
Source · github.com/zionsworking/security-research-notebook · writeups/aiven/pg-gatekeeper-bypass-shadow-functions.md