In many environments, macOS endpoint users are synonymous with software and systems engineers. One application commonly used among this user group is Microsoft’s Visual Studio Code, aka VSCode.
With a rich marketplace of extensions that improve functionality for various programming languages, as well as interaction with cloud service providers and remote hosts, it’s a popular choice among a broad range of engineering roles (and beyond).
From an offensive testing perspective, macOS VSCode has already been explored by others as a means of persistence and code execution. @domchell’s blog highlights how a malicious VSCode extension could be used as a loader for JXA script content, while @svcghost takes this further with a fully-fledged cross-platform Mythic C2 Agent, Venus.
Looking at this from another angle however - this short blog explores the situational awareness that can be gleaned from VSCode, should an operator have obtained an initial foothold; while also considering what sensitive data might be exposed, which would otherwise be protected by Apple’s oft-maligned ‘Transparency, Consent and Control’ framework, aka TCC. We’ll also see these reconnaissance activities operationalised in the Mythic Medusa agent.
We can view a user’s recently accessed files in a couple of places:
- In VSCode’s global sqlite
- In a JSON file which (among other things) dictates what appears in VSCode’s menu bars at
When a user makes changes to files in VSCode, until they’re saved, they’re backed up in temporary files within
- Files that may otherwise be protected by TCC, are accessible at this location (in their edited form) until a user saves them.
- New files that have yet to be saved are also stored in this location.
The Mythic Medusa agent has three new functions to target the above behaviours:
vscode_list_recent- List recent files stored in the
vscode_open_edits- List all edited and unsaved files.
vscode_watch_edits- Poll the Backups directory for file changes.
Having identified the usage of VSCode on our compromised endpoint, gaining an understanding of files and folders recently accessed by a user would be a good next step.
Under the hood, the
Finder_Preferences() function reads the below plist file, showing us several pertinent entries:
Here’s an abridged example of its output:
For VSCode specifically, we can identify recent files and folders accessed by our user from one of two locations. The primary one being VSCode’s
This file is a sqlite database that holds information pertaining to how VSCode is configured, including theme settings, installed extensions, and most importantly for us, recently opened files and folders.
Recently accessed files are listed under the key,
history.recentlyOpenedPathsList, which we can identify with the following SQL query:
The below Python code can be used to execute the above query and pull out the data we’re looking for:
This gives us the below output, notably highlighting instances where VSCode’s Remote SSH extension has been used to edit files on a remote host (useful for subsequent exploration!):
The Mythic agent, Medusa, has a new built-in function to perform the above. Below we can see usage of the
vscode_list_recent function. This targets the current user’s
state database by default, but an alternative sqlite file path can be provided.
state file offers all we need in this instance, an alternative location we could assess is below:
Amongst other things, this JSON contains the information used to populate the menu bars, and most importantly for us the
Open Recent option. A snippet of the JSON content is shown below:
Just as we did before, the below Python code can be used to quickly pull out the recent files and folders:
Which gives us the below output:
Reviewing the recent files and folders listed in the VSCode files above, we could identify high-value items unprotected by TCC and pursuant to our objective, and start sifting through those. But there’s another option available to us here.
For anyone that’s ever used VSCode, you’ll know that when you make changes to files and exit VSCode, reopening the application will present your modified, but unsaved, files. These edits must of course live somewhere on disk and this is where we have an opportunity to access files that may otherwise be protected by TCC.
To articulate how this can play out, we’ll walk through a scenario. We have a file we need to access in our target user’s
Documents folder, at the following location:
We can confirm we don’t have the privileges to read the file in our scenario by simply attempting to do so with the Terminal. Here we confirm it is protected by TCC:
Using the awesome Endpoint Security Framework event viewer, Crescendo, we can monitor file events when we open and edit our
secrets.txt file in VSCode.
Here we note that upon making changes to our
secrets.txt file, a new file is created in the following top-level folder:
Returning to our Terminal, let’s attempt to read the contents of that file:
Here we can view the unsaved changes our target user has made to files that would otherwise be protected by TCC.
By recursing through this
Backups directory, we can see which files are being edited, as well as their modified times.
The following Python code could be used to achieve this:
Which gives us the following output:
Notably here, newly created files that haven’t been saved yet will also be backed up to this directory.
Once again returning to Mythic’s Medusa agent, we have a second new function. This time for triaging unsaved edits -
vscode_open_edits. Below we can see an agent being tasked to retrieve unsaved edits, before we
cat a backup file to see its contents.
Of course, as a user makes changes to files in VSCode and subsequently saves them, it would be advantageous to monitor this activity. One way to achieve this would be to repeatedly execute the above
vscode_open_edits function and manually check for any changes. However, we have a third and final new function in Medusa,
vscode_watch_edits, which polls the backup folder and reports on any changes. A demo of this can be seen in the below video, as we make incremental changes to a file in VSCode.
Bonus Round: Modifying An Open Edit
Whilst we have the ability to see the unsaved edits for files in VSCode, we also have the ability to make edits of our own. We can achieve this by modifying the backup files we discovered above.
In our scenario, we’ll modify the backup corresponding with our
secrets.txt file to be the following:
Returning to VSCode we’ll see that the open
secrets.txt remains unchanged. However, we can prompt it to read from the backup file by terminating VSCode and restarting it.
In our contrived scenario, our changes to the one line in
secrets.txt are rather obvious, but in the context of a larger open code-base, or some other open configuration files, this could prove valuable.
There is a wealth of data present in VSCode’s global
state datebase, as well as in individual workspaces’
state databases (i.e. those present in
One such example is the data present under the
codelens/cache2 key which includes details on the files opened in a given workspace, along with each file’s line count. No doubt there are plenty of other reconnaissance activities that could be facilitated here too.
In this short blog we’ve seen how several VSCode configuration files can be used to gain an understanding of the recent files and folders a user has accessed. This is particularly useful as the JSON and sqlite files in question fall outside of TCC’s protection.
Having identified recent access, we’ve also looked at how the mechanism by which VSCode ‘backs up’ unsaved edits can be exploited to view file content which could otherwise be protected by TCC. We’ve then seen how the Medusa Mythic agent’s new functions allow us to operationalise these reconnaissance activities.
The data exposure described in this blog was reported to Apple and MSRC in August 2021, both of which did not deem the issue to warrant a fix.