For the complete documentation index, see llms.txt. This page is also available as Markdown.

75268 bc insight rpc enable admin false does not disable consensus admin rpc methods

Submitted on Apr 28th 2026 at 07:26:14 UTC by @silverologist for Audit Comp | Base Azul

  • Report ID: #75268

  • Report Type: Blockchain/DLT

  • Report severity: Insight

  • Target: https://github.com/base/base/tree/v0.8.0-rc.28

  • Impacts:

    • Network not being able to confirm new transactions (total network shutdown)

Description

Summary

The consensus node exposes the admin_* RPC namespace even when rpc.enable-admin is left disabled.

RpcArgs and RpcBuilder both model rpc.enable-admin as the flag that should enable the admin API. However, RpcActor::start() never checks that flag before merging AdminRpc into the live JSON-RPC module set. On Base nodes, the network_admin channel is wired unconditionally, and on sequencer nodes the sequencer admin client is also present. As a result, any reachable caller can invoke privileged admin_* methods such as admin_stopSequencer despite the operator never enabling the admin API.

Detailed Description

The CLI surface clearly models rpc.enable-admin as the switch for the consensus admin API.

In base/crates/client/cli/src/rpc.rs:30, the flag is documented as:

  • Enable the admin API.

That value is propagated into RpcBuilder.enable_admin in base/crates/client/cli/src/rpc.rs:72, and the consensus RPC config also documents the field the same way in base/crates/consensus/rpc/src/config.rs:12.

The problem is that the flag is never enforced when the RPC module set is built.

In base/crates/consensus/service/src/actors/rpc/actor.rs:119, RpcActor::start() merges AdminRpc whenever network_admin is present:

There is no self.config.enable_admin check anywhere in that path.

On real nodes, this condition is satisfied by default. In base/crates/consensus/service/src/service/node.rs:547, the node always starts the RPC actor with:

  • network_admin: Some(net_admin_rpc)

So the admin namespace is mounted whenever the RPC server is enabled, regardless of the flag.

On sequencer nodes, the impact is worse because a real sequencer admin client is also wired. AdminRpc::admin_stop_sequencer() in base/crates/consensus/rpc/src/admin.rs:107 forwards directly into the sequencer admin client, and stop_sequencer() in base/crates/consensus/service/src/actors/sequencer/admin_api_impl.rs:179 sets self.is_active = false, which stops sequencing.

The RPC server is also exposed on a public bind by default. RpcArgs.listen_addr defaults to 0.0.0.0 and listen_port defaults to 9545 in base/crates/client/cli/src/rpc.rs:24.

So the effective behavior is:

  1. Operator leaves rpc.enable-admin unset.

  2. Consensus RPC binds publicly on 0.0.0.0:9545.

  3. RpcActor mounts the admin_* namespace.

  4. A remote caller can invoke privileged methods that the operator did not intend to expose.

The critical manifestation is on sequencer nodes, where admin_stopSequencer can halt sequencing even though the operator never enabled the admin API, but the same issue applies to the other admin methods.

Concrete Exploit Sequence

The failure sequence is:

  1. Deploy or run a Base sequencer node with RPC enabled and set rpc.enable-admin to false in a setting where callers are not trusted.

  2. The node binds the consensus RPC server, by default on 0.0.0.0:9545.

  3. Because RpcActor::start() keys only on network_admin, it still merges AdminRpc into the live module set.

  4. An untrusted caller sends admin_stopSequencer to the node.

  5. AdminRpc::admin_stop_sequencer() forwards to the sequencer admin client.

  6. stop_sequencer() sets is_active = false and sequencing stops.

At that point the active sequencer can stop producing confirmations even though the operator did not enable the admin API.

Impact

A remote caller can invoke privileged consensus admin methods on a node whose operator never enabled the admin API.

On a sequencer node, the outcome is that admin_stopSequencer can halt sequencing. Under the bounty rules, this maps to:

  • Network not being able to confirm new transactions (total network shutdown)

Root Cause

The root cause is a mismatch between configuration intent and RPC module wiring:

  1. RpcArgs / RpcBuilder define enable_admin as the admin API gate.

  2. RpcActor::start() never consults that field.

  3. Instead, admin RPC exposure is controlled only by whether network_admin is present.

  4. Node::start() supplies Some(net_admin_rpc) unconditionally.

So the code carries an enable_admin flag all the way into runtime config, but the live server ignores it when deciding whether to expose AdminRpc.

RpcActor::start() should gate AdminRpc on self.config.enable_admin before merging it into the RPC module set.

A minimal fix is to require both:

  • self.config.enable_admin == true

  • network_admin.is_some()

before the admin namespace is mounted.

Proof of Concept

Apply the following git diff to base/crates/consensus/service/src/actors/rpc/actor.rs and run as cargo test -p base-consensus-node test_admin_rpc_exposed_even_when_enable_admin_is_false -- --nocapture:

The test starts a real RpcActor with:

  • enable_admin = false

  • network_admin = Some(...)

  • a live HTTP RPC server

It then calls admin_stopSequencer over JSON-RPC and asserts that the call succeeds and that the sequencer admin stub was actually invoked.

Was this helpful?