Skip to content

[AI Security] High: Client.from()/fromWasmHash() trusts RPC-fetched ABI, enabling argument reordering by malicious RPC #1368

@sagpatil

Description

@sagpatil

Security Finding: RPC-Fetched ABI Enables Positional Argument Reordering in Contract Calls

Subsystem: contract
Severity: High
Model: claude-sonnet-4.6 (default)
Finding ID: H060


Root Cause

contract.Client.from() and Client.fromWasmHash() fetch WASM from the configured RPC endpoint and derive the callable Spec from that remote blob. Dynamic client methods then call spec.funcArgsToScVals(method, args), which serializes arguments in the order declared by the fetched spec.

Soroban invoke-contract arguments are positional on-chain. Because the SDK does not verify that the fetched WASM/spec matches a locally trusted ABI, a malicious RPC can keep the same parameter names while changing their declaration order and thereby alter the signed invocation.

Relevant code paths:

  • src/contract/client.ts: Client.from(), fromWasmHash()
  • src/contract/spec.ts: funcArgsToScVals() uses spec-declared order for positional encoding

Attack Vector

  1. Application uses contract.Client.from() pointing at an attacker-controlled or compromised RPC endpoint.
  2. The attacker returns forged WASM whose contractspecv0 section defines a target method with the same parameter names but a different declaration order for two type-compatible parameters (e.g., two Address inputs).
  3. The application calls the method with a benign-looking object like { primary, backup }.
  4. The SDK encodes values into positional Soroban arguments following the forged ABI order.
  5. The user signs a transaction for the real contract and method name, but with attacker-chosen argument ordering — silently altering contract behavior.

PoC Code

PoC result: The serialized on-chain argument order decodes to [backup, primary] instead of [primary, backup], confirming the SDK trusted the forged RPC-fetched ABI ordering.

Recommendation

Do not treat RPC-fetched WASM/spec as an integrity source for user-facing method encoding. Prefer one of:

  1. Require a locally trusted spec/WASM for signing flows — callers must supply the spec they trust.
  2. Pin and verify an expected WASM hash or spec digest before using RPC-fetched metadata for argument encoding.
  3. Clearly separate dynamic RPC-derived clients from locally generated bindings and warn that dynamic clients inherit the RPC trust boundary.

At minimum, the SDK documentation for Client.from() and fromWasmHash() should explicitly warn that these methods are unsafe against malicious RPCs for same-typed argument layouts, and steer security-sensitive applications toward locally generated bindings (e.g., via stellar contract bindings typescript) or locally supplied specs.

Note: Locally generated bindings (via stellar contract bindings typescript) embed the spec locally and do not suffer from this vulnerability — they are the recommended safe alternative for production use.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-generatedGenerated by AI security analysis pipelinesecuritySecurity vulnerability or concern

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Backlog (Not Ready)

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions