Add protocol/grpc helper for plaintext gRPC exploits#588
Open
vlobstein-vc wants to merge 8 commits into
Open
Conversation
98987f2 to
12be363
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new protocol/grpc helper package to reduce boilerplate for exploit modules interacting with plaintext (h2c) and TLS gRPC services, mirroring the ergonomics provided by protocol/httphelper.
Changes:
- Introduces
protocol/grpcwithDialandInvokebuilt around a raw-bytes gRPC codec, plus minimal proto3 wire-format helpers (EncodeBytesField,EncodeStringField). - Adds unit tests covering the proto field encoding helpers.
- Updates
go.mod/go.sumto includegoogle.golang.org/grpcand related transitive dependencies.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| protocol/grpc/grpc.go | New gRPC helper API (Dial/Invoke), raw codec, and simple proto wire-format encoders. |
| protocol/grpc/grpc_test.go | Tests for EncodeBytesField / EncodeStringField fixtures and varint length encoding. |
| go.mod | Adds google.golang.org/grpc and required indirect protobuf/genproto deps. |
| go.sum | Records checksums for newly introduced direct/transitive modules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
j-baines
reviewed
May 8, 2026
lobsterjerusalem
requested changes
May 12, 2026
Collaborator
lobsterjerusalem
left a comment
There was a problem hiding this comment.
A couple of comments.
Modules targeting unauth gRPC services (lerobot async-inference, etc.) need three things: dial an h2c endpoint, ship pre-encoded protobuf bytes through a unary RPC, and shape the request as a proto3 message without pulling in protoc. The package wraps grpc-go behind two high-level functions: Invoke(host, port, method, in []byte, timeoutSec int) ([]byte, bool) EncodeBytesField(fieldNumber uint32, value []byte) []byte A package-level rawCodec passes pre-encoded bytes through grpc-go's codec pipeline untouched so callers do not need proto.Message values. The codec is registered from package init because grpc-go's codec registry is process-wide. Tests cover EncodeBytesField with empty / short / multi-byte-varint / high-tag inputs plus a thin EncodeStringField wrapper. The framework now depends on google.golang.org/grpc; one earlier exploit module (cve-2025-68926) already pulled it transitively, so this hoists what was effectively a pseudo-shared dependency into a proper shared package. Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
Doc strings were stating the obvious on every exported symbol. Tightened to one-liners where the function name + signature already say enough. Two parallel sentinel errors (errMarshalType / errUnmarshalType) collapsed into a single errCodecType with the direction kept in the format string. Tests: dropped the per-call /* Field N, wire type 2, length M */ narration, regrouped the basic EncodeBytesField cases into a table-driven subtest, and replaced a "12017800"[:6] slicing hack with the literal hex value. Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
Plaintext h2c was the only transport before; many real gRPC services
run TLS on 443 with ALPN h2 negotiation. Add a ssl boolean at the end
of Invoke's signature, matching the framework's HTTP helper convention
where SSL is wired through conf.SSL. The TLS path uses
InsecureSkipVerify since exploit targets routinely use self-signed or
expired certs.
Caller side:
grpc.Invoke(conf.Rhost, conf.Rport, method, body, 5, conf.SSL)
Internal transportCreds helper centralises the credential choice so
the Invoke body stays one screen.
Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
Invoke covered the unary fast path; everything else (server- / client- / bidi-streaming, reflection, multi-RPC on one conn) was off limits because the conn was internal. Surface Dial as a public entry point that returns the configured *grpc.ClientConn so callers can drive grpc.ClientStream directly when they need streaming or to amortise dial cost across calls. Invoke now uses Dial under the hood to keep its body short. Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
CodeQL flagged InsecureSkipVerify on transportCreds. The pattern is intentional and matches protocol/httphelper.go which has the same alert dismissed for the same reason: exploit modules have no control over the cert validity or TLS version on the remote target. Add MinVersion: tls.VersionSSL30 to match the http helper exactly and update the comment so the rationale lives next to the code. Repo maintainers will dismiss the new CodeQL alert as they did for httphelper's identical instances. Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
…ork log level Signed-off-by: Valentin Lobstein <281638514+vlobstein-vc@users.noreply.github.com>
1c6d37f to
d5c59da
Compare
lobsterjerusalem
approved these changes
May 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The framework already eliminates HTTP boilerplate for modules via
protocol/httphelper: callers sayHTTPSendAndRecv(...)and the framework handles the client, transport, TLS, timeouts, redirects. Modules targeting gRPC services have not had the same treatment, so they rewrite the same dial / codec / context / TLS plumbing every time. This PR fills that gap withprotocol/grpc/.Two layers:
Invoke(host, port, method, in, timeoutSec, ssl)runs a unary RPC and returns the response body orok=falseon any failure. Modules stay free offmt,contextandgoogle.golang.org/grpcimports.Dial(host, port, ssl)returns a configured*grpc.ClientConnfor everything Invoke does not cover: server- / client- / bidi-streaming RPCs, gRPC reflection, or amortising one connection across many calls. The caller drives the standard grpc-go ClientStream API.EncodeBytesField/EncodeStringFieldwrap values as proto3 length-delimited fields so modules can skip protoc on simple messages. For complex schemas, callers bring their own protoc-generated marshallers and pass the resulting bytes through the raw codec.The TLS config mirrors
protocol/httphelper.go(InsecureSkipVerify, permissive MinVersion) for the same reason: exploit targets routinely have self-signed or expired certs and unknown TLS versions.This adds
google.golang.org/grpcto the frameworkgo.mod. One earlier exploit module already pulled it transitively, so this hoists what was a pseudo-shared dependency into a real shared package.