gRPC Fuzzing

Mayhem for API has experimental support for gRPC servers through the use of a gRPC gateway. The gateway comes with a protoc plugin. It reads a gRPC service definition and generates a reverse-proxy server which translates a RESTful JSON API into gRPC.

gRPC Proxy diagram

This guide will walk you through the steps to fuzz a gRPC server. While this method can be used with any language, the guide assumes you're using go. If you're not and are having trouble following the steps for another language, let us know and we'll be happy to help out.

Installing gRPC-Gateway

Follow the instructions in the README. You'll need to have go and protoc installed.

Generate Server Stub

The following command generates the proxy stubs from your .proto file:

protoc -I . --grpc-gateway_out . \
    --grpc-gateway_opt logtostderr=true \
    --grpc-gateway_opt paths=source_relative \
    --grpc-gateway_opt generate_unbound_methods=true \
    your/service/v1/your_service.proto

The command above generates the gateway stub at your/service/v1/your_service.pb.gw.go. We will import the stubs to write the proxy.

Write an entrypoint for the proxy

We will now write the proxy in rest-proxy.go. The proxy will listen on port 8081 and forward requests to your gRPC server.

package main

import (
  "context"
  "flag"
  "net/http"

  "github.com/golang/glog"
  "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
  "google.golang.org/grpc"

  gw "github.com/your-org/your-project/your/service/v1"
)

var (
  // command-line options:
  // gRPC server endpoint
  grpcServerEndpoint = flag.String("grpc-server-endpoint",  "localhost:10990", "gRPC server endpoint")
)

func run() error {
  ctx := context.Background()
  ctx, cancel := context.WithCancel(ctx)
  defer cancel()

  // Register gRPC server endpoint
  // Note: Make sure the gRPC server is running properly and accessible
  mux := runtime.NewServeMux()
  opts := []grpc.DialOption{grpc.WithInsecure()}
  err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux,  *grpcServerEndpoint, opts)
  if err != nil {
    return err
  }

  // Start HTTP server (and proxy calls to gRPC server endpoint)
  return http.ListenAndServe(":8081", mux)
}

func main() {
  flag.Parse()
  defer glog.Flush()

  if err := run(); err != nil {
    glog.Fatal(err)
  }
}

You will need to change three things:

  • The import line "github.com/your-org/your-project/your/service/v1" to match your project and the path to the stub you generated in the previous step.
  • The url to your gRPC server. The code above assumes your gRPC server is listening on localhost:10990.
  • The call to gw.RegisterYourServiceHandlerFromEndpoint to match your service name. The function should be present in the gateway stub you generated earlier if you're not sure what the name should be.

Generate the OpenAPI Specification

The following command generates the OpenAPI specification:

protoc -I . --openapiv2_out . \
    --openapiv2_opt logtostderr=true \
    --openapiv2_opt generate_unbound_methods=true \
    your/service/v1/your_service.proto

Fuzz

  1. Start your gRPC server
  2. Start the proxy with go run rest-proxy.go
  3. Create a target: mapi target create target-name http://localhost:8081
  4. Fuzz: mapi run target-name 120 your/service/v1/your_service.openapi.json