mirror of https://github.com/postgres/postgres
Many process environment variables (e.g. PATH), bypass the containment
expected of a trusted PL. Hence, trusted PLs must not offer features
that achieve setenv(). Otherwise, an attacker having USAGE privilege on
the language often can achieve arbitrary code execution, even if the
attacker lacks a database server operating system user.
To fix PL/Perl, replace trusted PL/Perl %ENV with a tied hash that just
replaces each modification attempt with a warning. Sites that reach
these warnings should evaluate the application-specific implications of
proceeding without the environment modification:
Can the application reasonably proceed without the modification?
If no, switch to plperlu or another approach.
If yes, the application should change the code to stop attempting
environment modifications. If that's too difficult, add "untie
%main::ENV" in any code executed before the warning. For example,
one might add it to the start of the affected function or even to
the plperl.on_plperl_init setting.
In passing, link to Perl's guidance about the Perl features behind the
security posture of PL/Perl.
Back-patch to v12 (all supported versions).
Andrew Dunstan and Noah Misch
Security: CVE-2024-10979
pull/184/head
parent
220cea9411
commit
b7e3a52a87
@ -0,0 +1,53 @@ |
||||
-- |
||||
-- Test the environment setting |
||||
-- |
||||
-- directory path and dlsuffix are passed to us in environment variables |
||||
\getenv libdir PG_LIBDIR |
||||
\getenv dlsuffix PG_DLSUFFIX |
||||
\set regresslib :libdir '/regress' :dlsuffix |
||||
CREATE FUNCTION get_environ() |
||||
RETURNS text[] |
||||
AS :'regresslib', 'get_environ' |
||||
LANGUAGE C STRICT; |
||||
-- fetch the process environment |
||||
CREATE FUNCTION process_env () RETURNS text[] |
||||
LANGUAGE plpgsql AS |
||||
$$ |
||||
|
||||
declare |
||||
res text[]; |
||||
tmp text[]; |
||||
f record; |
||||
begin |
||||
for f in select unnest(get_environ()) as t loop |
||||
tmp := regexp_split_to_array(f.t, '='); |
||||
if array_length(tmp, 1) = 2 then |
||||
res := res || tmp; |
||||
end if; |
||||
end loop; |
||||
return res; |
||||
end |
||||
|
||||
$$; |
||||
-- plperl should not be able to affect the process environment |
||||
DO |
||||
$$ |
||||
$ENV{TEST_PLPERL_ENV_FOO} = "shouldfail"; |
||||
untie %ENV; |
||||
$ENV{TEST_PLPERL_ENV_FOO} = "testval"; |
||||
my $penv = spi_exec_query("select unnest(process_env()) as pe"); |
||||
my %received; |
||||
for (my $f = 0; $f < $penv->{processed}; $f += 2) |
||||
{ |
||||
my $k = $penv->{rows}[$f]->{pe}; |
||||
my $v = $penv->{rows}[$f+1]->{pe}; |
||||
$received{$k} = $v; |
||||
} |
||||
unless (exists $received{TEST_PLPERL_ENV_FOO}) |
||||
{ |
||||
elog(NOTICE, "environ unaffected") |
||||
} |
||||
|
||||
$$ LANGUAGE plperl; |
||||
WARNING: attempted alteration of $ENV{TEST_PLPERL_ENV_FOO} at line 12. |
||||
NOTICE: environ unaffected |
||||
@ -0,0 +1,58 @@ |
||||
-- |
||||
-- Test the environment setting |
||||
-- |
||||
|
||||
-- directory path and dlsuffix are passed to us in environment variables |
||||
\getenv libdir PG_LIBDIR |
||||
\getenv dlsuffix PG_DLSUFFIX |
||||
|
||||
\set regresslib :libdir '/regress' :dlsuffix |
||||
|
||||
CREATE FUNCTION get_environ() |
||||
RETURNS text[] |
||||
AS :'regresslib', 'get_environ' |
||||
LANGUAGE C STRICT; |
||||
|
||||
-- fetch the process environment |
||||
|
||||
CREATE FUNCTION process_env () RETURNS text[] |
||||
LANGUAGE plpgsql AS |
||||
$$ |
||||
|
||||
declare |
||||
res text[]; |
||||
tmp text[]; |
||||
f record; |
||||
begin |
||||
for f in select unnest(get_environ()) as t loop |
||||
tmp := regexp_split_to_array(f.t, '='); |
||||
if array_length(tmp, 1) = 2 then |
||||
res := res || tmp; |
||||
end if; |
||||
end loop; |
||||
return res; |
||||
end |
||||
|
||||
$$; |
||||
|
||||
-- plperl should not be able to affect the process environment |
||||
|
||||
DO |
||||
$$ |
||||
$ENV{TEST_PLPERL_ENV_FOO} = "shouldfail"; |
||||
untie %ENV; |
||||
$ENV{TEST_PLPERL_ENV_FOO} = "testval"; |
||||
my $penv = spi_exec_query("select unnest(process_env()) as pe"); |
||||
my %received; |
||||
for (my $f = 0; $f < $penv->{processed}; $f += 2) |
||||
{ |
||||
my $k = $penv->{rows}[$f]->{pe}; |
||||
my $v = $penv->{rows}[$f+1]->{pe}; |
||||
$received{$k} = $v; |
||||
} |
||||
unless (exists $received{TEST_PLPERL_ENV_FOO}) |
||||
{ |
||||
elog(NOTICE, "environ unaffected") |
||||
} |
||||
|
||||
$$ LANGUAGE plperl; |
||||
Loading…
Reference in new issue