It takes a special kind of person to name a company after their own body part. Fortunately the Microsoft Security Response Center doesn’t seem to have inherited that kind of mentality, because when I have reported not a bug but a feature as a vulnerability - they accepted it.


Shell Integration

VSCodes’s builtin terminal has a feature called shell-integration. It means that it can extract from the terminal the shell’s prompt, command, output, exit code, and so on…

How does it do that?
Our good old friend escape sequences.

Using proprietary terminal escape sequences, and the right environment variables, VSCode can extract all that data from the shell.

VSCode defines several sequences relating to shell integration, and you can find them here. Today, for the sake of brevity, we’ll focus on just the following one:

"\x1b]633;E;ping\x07" ( OSC 633 ; E ; command ST )

So this sequence, once observed in the output by VSCode, will set the last executed command (on the side of VSCode, not the shell itself). So, if… for example… someone were to cat a file that contained such sequence, and then try to rerun it using VSCode’s rerun option, they would end up running the command from the sequence and not the cat command.

Also note that such a sequence isn’t visible in the terminal, and there’s no visual indication of the command changing.

Here’s the exploit:


nc -Nl 8000 << EOF
HTTP/1.1 200 OK

$(printf '\x1b[1;4m')ERROR$(printf '\x1b[0;1m'): Host Unavailable$(printf '\x1b[0m')
Could not reach $(printf '\x1b[3m')$(printf '\x1b[0m')
$(printf '\x1b[31m')...convincing error log...$(printf '\x1b[39m')
$(printf '\x1b[32m')Better luck next time!$(printf '\x1b[39m')
$(printf '\x1b]633;E;ping\x07')

vscode terminal image

Using curl to display the response of that server, and then rerunning it using VSCode’s shell integration, would have resulted with running ping

The vulnerability was first mitigated by prompting for confirmation on the command. That is better than nothing, but most users would probably confirm it without giving it too much thought. A better solution was later introduced, using a nonce to verify the authenticity of the sequence. I have not verified it myself, but on paper it is better.

This was the most straight forward vulnerability I’ve ever encountered. Probably ever will.

Thanks again to MSRC for handling my report with an open mind.