Suo 投毒

原理

根据 微软文档,.suo是CompoundFile类文件,在打开.suo文件时会调用VS Package上的LoadUserOptions方法,使用Structured Storage Viewer打开攻击者制作的恶意.suio可以找到如下,很容易看出里面有一段序列化数据

根据 文章披露的Microsoft.Visual.dll,在其内找到如下代码,即在OnLoadOptions方法中会调用VsToolboxService.LoadOptions并把数据作为stream参数传进去

// Microsoft.VisualStudio.VSCorePackage  
// Token: 0x06000108 RID: 264 RVA: 0x00003C5C File Offset: 0x00001E5C  
protected override void OnLoadOptions(string name, Stream stream)
{
	if (name.Equals(typeof(VsToolboxService).Name))
	{
		VsToolboxService vsToolboxService = this.GetService(typeof(IToolboxService)) as VsToolboxService;
		if (vsToolboxService != null)
		{
			vsToolboxService.LoadOptions(stream);
		}
	}
}

// Microsoft.VisualStudio.Toolbox.VsToolboxService  
// Token: 0x06000289 RID: 649 RVA: 0x0000B23C File Offset: 0x0000943C  
		internal void LoadOptions(Stream stream)
		{
			BinaryReader binaryReader = new BinaryReader(stream);
			BinaryFormatter binaryFormatter = new BinaryFormatter();
			int num = binaryReader.ReadInt32();
			for (int i = 0; i < num; i++)
			{
				string category = binaryReader.ReadString();
				int num2 = binaryReader.ReadInt32();
				for (int j = 0; j < num2; j++)
				{
					string text = this.Links.Read(stream);
					VsToolboxService.ToolboxItemContainer toolboxItemContainer = (VsToolboxService.ToolboxItemContainer)binaryFormatter.Deserialize(stream);
					if (text != null && File.Exists(text))
					{
						toolboxItemContainer.LinkFile = text;
						this.Links.TrackLink(text);
						this.Items.GetFilteredList(category).Add(toolboxItemContainer);
					}
				}
			}
		}

很容易看出里面通过binaryFormatter.Deserialize直接反序列化了相关数据,这就是很经典的反序列化执行代码。

如何制作恶意.suo

在Microsoft.VisualStudio.VSCorePackage类中存在OnSaveOptions,调用SaveOptions保存序列化数据,而CompoundFile可以通过OpenMCDF处理,或者直接把现有的.suo里面的序列化数据进行替换也可。

protected override void OnSaveOptions(string name, Stream stream)
{
	if (name.Equals(typeof(VsToolboxService).Name))
	{
		VsToolboxService vsToolboxService = this.GetService(typeof(IToolboxService)) as VsToolboxService;
		if (vsToolboxService != null)
		{
			vsToolboxService.SaveOptions(stream);
		}
	}
}
		internal void SaveOptions(Stream stream)
		{
			try
			{
				IEnumToolboxTabs enumToolboxTabs = null;
				long position = stream.Position;
				int num = 0;
				int num2 = 0;
				IVsToolbox vsToolbox = this.VsToolbox;
				if (vsToolbox != null)
				{
					BinaryWriter binaryWriter = new BinaryWriter(stream);
					BinaryFormatter binaryFormatter = new BinaryFormatter();
					binaryWriter.Write(0);
					string[] array = new string[1];
					if (this._links != null && this._links.ContainsLinks)
					{
						NativeMethods.ThrowOnFailure(vsToolbox.EnumTabs(ref enumToolboxTabs));
						try
						{
							uint num3;
							NativeMethods.ThrowOnFailure(enumToolboxTabs.Next(1U, array, ref num3));
							long position3;
							while (num3 == 1U)
							{
								string text = array[0];
								num++;
								binaryWriter.Write(text);
								IEnumToolboxItems enumToolboxItems;
								NativeMethods.ThrowOnFailure(vsToolbox.EnumItems(text, ref enumToolboxItems));
								long position2 = stream.Position;
								binaryWriter.Write(0);
								try
								{
									IDataObject[] array2 = new IDataObject[1];
									uint num4;
									NativeMethods.ThrowOnFailure(enumToolboxItems.Next(1U, array2, ref num4));
									while (num4 == 1U)
									{
										IDataObject dataObject = array2[0];
										VsToolboxService.ToolboxDataObject toolboxDataObject = dataObject as VsToolboxService.ToolboxDataObject;
										if (toolboxDataObject != null)
										{
											VsToolboxService.ToolboxItemContainer toolboxItemContainer = toolboxDataObject.ToolboxItemContainer;
											string linkFile = toolboxItemContainer.LinkFile;
											if (linkFile != null && this.Links.Write(linkFile, stream))
											{
												binaryFormatter.Serialize(stream, toolboxItemContainer);
												num2++;
											}
										}
										num4 = 0U;
										NativeMethods.ThrowOnFailure(enumToolboxItems.Next(1U, array2, ref num4));
									}
									position3 = stream.Position;
									stream.Position = position2;
									binaryWriter.Write(num2);
									num2 = 0;
									stream.Position = position3;
								}
								catch
								{
								}
								enumToolboxItems = null;
								num3 = 0U;
								NativeMethods.ThrowOnFailure(enumToolboxTabs.Next(1U, array, ref num3));
							}
							position3 = stream.Position;
							stream.Position = position;
							binaryWriter.Write(num);
							stream.Position = position3;
						}
						catch
						{
						}
					}
					binaryWriter.Flush();
				}
			}
			catch
			{
			}
		}

其他

本文无意对APT样本进行分析,dump出样本,需要的可在https://github.com/Chestnuts4/APT_simple/blob/main/Ocean_Lotus/mal.dll下载

参考

sln任意代码执行的几种方式(evilsln) # Solution User Options (.suo) File EvilSln

创建于:Thursday, January 9,2025
最后修改于: Thursday, January 9,2025