Coordinated disclosure of XML roundtrip vulnerabilities in Go’s standard library
Anyone have examples of XML that can be mutated? My guess is that it wouldn&#x27;t take much.<p>I expect that a similar problem will be found in many other libraries, if the XML was publicized. XML namespaces made a critical... &quot;mistake&quot; is probably too strong, but &quot;design choice that deviated too far from people&#x27;s mental model&quot; is about right... that has prevented them from being anywhere near as useful or safe as they could be. In an XML document using XML namespaces, &quot;ns1:tagname&quot; may not equal &quot;ns1:tagname&quot;, and &quot;ns1:tagname&quot; can be equal to &quot;ns2:tagname&quot;. This breaks people&#x27;s mental models of how XML works, and correspondingly, breaks people&#x27;s code that manipulates XML.<p>(I actually used the Go XML library as an SVG validator in the ~1.8 timeframe and had to fork it to fix namespaces well enough to serve in that role. I didn&#x27;t know about how to exploit it in a specific XML protocol but I&#x27;ve know about the issues for a while. &quot;Why didn&#x27;t you upstream it then?&quot; Well, as this security bulletin implies, the data structures in encoding&#x2F;xml are fundamentally wrong for namespaced XML to be round-tripped and there is no backwards-compatible solution to the problem, so it was obvious to me without even trying that it would be rejected. This has also been discussed on a number of tickets subsequently over the years, so that XML namespace handling is weak in the standard library is not news to the Go developers. Note also that it&#x27;s &quot;round-tripping&quot; that is the problem; if you parse &amp; consume you can write correct code, it&#x27;s the sending it back out that can be problematic.)<p>Namespaces fundamentally rewrite the nature of XML tag and attribute names. No longer are they just strings; now they are tuples of the form (namespace URL, tag name)... and namespace URL is <i>NOT</i> the prefix that shows up before the colon! The prefix is an abbreviation of an earlier tag declaration. So in the XML<p><pre><code>    &lt;tag xmlns=&quot;https:&#x2F;&#x2F;sample.com&#x2F;1&quot; xmlns:example1=&quot;https:&#x2F;&#x2F;blah.org&#x2F;1&quot;&gt;
      &lt;example1:tag xmlns:example2=&quot;https:&#x2F;&#x2F;blah.org&#x2F;2&quot;&gt;
        &lt;example2:tag xmlns:example1=&quot;https:&#x2F;&#x2F;anewsite.com&#x2F;xmlns&quot;&gt;
          &lt;example1:tag &#x2F;&gt;
        &lt;&#x2F;example2:tag&gt;
      &lt;&#x2F;example1:tag&gt;
    &lt;&#x2F;tag&gt;
</code></pre>
not a SINGLE ONE of those &quot;tag&quot;s is the same! They are, respectively, actually (<a href="https:&#x2F;&#x2F;sample.com&#x2F;1" rel="nofollow">https:&#x2F;&#x2F;sample.com&#x2F;1</a>, tag), (<a href="https:&#x2F;&#x2F;blah.org&#x2F;1" rel="nofollow">https:&#x2F;&#x2F;blah.org&#x2F;1</a>, tag), (<a href="https:&#x2F;&#x2F;blah.org&#x2F;2" rel="nofollow">https:&#x2F;&#x2F;blah.org&#x2F;2</a>, tag), and (<a href="https:&#x2F;&#x2F;anewsite.com&#x2F;xmlns" rel="nofollow">https:&#x2F;&#x2F;anewsite.com&#x2F;xmlns</a>, tag). There&#x27;s a ton of code, and indeed, even quite a few standards, that will get that wrong. (Note the redefinition of &#x27;example1&#x27; in there; that is perfectly legal.) Even more excitingly,<p><pre><code>    &lt;tag xmlns=&quot;https:&#x2F;&#x2F;sample.com&#x2F;1&quot; xmlns:example1=&quot;https:&#x2F;&#x2F;sample.com&#x2F;1&quot;&gt;
      &lt;example1:tag&#x2F;&gt;
      &lt;example2:tag xmlns:example2=&quot;https:&#x2F;&#x2F;sample.com&#x2F;1&quot; &#x2F;&gt;
    &lt;&#x2F;tag&gt;
</code></pre>
<i>ARE</i> all the exact tag and should be treated as such, despite the different &quot;tag names&quot; appearing.<p>Reserializing these can be exciting, because A: Your XML library, in principle, ought to be presenting you the (XMLNS, tagname) tuple with the abbreviation stripped away, to discourage you from paying too much attention to the abbreviation but B: humans in general and a lot of code expect the namespace abbreviations to stay the same in a round trip, and may even standardize on what the abbreviations should be. There&#x27;s a LOT of code out there in the world looking for &quot;&#x27;p&#x27; or &#x27;xhtml:p&#x27;&quot; as the tag name and not (&quot;<a href="http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;xhtml" rel="nofollow">http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;xhtml</a>&quot;, &quot;p&quot;).<p>In general, to maintain roundtrip equality, you have to either A: maintain a table of the abbreviations you see, when they were introduced, and also which was used or B: just use the (XMLNS, tagname) and ensure that while outputing that the relevant namespaces have always been declared. Generally for me I go for option B as it&#x27;s generally easier to get correct and I pair it with a table of the most common namespaces for what I&#x27;m working in, so that, for example, XHTML gets a hard-coded &quot;xhtml:&quot; prefix. It is very easy if you try to implement A to screw it up in a way that can corrupt the namespaces on some input.<p>(Option B has its own pathologies. Consider:<p><pre><code>    &lt;tag xmlns:sample=&quot;https:&#x2F;&#x2F;example.com&#x2F;1&quot;&gt;
      &lt;sample:tag1 &#x2F;&gt;
      &lt;sample:tag2 &#x2F;&gt;
    &lt;&#x2F;tag&gt;
</code></pre>
It&#x27;s really easy to write code that will drop the xmlns specification on all of the children of &quot;tag&quot;, since it didn&#x27;t use it there, and if your code throws away where the XMLNS was declared and just looks to whether the NS is currently declared, it&#x27;ll see a new declaration of the &quot;sample&quot; namespace on every usage. Technically correct if the downstream code handles namespaces correctly (big if!), but visually unappealing.)<p>Not defending Go here, except inasmuch as it&#x27;s such a common error to make that I have a hard time naming libraries and standards that get namespaces <i>completely</i> correct, for as simple as they are in principle. (I think SVG and XHTML have it right. XMPP is very, very close, but still has a few places where the &quot;stream&quot; tag is placed in different namespaces and you&#x27;re just supposed to know to handle it the same in all the namespaces it appears it... which most people do only because it doesn&#x27;t occur to them that technically these are separate tags, so it all kinda works out in the end.... libxml2 is correct but I&#x27;ve seen a lot of things that build on top of it and they almost all screw up namespaces.)