DEV Community

Peter Mulligan
Peter Mulligan

Posted on

Your IDE is an Attack Vector

A new type of VSCode phishing attack is targeting freelancers via Upwork. Here’s how it works and how to protect yourself.


Hi, I'm Peter and I like to build things.

In recent years, we've seen a rise in high-profile attacks that use lifecycle hooks to run malicious code. The attack I am here to shine a spotlight on uses a similar "manipulate the tooling" mindset.

I work as a freelancer through a platform called Upwork. Over the last couple of months I have seen this platform turn into a hunting ground for a new type of phishing attack that uses VSCode as the attack vector.

It is an interesting attack because it sits somewhere between traditional phishing and spear phishing. It doesn't have a specific target but it doesn't cast a wide net. It uses a highly refined approach that enables the attacker to create a pool of self-selected high-value targets. Here's how it goes down...


The Tech

Visual Studio Code has the concept of tasks. They are defined in .vscode/tasks.json at the root of the project. Tasks are best thought of as an interface between the editor and your local tooling. They let you describe how commands should be run; what shell to use, what arguments to pass, what order to run them in. That kind of thing. VSCode has native bindings for npm, Gulp, Grunt, and Jake.

This is an auto-generated task for npm run build:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "npm: build",
      "type": "shell",
      "command": "npm",
      "args": ["run", "build"],
      "isBackground": false,
      "problemMatcher": ["$tsc"],
      "group": "build"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

If you need to run commands for a build tool that VSCode doesn't have native bindings for, you can do that using a custom task. These let you run commands with a variety of options as shown here in the schema. For now, we're only interested in the runsOn option.

From the docs:

Specifies when a task is run. Valid values are:

  • "default": The task will only be run when executed through the Run Task command.

  • "folderOpen": The task will be run when the containing folder is opened.

Now, if you're anything like me, the description of folderOpen is terrifying. Arbitrary code execution when the folder is opened? That can't be right. Surely there are protections in place?

Well yes, but actually no

There is a setting (task.allowAutomaticTasks) that lets you control the behavior. You can set it to always allow, always ask, or always deny. So we set it to "always ask" and we're good, right?

Unfortunately, as mentioned in this GitHub issue, when you clone a new repository and VSCode asks you if you trust the authors, that choice takes precedence over your settings. If you click "yes", you have told VSCode that you trust this project explicitly.

VSCode Trust Dialog


The Bait

The attacker posts a job on Upwork. On the surface, it looks the same as all the others. Tech stack and company description. Corporate jargon. Indistinguishable at first glance.

The jobs are almost exclusively in the crypto or DeFi domain. They are usually for between $75 and $150 per hour. They promise long-term work for the right candidate. They talk of technical excellence and team culture. It often mentions there will be a small take-home but not always.

Taken at face value, it looks like a good opportunity for a lot of developers. Developers in the crypto/DeFi sphere. Developers likely to have crypto wallets and api keys on their machine.


The Social Dynamics

It is free to post a job on Upwork. Freelancers then pay a small amount to submit a proposal for the project. The client reads the proposals and invites potential candidates into a chat room to interview. This allows both sides to define terms before initiating the contract.

For the freelancer, it's time to close the deal. Pressure is high and you can see from Upwork's UI that the client is interviewing multiple developers. Better bring your A-game.

The client sends a GitHub link.

It is cloned without question.


Do you trust the authors of the files in this folder?


The Payload

The handful of times I have encountered this attack (I stopped bidding on the jobs pretty quick!), the command being run was a lot of whitespace followed by a wget to a Vercel endpoint being piped into cmd. 😱

The whitespace confused me at first until I viewed the code on GitHub. Their code-viewer doesn't wrap lines, so I needed to scroll to the right about 3 times the width of the page to see the wget.


I didn't know I was going to turn this into an article at the time and I did not download the content of the URLs so I don't know what the malicious code itself did, but from context it downloads a crypto wallet exfiltration script. I still have one of the URLs if someone more skilled than me wants to go poking around. Just reach out.


Why This Attack Works

This attack is very well thought out. It puts itself in the target's shoes. I have no way of gathering data on its effectiveness but I can imagine it works wonders considering the amounts of these jobs posted daily.

The mechanics of Upwork do the heavy lifting of the social engineering. The ability to filter for crypto/DeFi developers and post a job for free provides frictionless target selection. The competitive and high-stress nature of the proposal system means you only interact with the target when they are already stressed. The platform’s social norm of running test repos, combined with the fact that freelancers are less likely to follow proper isolation protocols, makes this attack more robust.

The target selection is masterful, but combined with silent script execution when the project is opened in VSCode is devastating.


How To Stay Safe

Only open random repositories in an isolated environment (container or VM). Inspect them before running anything. Does it have a postinstall or preinstall script? What do they do? Does it have a .vscode folder? Why?

You can use VSCode's dev containers feature to open untrusted projects safely. From there, you can watch for any weird activity before it touches your main system.

You can also open projects with VSCode from the command line using the --disable-extensions flag. (code --disable-extensions .)

The key takeaway: Treat anything you didn’t personally create as untrusted by default.


Connect with me on LinkedIn or Upwork.

Top comments (13)

Collapse
 
maame-codes profile image
Maame Afua A. P. Fordjour

This is a real eye-opener. I usually think about security in my code, but I don't always think about the security of the editor itself. It is a great reminder to be more careful about which extensions I install on my Windows machine. Thanks for sharing these risks!

Collapse
 
pengeszikra profile image
Peter Vivo

Good to know about this attack vector. My instict are saved me, because on project which I work I add .vscode/ to .gitignore.
In other way my editor preference is vim, zed, nvim, VScode.
But for my focus on minimalism I am writing a cli based editor in rust, even a lot fever funcionality than vim

Collapse
 
aezur profile image
Peter Mulligan

I haven't checked if vim et al have lifecycle hooks that can be exploited, but I know what rabbit-hole I'm going down for the day!

My instict are saved me, because on project which I work I add .vscode/ to .gitignore.

Unfortunately, this won't save you from this particular attack because the creator of the repo didn't add it to the .gitignore so when you clone it is already in the project.

In general I agree though; saving the .vscode folder to the repo is something that only makes sense on a team repo, and even then I would prefer to just enforce anything I need to enforce in the CI pipeline. I don't care what is happening on the dev's local machine.

Collapse
 
pengeszikra profile image
Peter Vivo

I really rare clone repo from github for example, that why do not open in VSCode

Maybe I missing where this attack happen not under local development, when you open a repo with .vscode/task.json ?

Thread Thread
 
aezur profile image
Peter Mulligan

Oh okay. I misunderstood what you were saying. If you don't use VSCode, you are immune to this specific attack.

I think that "tooling as an attack vector" is the wider danger though. As @nedcodes pointed out, git hooks and husky also introduce this type of repository-level attack. They're not auto-initiated, but the risks are similar.

Collapse
 
nedcodes profile image
Ned C

Solid writeup. The folderOpen task execution is genuinely scary - most devs I know have never even looked at .vscode/tasks.json in repos they clone.

This extends to AI coding tools too. A malicious .cursorrules or CLAUDE.md in a repo could influence the AI to generate vulnerable code, disable security checks, or exfiltrate context through crafted suggestions. The trust boundary keeps expanding and most people aren't thinking about it.

Collapse
 
aezur profile image
Peter Mulligan

100%. Everything works on a "local is trusted" model that is no longer true and our machines hold valuable credentials in a way they just didn't a few years ago.

Your point about AI is very powerful. If you let an agent run wild, it's very hard to be sure nothing on the environment level was changed. Git doesn't track that.

Collapse
 
nedcodes profile image
Ned C

Exactly. And git hooks are another blind spot - most people run whatever is in .husky/ or .git/hooks/ without a second thought. The whole local dev environment is basically an honor system at this point.

Thread Thread
 
aezur profile image
Peter Mulligan

Lucky all the big players are trying to move local to the cloud. 💀

Collapse
 
gass profile image
gass

what a nasty attack that is.

Collapse
 
aezur profile image
Peter Mulligan

Yeah, I think it's scary because we are the targets. It's a dev hunting game for them.

Collapse
 
gass profile image
gass

Let the dev hunting games begin! .. just joking 😆

Collapse
 
frandev profile image
Franco

Very good article, very clear, thanks for sharing and saving the developers from this attack. VS Code should take note of this to prevent these types of attacks.