Chmura
Attacking The Supply Chain: Developer
In this proof of concept, we look into one of several attack vectors that can be abused to attack the supply chain: targeting the developer. With a focus on the local integrated developer environment (IDE), this proof considers the execution of malicious build scripts via injecting commands when the project or build is incorrectly “trusted”.
In 2021, we published an entry identifying the weak parts of the supply chain security. In the face of the surge in documented attacks, the entry gave a summarized overview of how malicious actors found gaps to abuse and take advantage of for possible gains and disruptions.
In this entry, we focus on one specific part of the supply chain: the developers themselves. To find a suitable attack model focusing on the developer, we must first understand who is considered the developer (and therefore the target), their workflow, and their daily tools. We also set the focus on how developers and their respective tools can be abused to compromise the supply chain, and how understanding these threat scenarios allows developers and the organizations to decide which tradeoffs to make to protect their projects and themselves.
Who is “The Developer”?
We can use a dictionary definition, stating a developer is a person that develops computer software. In our understanding, a person who writes code. This includes popular programming or scripting languages like Java, JavaScript, TypeScript, Go, Python, C/C++, and many other languages, including infrastructure or container deployment definitions such as Dockerfiles, Kubernetes, Terraform HCLs, and many others. From that description alone, the definition covers various parts of the IT industry, including every person writing code and security researchers, among many others.
Although the workflow itself may vary from developer to developer and from company to company, it will most likely fall into one of the following categories depending on how the developer is using the integrated developer environments (IDE):
- Local IDE: The developer has the IDE installed in his own machine locally. In this case, the developer can
- Pull or push code to the remote repository, and execute the build and debug it locally, or
- Commits changes to the remote repository, triggering the continuous integration/continuous delivery (CI/CD) event, and lead to the quality assurance (QA) assessment or even a deployment into the production environment.
- Cloud IDE: The developer uses cloud services-hosted IDE, such as AWS Cloud9, Visual Studio Online, GitHub Codespaces, and many other platforms available today. In this case, the developer machine works just as a gateway, usually via browser to the IDE, and the main code executions are performed in the cloud IDE’s remote hosts inside the cloud service provider.
As the developer definition covers multiple professions, some workflows could exclude some items from the list. For example, research purpose proof of concept would more likely not set up a whole CI/CD pipeline. However, most workflows will include usage of an IDE for the development. In this entry, we focus on local IDEs, as we also discussed specific platforms in our previous entries on the security risks of online coding platforms.
A Use Case of a Local IDE
When using the local IDE, one of the use cases is when the developer pulls the code to their local computer. This code is further compiled into binary format for it to be executed. There is an implicit trust in the code written by previous contributors because most developers assume that the codebase is likely not “dirty” since it works as intended. This trust is not only carried to and in the source code itself, but also in the build scripts, libraries, dependencies, and other project files when included. That brings us to the first threat scenario: injecting malicious actions into the project files or build scripts.
As developers, do we read the build scripts after pulling remote code prior to their execution?
We tested various popular IDEs and programming languages by injecting malicious build commands to the build scripts or project files if and when applicable. These are the results of version of IDEs we tested:
- Eclipse 2022-09
- Apache NetBeans 16
- PyCharm 2022.2.4
- IntelliJ IDEA 2022.03
- Visual Studio 2022
- Visual Studio Code 1.73.1
When we consider the generic threat model, we also must include every non-controlled input. This includes the source code, its files, and including its pre- and post-build scripts and IDE extensions, if applicable. We previously wrote about the danger of possible malicious IDE extensions in one of our 2020 articles.
We defined the following scenarios for each of the IDEs to validate our threat model:
- Developer pulls a code from a non-trusted repository online
- Developer opens a project inside the IDE
- Developer tries to compile or build the project
Using Visual Studio Code
Starting with Visual Studio Code version 1.57 (released May 2021), the code editor introduced the concept of Workspace Trust. This feature helps developers safely browse and edit code regardless of the source or authors by preventing code execution from untrusted files and repositories. This is probably due to the increasing number of third party extension vulnerabilities at the time, which — when abused — could allow remote code execution (RCE) when opening an untrusted file. The concept is straightforward: it does not allow any (build/debug) task or some extension features to operate unless the workspace is trusted. This shifts the responsibility to the developer and prompts them to trust or not trust the downloaded code.
The thing to emphasize here is to not blindly trust every workspace.
What should the developer look for and consider as suspicious signs that the code should not be trusted? Among other instances, signs that should raise red flags among developers are:
- lower number of downloads
- projects shared on forums
- gray areas
- generally unverified sources, and
- unknown people
Prior to executing the IDE tasks, one should verify the .vscode/tasks.json file, especially when downloaded from an unknown source, by auditing the file within the project directory for suspicious or malicious commands.
A malicious command can be hidden under the tasks.json file and masquerade as a build command. When a developer tries to build the previously blindly trusted project above, the developer machine will execute the remote code, which can be malicious. The attacker could also make the payload stealthier by hiding the malicious command between regular build commands. This will raise less suspicion with the developer.
In our simulation, we put a script on a remote server via Pastebin. This is a method abused by some threat actors to deliver their malicious payloads into infected machines. The benefit of this technique for cybercriminals is that the payload can be changed remotely. For instance, the payload can be changed for something harmless after a successful infection.
Using Visual Studio
Visual Studio, the proprietary IDE from Microsoft for .NET and C++ developments, does not have the Workspace Trust feature. As a result, the developer should be extra careful when loading non-trusted project files and executing builds. A malicious pre- or post-build task could be injected into the file, causing unwanted execution from the start of the build.
Using Other IDEs
In the case of Eclipse IDE, the injection of build commands is still possible. Hence, the files differ. At first, a build command of ExternalToolBuilder has to be specified inside the .project file, referencing another file present inside the .externalToolBuilders folder where the actual execution command is defined. By chaining multiple build commands together, we can achieve the same multiple command execution as in the case of Visual Studio Code.
As the project file injection with external build tool is applicable within the scope of the base IDE, it is applicable only for languages where actual code is compiled into a binary file. This includes Java and C/C++, but excludes languages like PHP as no build is performed.
NetBeans IDE is mainly used for Java development, although it also supports PHP, HTML5, JavaScript, or C/C++ developments via a third party extension. Java development projects can leverage Maven, Gradle, or Ant as their dependency management and build automation tool. As such, the project and build definitions may be different. However, all these tools support the execution of third party processes as pre- or post-build actions. In this scenario, an attacker could inject malicious code and hope that the developer will not notice and execute unwillingly.
In the case of Ant, the injection can be done inside the nbproject/build-impl.xml file by adding the following code snippet into one of the suitable targets tags:
<exec executable=”{command to execute}”>
<arg value={argument}/>
</exec>
When the developer uses Maven as the build tool, the same objective could be achieved by altering the pom.xml inside the project folder. This time, the org.codehaus.mojo plugin is used inside the build tag. The syntax used is like the one used with Ant.
In the case of Gradle, a Groovy language script is used for the build definition located inside the app/build.gradle file, and calling the execute() function on a string inside the task of choice will trigger a code execution.
Even though the Open Project dialog has an option for a “Trust Project Build Script”, its functionality is only valid for Gradle projects. When unchecked, it prevents Gradle script priming and, thus, code execution is possible while loading the project as a fix of CVE-2020-11986. Nonetheless, when the user decides to execute the build manually, no further dialogs are shown and the build is considered trusted.
IntelliJ IDEA is another IDE used for Java, Kotlin, Groovy, and other Java Virtual Machine-(JVM) based languages’ development. It also supports Ant building scripts. Loading a project that contains an Ant build script triggers a dialog warning that it might execute potentially malicious code, and advises to use safe mode if it is not coming from a trusted source. When a developer tries to perform a build in safe mode, the IDE warns the user that the action can be done only in trusted mode.
PyCharm is an IDE used for Python development. Python scripts are not usually compiled prior to its execution. However, a developer could still specify a custom Run/Debug configuration, allowing to execute a third party binary before the actual script execution. This might be used for script data input preparation.
The action is referenced inside the project. However, the actual executable specification is stored on a different place, more specifically at ~/.config/JetBrains/PyCharmXXXX/tools/External Tools.xml. As we can see, the file is stored inside the user home directory, protecting it against our threat model scenario as it would require local file system modifications.
Conclusion
We evaluated all the identified IDEs with a threat scenario of executing malicious build scripts. We proved that it is possible to inject malicious commands into these build scripts. As described above, some IDEs explicitly warn the developer on the possibility of malicious actions, and unless the project configuration marks them as explicitly trusted it won’t be allowed to perform the task. On the other hand, some IDEs use the assumption that when the developer opens a project or copies it into his workspace, it is automatically trusted and will not require any further action.
No matter what IDE we use, there will be always a tradeoff between security and usability. Developers should not blindly trust every open-source project found on the internet. Prior to any execution of build actions, developers should at least know about the possibility that they can be targeted and review the build scripts.
We would also like to emphasize that the described threat scenario is not limited to local IDEs only, but the fact that security importance lies within the used workflow and workspace trust itself — regardless of the situation/s if the developer executed the actual build/compilation inside a container or VM powering online IDE. Once the workspace is marked trusted and the build script is modified, it could trigger unwanted code execution within the environment and access rights available to the IDE. To mitigate the risk of supply chain compromise using the described threat scenario, here are some best practices developers can keep in mind:
- Use a securely configured CI/CD platform, and perform the build on an external device or service with proper role-based access control (RBAC) where only authorized people can change the build script .
- Check external source codes and build script(s) prior to integration to the project.
- Avoid blindly using out-of-the-box solutions prior to auditing, especially when they are not widely used within the community or coming from unverified sources.
- Regularly track changes and conduct reviews.