Nicky_home⛄

Nicky模拟人生.Log

Visual Studio项目利用

Visual Studio项目利用

Visual Studio (IDE) 与 VS Code(文本编辑器)是不同的,前者是集成开发环境,后者则是文本编辑器。在Visual Studio中加载项目时,将在后台执行各种预操作。

1、在编译前加载恶意指令

PreBuildEvent: 在工程编译前执行任意命令。


    
    cmd /c calc
    

2、在编译时触发

设置调试命令行

image-20250117145148418

image-20250117145247024

生成事件命令行

三事件都可以利用

image-20250117145449801

自定义生产工具

image-20250117150106437

image-20250117153344264

输出需要填数据 任意数据都可以 没有输出数据不会执行

在编译时加载恶意类型库

利用链:

  1. Microsoft 的 C++ 编译器(预处理器)通过#import 指令加载类型库引用LoadTypeLib()。
  2. LoadTypeLib()加载类型库。将做嵌套引用,即创建一个类型库,该类型库引用另一个类型库,该类型库实际上是一个名字对象字符串。
  3. MkParseDisplayName()分析名字对象字符串,然后通过 IMoniker::BindToObject()绑定 Windows 脚本组件对象。
  4. Script Component 对象将加载我们的恶意脚本文件,该文件可以托管在任意网站上。

img

3、在项目打开时触发

使用MSBuild任务可实现打开项目就触发

在.vcxproj文件中添加

image-20250117153803563

在项目打开TypeLib加载时触发

COMFileReference: 项目打开TypeLib加载时触发


     True

img

利用链路:

  1. 打开项目文件后,Visual Studio 将加载通过 COMFileReference 标记指定的所有类型库。
  2. Properties 窗口将分析所有类型库中的 HelpstringDLL 属性。
  3. 我们的恶意 DLL 将通过 LoadLibrary()和DLLGetDocumentation()调用。

suo文件反序列化漏洞

在打开项目后,Visual Studio 会自动在项目根目录生成一个名为 .vs 的文件夹,该文件夹中包含一个特殊的二进制文件 .suo

与纯文本的 .sln.csproj 文件不同,.suo 文件具有隐藏属性(在文件资源管理器中,以 . 开头的文件和文件夹默认不显示),并且其内容可读性较差。

当 Visual Studio 关闭时,会将新内容保存到 .suo 文件中,也就是说,恶意代码执行一次即会被覆盖删除,具有极强的隐蔽性。

实践:

使用ysoserial.net以以下命令行生成payload

ysoserial -g ClaimsIdentity -f BinaryFormatter -c calc -o base64 -bgc TypeConfuseDelegate

image-20250117161328154

把payload塞到.sou里

修改了工具VS_Deserialize_Exploit改成读取payload文件

image-20250117180100060

还可以使用SSview.exe直接在VsToolboxService流载入payload只是这个payload需要构造一下

image-20250117180959417

构造好的复制到对应的流

image-20250117183544793

保存流数据使用Decoder—>Apply extra…

投毒实例:

投毒者:https://github.com/0xjiefeng

其下的多个repo使用类似Visual Studio 项目模板

https://github.com/0xjiefeng/CVE-2024-35250-BOF

https://github.com/0xjiefeng/BOF-BypassUAC

https://github.com/0xjiefeng/SspiUacBypass-BOF

现均已无法访问。

使用Structured Storage Viewer工具,打开.suo文件分析数据。在VsToolboxService流中发现明显可疑的反序列化数据。

image-20250116172019052

打开项目时,在加载VSPackage中VsToolboxService流的过程中,会用到BinaryFormatter做反序列化,此时反序列化的对象包含了可执行代码,就会运行这些代码。

保存这段数据做base64解密后得到:

image-20250116172037406

将其中base64加密字段单独提取保存解密后得到:

image-20250116173428314

打开可以看到是释放两个恶意文件,并且将程序加入注册表自启动。

image-20250116173303484

参考:

攻击 Visual Studio 进行初始访问 |包抄

sln任意代码执行的几种方式(evilsln) | CrackMe.net

拓展:

检测恶意sln的项目:backengineering/CheckEvilSln: A simple python script to check evil Visual Studio projects

import os
import sys
import xml.etree.ElementTree as ET

def check_common(foldername, filename):
    tree = ET.parse(os.path.join(foldername, filename))
    root = tree.getroot()
    namespaces = {'ns': root.tag.split('}')[0].strip('{')}

    # Check 
    for target in root.findall(".//ns:Target[@Name='GetFrameworkPaths']", namespaces):
        print(f'Found({filename}) ')

    # Check 
    for com_file_ref in root.findall(".//ns:COMFileReference", namespaces):
        include_attr = com_file_ref.get('Include')
        if include_attr:
            print(f'Found({filename}) ')

    # Check 
    for pre_build_event in root.findall(".//ns:PreBuildEvent", namespaces):
        for command in pre_build_event.findall(".//ns:Command", namespaces):
            print(f'Found({filename}) PreBuildEvent command: {command.text}')

    # Check 
    for pre_build_event in root.findall(".//ns:PreLinkEvent", namespaces):
        for command in pre_build_event.findall(".//ns:Command", namespaces):
            print(f'Found({filename}) PreLinkEvent command: {command.text}')

    # Check 
    for pre_build_event in root.findall(".//ns:PostBuildEvent", namespaces):
        for command in pre_build_event.findall(".//ns:Command", namespaces):
            print(f'Found({filename}) PostBuildEvent command: {command.text}')

# Check m_serializedClaims  检查序列化变量
def check_serialized_claims(foldername, filename):
    with open(foldername + "\\" + filename, 'rb') as file:
        content = file.read()
        if b'm_serializedClaims' in content:
            print(f'Found({filename}) m_serializedClaims')

def scan_folder(path):
    for foldername, subfolders, filenames in os.walk(path):
        for filename in filenames:
            if filename.endswith('.vcxproj'):
                check_common(foldername, filename)
            elif filename.endswith('.suo'):
                check_serialized_claims(foldername, filename)

def main():
    if len(sys.argv) < 2:
        print('Please input the directory you want to check.')
        return   

    path = sys.argv[1]
    scan_folder(path)

if __name__ == '__main__':
    main()

里面包含了上面所有介绍到的方法的检测

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注