Skip to content

Latest commit

 

History

History
100 lines (70 loc) · 4.04 KB

019.md

File metadata and controls

100 lines (70 loc) · 4.04 KB

Brilliant Umber Eagle

Medium

CreateFinalityProvider tx can be front-ran by anyone

Summary

Anyone can front-run CreateFinalityProvider tx.

Root Cause

When any fp(finality provider) creates an fp to Babylon chain by calling CreateFinalityProvider, it verifies the data fields here, if the sign is verified success, the fp can be added to the chain db. After that, if the same BtcPk is created again by the same function, the latter tx can fail because here, such that any malicious user can front-run the legit fp create a tx to modify the necessary fields such that Commission and Description. The commission represents a percentage of rewards that fp takes from the delegators' rewards as a fee for running and maintaining their validation services, any user can set it to a smaller value than the fp wanted to reduce the fp commission fee.

Internal Pre-conditions

None.

External Pre-conditions

None.

Attack Path

  1. Legit fp wants to create the fp to the Babylon chain with correct commission and desc.
  2. Malicious users monitor the chain mempool and front-run the fp tx but modify the commission by sending the tx with cosmos grpc message instead of cmd client.
  3. The front-run tx executes successfully.
  4. The legit tx execute fails because the BtcPk is already registered in the chain db.

Impact

The fp CreateFinalityProvider tx can be front-ran by any user to modify the commission parameter to reduce the fp commission fee, such that cause the fp may loss some commission fee until the commission be edited by EditFinalityProvider function. The fp may loss more if they dont notice that in a long time. Due to it can be edited at any time, so it should be loss funds temporiry and deserve medium severity.

PoC

func TestMsgCreateFinalityProviderFrontRun(t *testing.T) {
	// Create a new random source with fixed seed for reproducibility
	r := rand.New(rand.NewSource(1))
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	// mock BTC light client and BTC checkpoint modules
	btclcKeeper := types.NewMockBTCLightClientKeeper(ctrl)
	btccKeeper := types.NewMockBtcCheckpointKeeper(ctrl)
	h := testutil.NewHelper(t, btclcKeeper, btccKeeper)

	// set all parameters
	h.GenAndApplyParams(r)

	// Test creating a finality provider with empty commission
	fp, err := datagen.GenRandomFinalityProvider(r)
	require.NoError(t, err)

	// Set commission to 0.1
	commission, err := sdkmath.LegacyNewDecFromStr("0.01")
	require.NoError(t, err)
	msg := &types.MsgCreateFinalityProvider{
		Addr:        fp.Addr,
		Description: fp.Description,
		Commission:  &commission,
		BtcPk:       fp.BtcPk,
		Pop:         fp.Pop,
	}

	// First creation should succeed
	_, err = h.MsgServer.CreateFinalityProvider(h.Ctx, msg)
	require.NoError(t, err)

	// Verify finality provider exists in store
	btcPK := *fp.BtcPk
	require.True(t, h.BTCStakingKeeper.HasFinalityProvider(h.Ctx, btcPK))

	// Attempt to create duplicate finality provider
	duplicateMsg := &types.MsgCreateFinalityProvider{
		Addr:        fp.Addr,
		Description: fp.Description,
		Commission:  fp.Commission,
		BtcPk:       fp.BtcPk,
		Pop:         fp.Pop,
	}

	// Duplicate creation should fail
	_, err = h.MsgServer.CreateFinalityProvider(h.Ctx, duplicateMsg)
	require.Error(t, err)

	// Get stored finality provider and verify commission
	storedFp, err := h.BTCStakingKeeper.GetFinalityProvider(h.Ctx, btcPK)
	require.NoError(t, err)
	require.Equal(t, commission, *storedFp.Commission)
}

Mitigation

  1. It's better to recognize the context sender when validating the fields.
  2. Sign the address, commission, description, add nonce as well such as ethereum permit function, verify it when creating the fp.