Remote Code Execution in N2VC/LCM


Pedro Escaleira   April 2024

By Pedro Escaleira, IT Aveiro and OSM TSC Member, on behalf of OSM TSC.

The N2VC OSM's module executes shell code in the LCM container to interact with Helm and Kubectl. However, the executed code is obtained from some user-provided values, a portion of which is validated using an incomplete deny list, allowing an attacker to craft an exploit to execute arbitrary code.


The impact of this vulnerability is 8.5 in the Common Vulnerability Scoring System (CVSS), with the vector AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H. Moreover, the impacted OSM Releases are SEVEN to FOURTEEN, both included. The vulnerability was completely patched in version FIFTEEN, with one patch for the NBI and another for the N2VC modules, and in Release FOURTEEN, with a patch for the N2VC module. Moreover, the patch for the NBI was also cherry-picked to Release FOURTEEN, being corrected in the 14.0.2 point release, and the patches for both modules were cherry-picked for Release TWELVE, being available in the 12.0.8 point release.

Detailed Description

The N2VC's Helm base connector uses the Asyncio create_subprocess_exec method to execute commands throughout the remaining class methods. The first thing to consider is that the command executed contains parts provided by the user in multiple parts of the code. For instance, the K8sHelm3Connector's install method will call other methods to create a command to be executed, which contains the namespace, which the user can provide when instantiating a new NS. From this, there are parts of the command that the user input will control. However, one may argue that the Asyncio method being used is not signaled in the documentation as dangerous, unlike the method create_subprocess_shell, which is referred to as being prone to shell injection. And, to some extent, this may be right, in the sense that it is not possible to directly execute more than one process when calling this method, i.e., for example, if the command being executed is kubectl, we are not able to execute another command, for example, echo, in the same method call:


 However, although we can not execute two processes directly, if the process we launch by calling this method generates child processes, it will work. Consequently, although the vulnerability does not seem to have much reach at first sight, in reality, and with an attacker willing to cause damage to the platform, this should be treated with more attention. And the provided exploit in the appendix is just proof of that. The exploit was thought of the following way:

1) We know that we can not execute multiple commands, so this decreases our chances of injecting executable code;

2) However, looking at the code that N2VC is executing, we conclude that it involves the usage of the helm, helm3, and kubectl commands. Therefore, we only have to find a way of working with these commands to execute some specific code in some way. Thus, this exploit tries to exploit one of these command calls (from the K8sHelm3Connector class' _get_install_command method):

3) From analyzing the Helm documentation, we find a rather exciting flag related to the execution of helm3 install, helm3 upgrade, or helm3 template commands: the flag --post-renderer. As explained in the documentation, this flag will run an executable and pass to it, as an argument, the rendered Helm template related to the corresponding Chart being used. So, we can not, for example, pass something like an echo test to this flag because it will only execute one executable (and the flag only receives one argument, not two or more). However, if we pass echo, the echo script will receive all the chart templates as an argument. From this, we can conclude that if we pass /bin/bash or /bin/sh to this flag, then there will be an attempt to execute each line of the Chart template as a Bash command. Therefore, if an attacker controls the Chart, he may introduce a valid command in one of the Chart lines, which will be executed successfully. In this PoC exploit, since its purpose is only demonstrative, the Bash command executed is simply one to create a new directory, as shown below (obviously, an attacker would execute more complex commands, probably to open a remote shell or to execute a remote script, to take over the container, and to access the Kubernetes resources, by accessing the ambient variables with the Kube API credentials):

4) Finally, we need to find a way of passing this flag to N2VC. The way this PoC exploit does so is by including it within the VNFD, in the field vnfd.kdu.helm-chart, since this field is not verified correctly (NBI conducts some kind of deny listing verification, which is an unrecommended strategy from the security point of view, it should be used allow listing instead). An attacker could also pass the flag in the namespace while instantiating the new NS, but this was the strategy followed in this case (as you can see, there are multiple entrance points). Therefore, the command that is executed by N2VC, taking into account the command from the previous figure, is the following (where the flag --post-render is introduced within the original helm location, which is correspondent to the vnfd.kdu.helm-chart VNFD field):

5) Since we are using a complex string within the vnfd.kdu.helm-chart, we are not able to use a local Helm Chart. Therefore, the intruder must launch a Helm Repo containing the Chart in this case. For test purposes, we achieve that using the Python http.server. Nevertheless, an attacker would presumably launch a remote server to make the exploit Helm Chart available.

This PoC, although somewhat intricate, demonstrates that a willing attacker will always be able to achieve what he wants by chaining multiple attack parts into a general one if there is no attention in the system protection. Also, this is just one of multiple possible exploit possibilities. If we are not careful in how we treat this vulnerability, there might be a lot of possible exploits with different chains.

Also, this exploit only creates a directory in the LCM container to demonstrate that code execution is possible. If an attacker has access to the LCM container, he will be able to access all the namespace entities where OSM is instantiated, meaning that he will have, for instance, access to secrets and other services. Therefore, with some effort, he may be able to compromise not only all the OSM infrastructure but also other related infra, such as VIMs or K8s clusters that were added to OSM.


The patches for this CVE were made as part of the OSM#15 Hackfest event. The challenge, successfully concluded by Daniel Arndt, led to the following patches: