diff --git a/expected/pgaudit.out b/expected/pgaudit.out index d121873..6e80053 100644 --- a/expected/pgaudit.out +++ b/expected/pgaudit.out @@ -1152,6 +1152,34 @@ NOTICE: AUDIT: SESSION,68,1,READ,SELECT,TABLE,public.tmp,CREATE TABLE tmp2 AS ( NOTICE: AUDIT: SESSION,68,1,WRITE,INSERT,TABLE,public.tmp2,CREATE TABLE tmp2 AS (SELECT * FROM tmp);, DROP TABLE tmp; DROP TABLE tmp2; +-- +-- Test that pgaudit event triggers are immune to search-path-based attacks +-- Attempt to capture unqualified references to standard functions +CREATE FUNCTION upper(text) RETURNS text +LANGUAGE SQL AS 'SELECT (1/0)::text'; +CREATE FUNCTION lower(text) RETURNS text +LANGUAGE SQL AS 'SELECT (1/0)::text'; +CREATE FUNCTION my_ne(text, text) RETURNS bool +LANGUAGE SQL AS 'SELECT (1/0)::bool'; +CREATE OPERATOR <> (FUNCTION = my_ne, LEFTARG = text, RIGHTARG = text); +WARNING: operator attribute "function" not recognized +ERROR: operator procedure must be specified +CREATE EXTENSION IF NOT EXISTS pgaudit; +SET pgaudit.log = 'DDL'; +-- Put public schema before pg_catalog to capture unqualified references +SET search_path = public, pg_catalog; +-- If there was a vulnerability, these would fail with division by zero error +CREATE TABLE wombat (); +NOTICE: AUDIT: SESSION,69,1,DDL,CREATE TABLE,TABLE,public.wombat,CREATE TABLE wombat ();, +DROP TABLE wombat; +NOTICE: AUDIT: SESSION,70,1,DDL,DROP TABLE,TABLE,public.wombat,DROP TABLE wombat;, +SET pgaudit.log = 'NONE'; +DROP EXTENSION pgaudit; +DROP OPERATOR <> (text, text); +ERROR: cannot drop operator <>(text,text) because it is required by the database system +DROP FUNCTION my_ne(text, text); +DROP FUNCTION lower(text); +DROP FUNCTION upper(text); -- Cleanup -- Set client_min_messages up to warning to avoid noise SET client_min_messages = 'warning'; diff --git a/pgaudit.c b/pgaudit.c index cbe3184..f09b33f 100644 --- a/pgaudit.c +++ b/pgaudit.c @@ -1521,7 +1521,9 @@ pgaudit_ddl_command_end(PG_FUNCTION_ARGS) CreateCommandTag(eventData->parsetree); /* Return objects affected by the (non drop) DDL statement */ - query = "SELECT UPPER(object_type), object_identity, UPPER(command_tag)\n" + query = "SELECT pg_catalog.upper(object_type),\n" + " object_identity,\n" + " pg_catalog.upper(command_tag)\n" " FROM pg_catalog.pg_event_trigger_ddl_commands()"; /* Attempt to connect */ @@ -1622,11 +1624,11 @@ pgaudit_sql_drop(PG_FUNCTION_ARGS) contextOld = MemoryContextSwitchTo(contextQuery); /* Return objects affected by the drop statement */ - query = "SELECT UPPER(object_type),\n" - " object_identity\n" - " FROM pg_catalog.pg_event_trigger_dropped_objects()\n" - " WHERE lower(object_type) <> 'type'\n" - " AND schema_name <> 'pg_toast'"; + query = "SELECT pg_catalog.upper(object_type),\n" + " object_identity\n" + " FROM pg_catalog.pg_event_trigger_dropped_objects()\n" + " WHERE pg_catalog.lower(object_type) operator(pg_catalog.<>) 'type'\n" + " AND schema_name operator(pg_catalog.<>) 'pg_toast'"; /* Attempt to connect */ result = SPI_connect(); diff --git a/sql/pgaudit.sql b/sql/pgaudit.sql index a4d53db..480b038 100644 --- a/sql/pgaudit.sql +++ b/sql/pgaudit.sql @@ -788,6 +788,38 @@ CREATE TABLE tmp2 AS (SELECT * FROM tmp); DROP TABLE tmp; DROP TABLE tmp2; +-- +-- Test that pgaudit event triggers are immune to search-path-based attacks + +-- Attempt to capture unqualified references to standard functions +CREATE FUNCTION upper(text) RETURNS text +LANGUAGE SQL AS 'SELECT (1/0)::text'; + +CREATE FUNCTION lower(text) RETURNS text +LANGUAGE SQL AS 'SELECT (1/0)::text'; + +CREATE FUNCTION my_ne(text, text) RETURNS bool +LANGUAGE SQL AS 'SELECT (1/0)::bool'; + +CREATE OPERATOR <> (FUNCTION = my_ne, LEFTARG = text, RIGHTARG = text); + +CREATE EXTENSION IF NOT EXISTS pgaudit; +SET pgaudit.log = 'DDL'; +-- Put public schema before pg_catalog to capture unqualified references +SET search_path = public, pg_catalog; + +-- If there was a vulnerability, these would fail with division by zero error +CREATE TABLE wombat (); +DROP TABLE wombat; + +SET pgaudit.log = 'NONE'; +DROP EXTENSION pgaudit; + +DROP OPERATOR <> (text, text); +DROP FUNCTION my_ne(text, text); +DROP FUNCTION lower(text); +DROP FUNCTION upper(text); + -- Cleanup -- Set client_min_messages up to warning to avoid noise SET client_min_messages = 'warning';