GHSA-7W38-GGH5-V542

Vulnerability from github – Published: 2026-11-03 17:51 – Updated: 2026-11-03 17:51
VLAI
Summary
Elo Ruby backend has Remote Code Execution via unescaped `#{...}` interpolation in emitted string literals and subtype-constraint labels
Details

Summary

Elo's Ruby backend emits attacker-controlled text into Ruby double-quoted strings without escaping the # character. Because # begins Ruby's interpolation sequences — #{expr}, #@ivar, #@@cvar, #$gvar — and any expression wrapped in #{...} may use backticks for shell execution, an Elo program that contains a crafted string literal or a labelled subtype constraint compiles to Ruby that evaluates attacker-chosen Ruby, and through backticks runs attacker-chosen shell commands, at runtime.

The same Elo source compiled to JavaScript, Python, or SQL is benign, so the bug also breaks the cross-target portability guarantee the rest of the compiler honours.

Vulnerable sinks

Two sinks exist in src/compilers/ruby.ts at commit 749af7b:

  1. String-literal emitter — lines 180–184. The emitter escapes backslash, double quote, and control characters, but not #. Any #{...} sequence in an Elo string literal flows straight into the emitted Ruby source.

  2. Labelled subtype-constraint branch — lines 405–412. The error message passed to p_fail(p, "...") is wrapped in a Ruby double-quoted string with no escape pass at all. Because the parser at src/parser.ts:1339–1348 accepts single-quoted Elo strings as constraint labels, a label such as '#{ `id` }' flows unchanged into the emitted Ruby.

Attack surfaces

All three surfaces share the same root cause (the missing # escape in the Ruby double-quoted-string emission path) and are addressed by the same patch:

# Entry point Threat model
1 /try playground on elo-lang.org (Ruby target) 0-click server-side RCE against elo-lang.org infrastructure from a shared PoC URL
2 bin/eloc -t ruby -x on a CI or DevOps host RCE on any host that passes Elo strings through the CLI
3 Library API — defaultFormats.elo.parse() with a Ruby emission step Ruby RCE for any downstream integrator using Elo as a rule engine, configuration DSL, or form builder DSL (the primary documented use cases)

Proof of Concept

String-literal sink:

# poc.elo
'RES=#{`id 2>&1`}'
$ eloc -t ruby -x poc.elo
# emitted Ruby contains:  "RES=#{`id 2>&1`}"
# running it returns the output of id(1).

Replacing `id 2>&1` with `touch /tmp/pwned` produces a trivially observable filesystem side effect.

Subtype-constraint label sink:

# poc-subtype.elo
let X = Int(i | '#{`touch /tmp/pwned2`}': i > 0) in X(-1)

The predicate fails for -1, causing p_fail(p, "#{...}") to interpolate and execute the backtick command at the Ruby layer.

Both flows are automated in the reproduction script submitted with the original report and were verified against Ruby 3.x.

Root cause

The Ruby-emitter's double-quoted-string escape routine omitted # from its character set, and one emission path (the subtype-constraint label branch) performed no escape pass at all.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.0.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "@enspirit/elo"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.0.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-44266"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-116",
      "CWE-1336",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-11-03T17:51:30Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "### Summary\n\nElo\u0027s Ruby backend emits attacker-controlled text into Ruby double-quoted strings without escaping the `#` character. Because `#` begins Ruby\u0027s interpolation sequences \u2014 `#{expr}`, `#@ivar`, `#@@cvar`, `#$gvar` \u2014 and any expression wrapped in `#{...}` may use backticks for shell execution, an Elo program that contains a crafted string literal or a labelled subtype constraint compiles to Ruby that evaluates attacker-chosen Ruby, and through backticks runs attacker-chosen shell commands, at runtime.\n\nThe same Elo source compiled to JavaScript, Python, or SQL is benign, so the bug also breaks the cross-target portability guarantee the rest of the compiler honours.\n\n### Vulnerable sinks\n\nTwo sinks exist in `src/compilers/ruby.ts` at commit `749af7b`:\n\n1. **String-literal emitter \u2014 lines 180\u2013184.**\n   The emitter escapes backslash, double quote, and control characters, but not `#`. Any `#{...}` sequence in an Elo string literal flows straight into the emitted Ruby source.\n\n2. **Labelled subtype-constraint branch \u2014 lines 405\u2013412.**\n   The error message passed to `p_fail(p, \"...\")` is wrapped in a Ruby double-quoted string with no escape pass at all. Because the parser at `src/parser.ts:1339\u20131348` accepts single-quoted Elo strings as constraint labels, a label such as `\u0027#{` `` `id` `` `}\u0027` flows unchanged into the emitted Ruby.\n\n### Attack surfaces\n\nAll three surfaces share the same root cause (the missing `#` escape in the Ruby double-quoted-string emission path) and are addressed by the same patch:\n\n| # | Entry point | Threat model |\n|---|---|---|\n| 1 | `/try` playground on elo-lang.org (Ruby target) | 0-click server-side RCE against elo-lang.org infrastructure from a shared PoC URL |\n| 2 | `bin/eloc -t ruby -x` on a CI or DevOps host | RCE on any host that passes Elo strings through the CLI |\n| 3 | Library API \u2014 `defaultFormats.elo.parse()` with a Ruby emission step | Ruby RCE for any downstream integrator using Elo as a rule engine, configuration DSL, or form builder DSL (the primary documented use cases) |\n\n### Proof of Concept\n\n**String-literal sink:**\n\n```\n# poc.elo\n\u0027RES=#{`id 2\u003e\u00261`}\u0027\n```\n\n```\n$ eloc -t ruby -x poc.elo\n# emitted Ruby contains:  \"RES=#{`id 2\u003e\u00261`}\"\n# running it returns the output of id(1).\n```\n\nReplacing `` `id 2\u003e\u00261` `` with `` `touch /tmp/pwned` `` produces a trivially observable filesystem side effect.\n\n**Subtype-constraint label sink:**\n\n```\n# poc-subtype.elo\nlet X = Int(i | \u0027#{`touch /tmp/pwned2`}\u0027: i \u003e 0) in X(-1)\n```\n\nThe predicate fails for `-1`, causing `p_fail(p, \"#{...}\")` to interpolate and execute the backtick command at the Ruby layer.\n\nBoth flows are automated in the reproduction script submitted with the original report and were verified against Ruby 3.x.\n\n### Root cause\n\nThe Ruby-emitter\u0027s double-quoted-string escape routine omitted `#` from its character set, and one emission path (the subtype-constraint label branch) performed no escape pass at all.",
  "id": "GHSA-7w38-ggh5-v542",
  "modified": "2026-11-03T17:51:30Z",
  "published": "2026-11-03T17:51:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/enspirit/elo/security/advisories/GHSA-7w38-ggh5-v542"
    },
    {
      "type": "WEB",
      "url": "https://github.com/enspirit/elo/commit/51b869963fbe445c18328714a7ccfbd76b21ba38"
    },
    {
      "type": "WEB",
      "url": "https://github.com/enspirit/elo/commit/d2a89a8db76a244eb740687524a7e8529b7554b5"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/enspirit/elo"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Elo Ruby backend has Remote Code Execution via unescaped `#{...}` interpolation in emitted string literals and subtype-constraint labels"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.