Page MenuHomePhabricator

CVE-2026-103044 upload.wikimedia.org should not serve image maps generated by EasyTimeline as application/xml
Closed, ResolvedPublicSecurity

Description

upload.wikimedia.org currently serves image maps (.map files) that are generated by EasyTimeline/ploticus as XML if they are valid XML and only contain one <area> tag (this is only an assumption based on my testing though as I'm not sure where/how the content type is decided). An example for this is https://upload.wikimedia.org/wikipedia/en/timeline/46e9y6gya1583ddr1q98xxe7uu2ahfd.map.
This is suboptimal because it's possible to insert double quotes through links and break out of the href attribute because neither ploticus nor EasyTimeline escape double quotes. Example: https://upload.wikimedia.org/wikipedia/en/timeline/m81ehl7ex9gc1lpyaqoou57vbag7a9u.map (not served as XML as it's not valid XML)
Timeline will error with Invalid image map generated by EasyTimeline (at least if the XML is not valid), but the files are still uploaded, and it's possible to access them by calculating the hash locally (which I did to access the example above).

Files served as application/xml can be used for XSS, e.g. by using <a:script xmlns:a="http://www.w3.org/1999/xhtml">alert(1)</a:script>.
The only reason why AFAICS it's not possible to abuse this for XSS right now is that it's not possible to add a space, tab or a newline into an attribute when breaking out of it, because the EasyTimeline script splits on newlines, replaces tabs with spaces and ploticus then replaces spaces with underscores in links. Those characters are required to separate two attributes in XML 1.0, and unlike in HTML, / is not supported.
However, I don't think we should rely on this behaviour, and I'm not familiar enough with the XML specification (or whatever is used to decide whether to serve the file as XML) to be able to tell whether there could be a bypass.

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript
sbassett changed the task status from Open to In Progress.Jun 9 2026, 4:45 PM
sbassett triaged this task as Medium priority.
sbassett moved this task from Incoming to In Progress on the Security-Team board.
sbassett edited projects, added: SecTeam-Processed; removed: Product Safety and Integrity.
sbassett subscribed.

@SomeRandomDeveloper - Are you aware of any legitimate reason we'd need to actually publicly serve these files? Either for Wikimedia or Miraheze?

@SomeRandomDeveloper - Are you aware of any legitimate reason we'd need to actually publicly serve these files? Either for Wikimedia or Miraheze?

I am not aware of any reason they need to be served. We need to store them though as they are used when displaying a cached timeline, and I'm not sure whether there's an easy way to store them without serving them. It probably depends on the file backend, and ideally we should find a solution that works with all types of file backends

I feel like the easy approach is to just deprecate the generation/output/serving of the .map files. Timeline defines image maps via proper <map> tags in html which has been browser-supported since... the mid-1990s. I just can't imagine the .map files are relevant anymore and I don't see how that should affect Wikimedia caching. Not to mention the fact that they conflict with far more modern/common JS source map files of the same extension that most developers would probably expect.

I feel like the easy approach is to just deprecate the generation/output/serving of the .map files. Timeline defines image maps via proper <map> tags in html which has been browser-supported since... the mid-1990s. I just can't imagine the .map files are relevant anymore and I don't see how that should affect Wikimedia caching. Not to mention the fact that they conflict with far more modern/common JS source map files of the same extension that most developers would probably expect.

The contents of the HTML <map> tag for a timeline are generated by retrieving the .map file and cleaning up the XML, so unfortunately we will still need to generate it unless we decide to store the data somewhere else.

I think you could tell swift a different MIME type when storing these files.

I think you could tell swift a different MIME type when storing these files.

Yeah, I think storing them as text/plain would probably guarantee some better safety here? Like so:

$op = [
    'op'  => 'store',
    'src' => "{$factoryDirectory}/file.{$ext}",
    'dst' => "{$pathPrefix}.{$ext}",
];
if ( $ext === 'map' ) {
    $op['headers'] = [ 'Content-Type' => 'text/plain; charset=utf-8' ];
}
$ops[] = $op;

within the timeline file storage loop. It looks like this could also be set via $opts in doQuickOperations(), but that doesn't seem preferred from my read of the docblock? Until we refactor timeline to discard the image map files after they're used or generate that source some other way, this is probably the simplest approach.

Formalized my suggestion above into a patch:


I feel like this could probably just go through gerrit, tbh. It also only satisfies the condition where media/file backends (like Swift) support writing a content-type for a given asset. For plain installations of MediaWiki, this would still likely need to be managed at the web server layer.

Formalized my suggestion above into a patch:


I feel like this could probably just go through gerrit, tbh. It also only satisfies the condition where media/file backends (like Swift) support writing a content-type for a given asset. For plain installations of MediaWiki, this would still likely need to be managed at the web server layer.

Deployed

This seems to be working in production when I recreate the example at https://test.wikipedia.org/wiki/User:SBassett_(WMF)/Sandbox/Test. The image map functionality still appears to work with the PNG that gets included on User:SBassett_(WMF)/Sandbox/Test. And the map functionality also still works for the raw SVG: https://upload.wikimedia.org/wikipedia/test/timeline/p73kykc82kwrqwwg0aqd2ti98ypdmdx.svg. And the map file is being served as plain text: https://upload.wikimedia.org/wikipedia/test/timeline/p73kykc82kwrqwwg0aqd2ti98ypdmdx.map. The big caveat is that this is a point-in-time-forward fix, in that it does not retroactively fix any potentially problematic existing map files.

Catrope renamed this task from upload.wikimedia.org should not serve image maps generated by EasyTimeline as application/xml to CVE-2026-103044 upload.wikimedia.org should not serve image maps generated by EasyTimeline as application/xml.Tue, Sep 29, 10:47 PM
sbassett claimed this task.
sbassett removed a project: Patch-For-Review.
sbassett changed the visibility from "Custom Policy" to "Public (No Login Required)".
sbassett changed the edit policy from "Custom Policy" to "All Users".
sbassett changed Risk Rating from N/A to Medium.