一、前 言
二、关键点分析
三、拓展分析
四、总 结
CVE-2024-12356
命令注入漏洞影响BeyondTrust
的Privileged Remote Access
和Remote Support
系列产品,并实际上依赖于PostgreSQL
的CVE-2025-1094
漏洞。本文从BeyondTrust
的CVE-2024-12356
为场景入口,逐步分析到PostgreSQL
的CVE-2025-1094
,解释引起命令注入的核心编码问题。
CVE-2024-12356
命令注入漏洞通过WebSocket
访问BeyondTrust
认证前路由/nw
,将HTTP中的Sec-WebSocket-Protocol
子协议头设定为ingredi support desk customer thin
(以及设定一些其它类似Host的必需参数),即可访问到thin-scc-wrapper
脚本。
thin-scc-wrapper分析(CVE-2024-12356)
thin-scc-wrapper文件补丁前后主要变化:
-
read-t 30 gskey从标准输入(WebSocket数据流)中读取数据,并存储到变量gskey中,该$gskey变量数据用户可控;
-
quoted=$(export PHPRC="$BG_app_root/config/php-cli.ini"; echo "$gskey" | $ingrediRoot/app/dbquote) 将$gskey变量数据数据传递给dbquote脚本处理处理(目的是转义不安全字符),并将其处理结果赋值给quoted;
-
$(echo "SELECT COUNT(1) FROM gw_sessions WHERE session_key = $quoted AND session_type = 'sdcust' AND (expiration IS NULL OR expiration>NOW())" | $db)将$quoted拼接到字符串中,并通过管道传递给$db执行(即通过psql执行拼接$quoted后的SQL语句)。
补丁中将echo $gskey变为了echo "$gskey",多出了一个双引号。它们之间的区别,可通过下面测试进行体现:
在sh环境中,可以看到echo $test和echo "$test"在结果上没有区别,$test作为一个完整的字符串被打印。在bash环境中,可以看到echo $test产生了变化,变量$test字符串的开头一部分-e会被视为echo命令的一个参数(使echo解释xNN形式的数据),后续部分会被echo命令解释输出,其中的x31x32x33x34会被解释为1234;相比而言,在bash环境有双引号时,echo "$test"会原样输出$test,不会做任何解释。因此,修复前echo $gskey | $ingrediRoot/app/dbquote这种写法,意味着可以将$gskey设定为-e xNNxNN...形式的数据,从而向dbquote传入任何攻击者指定的字节数据。
dbquote分析(CVE-2025-1094)
前文中 $gskey 可控值被传输到名为dbquote的脚本中,该脚本内容:
传入的$gskey先通过fgets读取,之后会交给PHP的pg_escape_string进行转义,转义后的结果会放在两个单引号之间并打印到标准输出(最后会存储在thin-scc-wrapper中名为quoted的新变量中)。
上述操作的目的是利用pg_escape_string函数转义特殊字符(如单引号),并使其在后续拼接的 SQL 语句中安全使用。如果pg_escape_string转义没有问题,那么理论上是不会产生SQL注入的,但事实上产生了CVE-2025-1094问题。
pg_escape_string的底层实现:
可以看到pg_escape_string会进一步调用PostgreSQL的PQescapeStringConn/PQescapeString,两者都会调用PQescapeStringInternal。
PQescapeStringInternal底层实现:
为了解释上述代码,需要先解释一下什么是多字节字符。类似UTF8这样的可变字符,可以是1个字节是1个字符,也可以是2个字节或更多字节构成1个字符。本文中单个字节构成1个字符称为单字节字符,2个或以上的字节构成1个字符,称为多字节字符。上述代码是读取source中的字符进行处理,将其中的特殊字符前添加转义字符,拷贝到target中。在处理逻辑中有如下关键点:
-
从source读取一个字节,如果字节最高Bit为0,视为单字节字符;否则视为多字节字符;
-
如果是单字节字符,判断单字节字符是否为单引号'或右斜线,是的话双倍该单字节字符拷贝到target中(双倍的含义就是转义),不是的话直接将单字节字符拷贝到target中,不进行转义;
-
如果是多字节字符,先通过pg_encoding_mblen的分析多字节字符的长度len(该长度即几个字节构成一个多字节字符),接着将source中len个字节直接拷贝target中。
从上面的关键点来看,多字节字符拷贝的时候没有添加任何其它字符,即不进行任何的转义。那就抛出来一个问题,能不能特殊构造一个多字节字符,该字符存在某个字节为单引号'(这样就有可能在后续引发单引号'闭合触发SQL注入)?
对于双字节字符来说,一个可能的情况是pg_encoding_mblen返回len为2,且对应从source中读取的第2个字节为单引号'。由于source任意字节可控(来自dbquote脚本中pg_escape_string的传入参数),所以前面条件暂可简化为要求pg_encoding_mblen返len为2。
所以现在来看一下pg_encoding_mblen的底层实现:
pg_encoding_mblen方法会从pg_wchar_tbl表中找到某字符编码对应的函数集合,并从中找到存储的mblen方法。 对与PG_UTF8字符编码来说,相当于要调用上述的pg_utf_mblen方法。在该方法中,如果某字符的第1个字节&0xe0的结果为0xc0,那么就认为该字符占用两个字节。所以,可以构造 0xC0, 0x27 的字节序列,该序列视为双字节字符,其0xC0&0xE0等于0xC0,而且0x27为单引号。这种方式构造的多字节字符会直接从source被拷贝到target,且不会进行任何的转义。
有了上述分析后,直接进行如下三个测试进行验证。
无单引号测试:
结果可以看到未对hey进行任何转义,且首尾额外增加一个单引号。
有单引号测试:
结果可以看到h'ey'中所有单引号前被额外增加了一个单引号进行转义,且首尾额外增加一个单引号。
特殊构造测试:
结果可以看到hxC0'ey'中xC0后的单引号未被转义,ey后的单引号被转义,且首尾额外增加一个单引号。该方式的结果初步看像是可以绕过单引号闭合,可能可以被后续用于SQL注入,也就是接下来的psql分析。
psql分析(CVE-2025-1094)
前文thin-scc-wrapper中$(echo "SELECT COUNT(1) FROM gw_sessions WHERE session_key = $quoted AND session_type = 'sdcust' AND (expiration IS NULL OR expiration>NOW())" | $db这部分sql语句最终交由psql执行,其中$quoted可以通过特殊构造测试中的类似方法实现SQL注入。
psql作为一个PostgreSQL的客户端,其本身支持一些meta-commands and various shell-like features,如下是命令执行的官方文档说明:
通过如下方式模拟实现SQL注入以及命令执行:
前文中pg_escape_string
转义配合这里的psql
,构成了实际上的CVE-2025-1094
漏洞。下面是Postgres官方的说明:
可以看出来,CVE-2025-1094不仅包括PQescapeString,还涉及PQescapeLiteral,PQescapeIdentifier和PQescapeStringConn。
触发流程
CVE-2024-12356
通过WebSocket
访问BeyondTrust
认证前路由/nw
,将HTTP中的Sec-WebSocket-Protocol
子协议头设定为ingredi support desk customer thin
(以及设定一些其它类似Host的必需参数),即可访问到thin-scc-wrapper
脚本。
thin-scc-wrapper
脚本中$gskey
变量数据来自WebSocket
数据流,用户可控。可通过特殊构造的Invalid UTF-8 0xC0, 0x27
绕过dbquote
脚本中的pg_escape_string
转义即CVE-2025-1094
,并拼接到SQL语句中实现SQL注入【注入psql
元命令! [ command ]
语句】。
包含SQL
注入的字符串,被传递到psql
解释,触发元命令! [ command ]
执行。
既然Postgres存在这个问题,那么其它的开源数据库是否有类似的问题呢?
Mysql分析
使用Mysql时,类似的转义方法有mysqli_real_escape_string(mysqli $mysql, string $string): string。mysqli_real_escape_string会调用mysql_real_escape_string_quote,而mysql_real_escape_string_quote等同于mysql_real_escape_string。
在mysqlnd_libmysql_compat.h中mysql_real_escape_string又等同于mysqlnd_real_escape_string。
在mysqlnd.h中mysqlnd_real_escape_string会调用连接对象的escape_string方法,该方法又调用mysqlnd_cset_escape_quotes或mysqlnd_cset_escape_slashes。
上面的escape_string调用mysqlnd_cset_escape_quotes,这部分是多字节字符处理的核心逻辑:
可以看到其逻辑类似PostgreSQL,如果cset->mb_valid(escapestr, end)调用只检查长度,不检查多字节字符是否Valid,那么应该也有类似Postgres的问题。但进一步查看源码(以UTF8为例),可以发现mb_valid对字节是否合法做了检查。
可以看出来utf8字符集的mb_valid操作,会对多字节字符进行检查,要求其必须是Valid utf8 character。但前述代码都是在php-src分析,事实上,在mysql-src源码中,也存在mysql_real_escape_string ,这里简单看一下,逻辑和php-src中很类似,也会对多字节字符进行检查,要求其必须是Valid utf8 character。
除去一些无关代码后,上面my_ismbchar对于对于utf8来说,相当于调用my_mb_wc_utf8_prototype进行检测。
上述过程在获取单个多字节字符的长度时,同时进行了“是否为一个有效UTF8字符”的检查。
因此,从前述源码来看,Mysql在UTF8字符集方面应该不存在类似Postgres的问题。
Postgres修复
在最新的Postgres源码fe-exec.c中,对于PQescapeStringInternal方法,通过增加pg_encoding_verifymbchar函数调用来进行多字节字符的有效性检查。
对于UTF8来说, pg_encoding_verifymbcha的检测会调用到pg_utf8_islegal,可以看到该函数会检测是否为一个有效UTF8字符。
新的问题
CVE-2025-1094修复后,postgres对于UTF8的处理看着没什么问题,那么对于gbk的处理是否有问题?
事实上,postgres在服务端不支持gbk编码,但是客户端是支持gbk编码,在源码中有所体现。
检查之前的pg_wchar_table表,查看gbk相关的长度处理pg_gbk_mblen和字符检查pg_gbk_verifychar。
简单从源码来看,似乎gbk的检查很宽松,是不是有可能存在之前的问题?直接盲测一下,让postgres-server使用UTF8编码,而客户端采用gbk编码。
服务端信息(修复后版本):
创建dbq脚本(内部用户名/密码等信息用于连接postgre-server)。
该脚本与前文dbquote逻辑基本一样,主要区别是这里增加了client_encoding='GBK'客户端编码。
执行quoted=$(echo -e "heyxC0'; ! id # " | ./dbq); echo "select $quoted" | sudo -u postgres psql -e,其输出类似:
从结果来看,出发了命令执行,也就是说xc0x27序列在client_encoding='GBK' server_encoding=UTF8下依旧可用。此外简单尝试了下,BIG5,UHC做为客户端编码,现象也是如此。
这部分内容和PostgreSQL安全团队反馈后,对方回复认为这里的问题算是一种使用上的错误(a problem in how the escape functions are used, not a bug in how the escape functions work),而不是Bug(因为UTF8等编码本身的检测是没有问题的)。所以,这个现象目前在最新版上还是存在的。
本文分析了因编码问题引起的CVE-2024-12356
和CVE-2025-1094
,解释了产生BeyondTrust
命令注入的核心编码问题。基于该编码问题的思想,笔者对比分析了Mysql
的代码以及补丁修复后的PostgreSQL
代码,并进行了一定的拓展分析。Mysql
暂未发现问题,但PostgreSQL
在特定“使用错误”场景下,依旧会存在类似CVE-2025-1094
的问题。
奇安信天工实验室,专注于漏洞攻防领域技术研究,面向互联网基础设施,以操作系统平台、基础软件应用、网络通信协议、关键网络设备为目标,研究漏洞挖掘、利用、检测等关键技术。漏洞研究成果连续在GeekPwn、天府杯等漏洞破解赛事中斩获奖项,漏洞挖掘方法发表于ACM CCS、Usenix、EuroS&P、BlackHat、HITB等国际重量级会议。
原文始发于微信公众号(奇安信天工实验室):编码问题引起的RCE分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论