Page MenuHomePhabricator

Security: CSS positioning can be used to break out of the content area
Open, HighPublic

Assigned To
None
Authored By
bzimport
Jul 30 2012, 3:15 PM
Referenced Files
F35735298: image.png
Nov 9 2022, 9:41 PM
F35735243: image.png
Nov 9 2022, 9:27 PM
F35735241: image.png
Nov 9 2022, 9:27 PM
F35735239: image.png
Nov 9 2022, 9:27 PM
F35735237: image.png
Nov 9 2022, 9:27 PM
F35735235: image.png
Nov 9 2022, 9:27 PM
F35735233: image.png
Nov 9 2022, 9:27 PM
F35735231: image.png
Nov 9 2022, 9:27 PM

Description

The position CSS attribute is allowed in wikitext and TemplateStyles CSS; it can be used to position parts of the user-generated content in places which are normally software-generated, e.g. on top of the sidebar menu or the search box. (Depending on the skin, z-index might or might not be also needed to achieve this.) A similar effect can be achieved with negative margins as well.

This is occasionally used for vandalism, e.g. flipping the contents of the page or obscuring everything using absolute or fixed positioning. This is confusing for patrollers as it also affects difflinks, but still relatively easy to revert using the undo/rollback links in recent changes / page history / etc.
In theory it could also be used for more dangerous things, by overlaying transparent links on top of clickable areas of the interface area it's possible to hijack clicks.
CVE-2012-5392 has been reserved for this.

On the other hand, positioning is a fundamental part of anything with a nontrivial amount of formatting, and is regularly used in infoboxes, amboxes and similar templates. Even the ability to break out of the content area is (ab)used for displaying various indicators on top of the page (such as a featured star, or the coordinates of the article subject). Page status indicators have been provided as a replacement for this, but the old method is still being used, so fixing this issue is blocked on finishing that migration. (See T135802#2332298 for some problems communities have with using indicators.) They are also used decoratively on user pages (e.g. to modify the site logo); breaking that probably would not be the end of the world.

Some potential solutions that were dicussed in the comments:

  • Make the content are a stacking context (done in df10f03a7a28) and make sure everything else has a higher z-index (and non-transparent background, although positioning content behind a transparent interface block is harder to abuse). This would have to be done for every skin.
  • Make the content area overflow: hidden (and either ban position: fixed, or make the content area a 3D rendering context). See T40848#1913661 for more details.
  • Use CSS containment. Not widely supported yet though.

Per T40848#1938655 the plan is to use overflow: hidden, first on a larger area so it's still possible to overlay the interface area where status indicators are, then on the content area once status indicator migration is finished. Status indicator blocks themselves would have to be secured (T135802).

Related Objects

StatusSubtypeAssignedTask
OpenNone
OpenNone
ResolvedNone
ResolvedNone
ResolvedNone
ResolvedNone
ResolvedNone
ResolvedTgr
ResolvedAnomie
Resolvedtstarling
Resolvedcoren
ResolvedAnomie
DeclinedBUG REPORTNone
ResolvedAnomie
ResolvedEsanders
ResolvedEsanders
Resolvedssastry
ResolvedAnomie
ResolvedCKoerner_WMF
Resolvedjhsoby
ResolvedTgr
DeclinedTgr
Resolvedcoren
ResolvedAnomie
ResolvedTgr
DeclinedNone
Resolvedssastry
ResolvedTgr
ResolvedTgr
ResolvedTgr
Resolved Deskana
ResolvedCKoerner_WMF
Resolved Whatamidoing-WMF
ResolvedTgr
ResolvedTgr
ResolvedTgr
ResolvedUrbanecm
ResolvedTgr
ResolvedTacsipacsi
ResolvedTgr
ResolvedCKoerner_WMF

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Restricted Application changed the visibility from "Custom Policy" to "Custom Policy". · View Herald TranscriptDec 7 2014, 8:27 PM
Restricted Application changed the edit policy from "Custom Policy" to "Custom Policy". · View Herald Transcript

The Vector skin has been moved into a separate repository and the element is now targeted as .mw-body-content instead of #bodyContent.

Part of task has been resolved with T76965.

Restricted Application changed the visibility from "Custom Policy" to "Custom Policy". · View Herald TranscriptDec 7 2014, 8:28 PM
Restricted Application changed the edit policy from "Custom Policy" to "Custom Policy". · View Herald Transcript
Krinkle changed the edit policy from "Custom Policy" to "Custom Policy".
Restricted Application changed the visibility from "Custom Policy" to "Custom Policy". · View Herald TranscriptDec 7 2014, 8:32 PM
Restricted Application changed the edit policy from "Custom Policy" to "Custom Policy". · View Herald Transcript
Restricted Application changed the visibility from "Custom Policy" to "Custom Policy". · View Herald TranscriptDec 10 2014, 8:39 PM
Restricted Application changed the edit policy from "Custom Policy" to "Custom Policy". · View Herald Transcript

Turns out applying overflow: hidden to .mw-body doesn't solve for fixed positioned content[1].

I looked at various techniques, including using backface-hidden which works in certain cases on certain browsers, but nothing appears to be reliable.

The only safe approach I can see is to combine z-indexing with obscuring content behind UI areas using positioned divs that fully block out the UI area and have opaque backgrounds[3].

[1] https://en.wikipedia.org/wiki/User_talk:Bababa67
[2] http://jsfiddle.net/w119kpwL/
[3] http://jsfiddle.net/Loezmm7y/1/

Giving content its own rendering context (e.g. using translateZ(0)) would also restrict position: fixed;. It's similar to "visual" security provided by iframes. Perhaps overflow: hidden; and translate can be used together.

Note that mw-indicators has been introduced in MediaWiki core and projects can now migrate templates such as Coordinates, Featured article etc. to using that instead. Which, to my knowledge, was the last remaining (valid) use case for allowing position: absolute; outside the content area. Which means, once migration period settles, we are free to apply overflow: hidden;. We may want to additionally add z-indexes in various places, but we should hide overflow no matter what.

Hiding overflow will also hide content when someone with a very wide screen resolution makes something that won't fit for people with lower resolutions. Horizontal scrollbars suck, yes, but is just cutting it off necessarily better?

Hiding overflow will also hide content when someone with a very wide screen resolution makes something that won't fit for people with lower resolutions. Horizontal scrollbars suck, yes, but is just cutting it off necessarily better?

This task is about elements placing themselves outside the content area to overlap interface elements. Capturing overflow doesn't mean it has to be inaccessible. overflow: auto; (e.g. scroll when overflow) would work just as well. The scrollbar would be on the content area instead of on the window. Unless scrolled to the bottom of the page the user wouldn't notice much difference in behaviour. Except that the excess content would be correctly displayed on a white background instead of against the outer the skin body (which is grey in Vector).

Example: https://codepen.io/Krinkle/debug/eNEwYo.

The only difference is that (on desktop) the sidebar would stay in view. On mobile the sidebar may be out of view depending on the viewport and how far away you scroll (virtual depth is applied). This is making its way to desktop browsers as well.

http://www.quirksmode.org/mobile/viewports.html
http://www.androidpolice.com/2015/01/09/chrome-beta-v40-new-zoom-behavior-makes-fixed-headers-scrollable-less-aggravating/

(documenting my email to security@, and Timo's writeup)

Last week Jerry Hoff from WhiteHat Security posted https://twitter.com/jerryhoff/status/617340137689755653?s=03.

The page the Twitter user in question was viewing is:
https://en.wikipedia.org/wiki/Thomas_Sowell

This page uses Template:Hlist in the infobox, which was changed briefly to include Lua Module "Dynkin" (edit made by an experienced user):
https://en.wikipedia.org/w/index.php?title=Template:Hlist&diff=prev&oldid=669920021

Which in turn was vandalised to include the red box (edit made by an anonymous user):
https://en.wikipedia.org/w/index.php?title=Module:Dynkin&diff=prev&oldid=669919700

Because https://gerrit.wikimedia.org/r/#/c/178086/ was deployed last December, on Vector

So all that happened is a big red box was displayed *behind* the interface menus.

For monobook, much of the interface was obscured. Fixing this issue on monobook seems unlikely.

Krinkle mocked up the interface if we also had overflow: hidden, and the result visually (imo) looks much less like a security issue and more like vandalism,

If we had done overflow-hidden, the page would've looked like this: http://i.imgur.com/TrPeYxG.png
Instead of the current: http://i.imgur.com/MFN8cBo.png

@Krinkle, can you post the style updates that you used to get that result? It looks as Trevor said, setting overflow:hidden isn't quite enough.

Krinkle mocked up the interface if we also had overflow: hidden, and the result visually (imo) looks much less like a security issue and more like vandalism,

If we had done overflow-hidden, the page would've looked like this: http://i.imgur.com/TrPeYxG.png
Instead of the current: http://i.imgur.com/MFN8cBo.png

@Krinkle, can you post the style updates that you used to get that result? It looks as Trevor said, setting overflow:hidden isn't quite enough.

It turns out that result was a lucky match between screen height and the exact code used by that piece of vandalism. It used width: 100%; height: 100%; which means in an overflow scenario with sufficient page content and a short window, it wouldn't extend far enough to peak out.

Ignoring position: fixed for a second (see T123811 for that), overflow: hidden is enough and addresses this issue entirely for both Vector, MonoBook and any other skin. The core patch will affect modern skins using the mediawiki.skinning base; other skins can adopt the same in their own CSS.

As for position: fixed. That can be reliably addressed by giving the content its own rendering context using transform: translateZ(0);. See http://jsfiddle.net/Loezmm7y/4/. However that impacts rendering performance. Supported in 90%+ of browsers per http://caniuse.com/#feat=transforms2d (not in IE<=8).

However I suggest we simply forbid position: fixed in the Sanitizer. I don't see any valid use case for it. Fixed position is bound to rendering context (= window by default) and can always peek out if allowed. The original issue here was absolute position and relative position.

In light of recent attacks, reviving this task.

First of all, the current state of things has changed over the past year. Some layer security patches have been applied as part of other security bugs.

Current state of Vector (using example code from T40848#425108):

Screen Shot 2016-01-03 at 12.19.31.png (1×2 px, 508 KB)

  • Logo: Safe, but transparent
  • Sidebar: Safe, but transparent
  • Personal: Safe, but transparent
  • Page tabs: Safe, but transparent
  • Search: Safe, but transparent
  • Sitenotice: Not safe
  • Page name: Not safe

"Safe" refers to the visibility and click-ability of the above listed components' contents. Not their container overall. Meaning, an attacker is not able to change the link target of any of our user interface links and cannot overlap e.g. the logo. However, as you can see, the space in between the sidebar links and page tabs etc is still vulnerable because things can be layered underneath. This is why the z-index approach is only effective as an attack, not as a defence.

Overflow-hidden is still the best way forward. However as mentioned, this is non-trivial because of established use cases from the community surrounding "hacks" that place icons outside the page content area (e.g. Geo coordinates, Featured article status, Spoken Wikipedia, etc.). This use case has been recognised by MediaWiki in 2014 in the MediaWiki 1.25 release cycle (mw:Help:Page status indicators). I don't know how well this has been adopted by now. I'd say we give communities a heads up through mailing lists, ambassadors and tech news and then go ahead with it.

Proposed change A:

.mw-body {
    overflow: hidden;
}

Impact:

Screen Shot 2016-01-03 at 12.19.40.png (1×2 px, 497 KB)

This has no bad side sides (compared to status quo) and protects transparency from all currently safe areas. It still leaves page title and site notice unsafe, however. This proposal exists because of the side effects and complications with the following proposals. I'd recommend this as a transitional change to cover most of our problem space while we work on the rest.

Proposed change B:

.mw-body-content {
    overflow: hidden;
}

Screen Shot 2016-01-03 at 12.45.13.png (1×2 px, 494 KB)

This makes all listed areas safe (including the previously unsafe site notice and page title). Side effects:

  • This would break any "top icons" and "indicators" that haven't migrated from absolute-position to using <indicator> yet. (Because change B shrinks the overflow area from mw-body to mw-body-content, meaning content can't overlay icons and images next to the page title. An otherwise desirable change.)
  • This breaks anti-margin collapse between contentSub and elements inside the text content. This element is unfortunately within mw-body-content in most skins. It is empty by default and enjoys CSS auto margin collapse and takes up no space. Setting overflow-hidden on its container (mw-body-content) means it is no longer able to collapse its margin with the page title. As such, creating a relatively large white gap between the page title and the start of the content.

Proposed change C:

#mw-content-text {
    position: relative;
    overflow: hidden;
}

Screen Shot 2016-01-03 at 12.55.10.png (1×2 px, 502 KB)

This makes all listed areas safe (including the previously unsafe site notice and page title). Side effects:

  • Breaks any indicators that haven't migrated yet (same as B).
  • This breaks margin collapse between page title (firstHeading) and page content . Even if we move out contentSub to be child of mw-body directly before mw-body-content, the same effect will still affect margin collapse between the first heading of the content area. We can probably avoid that by setting margin-bottom to zero for the page title.

I chatted with Krinkle about this bug yesterday, I think we have a clear roadmap for fixing it. We'll start by pushing method A to our sites, which is the least risk to our sites. It will affect some users who have updated their user pages to "customize" the wiki logo, so those users will notice as soon as we patch the cluster. Due to that, we'll need to push the fix to our sites, and release this as a mediawiki tarball update in quick succession. We'll time it so that we also alert ambassadors and tech-news, so that technical people in the movement all hear about this soon after we push it onto the site.

Once that is in place, we'll work towards change C as a public followup. Wikis will need some time to finish converting indicators, and we can test ways of fixing the margin collapse issue.

Before we can start this, we need to do some more testing with method A. Maybe we can use Tim's visual-diff to see what it would affect. Target of Early Feb might be realistic.

Krenair changed the edit policy from "Custom Policy" to "Custom Policy".
Krenair removed a subscriber: StudiesWorld.

Just to comment that position:fixed also affect extensions, such as Flow. I suggest sanitize it completely.

Bawolff subscribed.
Tgr added subscribers: bzimport, He7d3r.
Tgr subscribed.

Negative margins combined with z-index can be used for similar re-dressing attacks. Example:

<div style="margin-top: -340px; margin-left: 100px; color: red">Hello there</div>

It would probably be possible to sandbox away the content area (make sure the top skin, left skin and content are in different stacking contexts and the skin ones have positive z-index). There is no way for content in a stacking context to ever overlap content in sibling stacking contexts with higher z-index, even with position:fixed.

https://developer.mozilla.org/en-US/docs/Web/CSS/contain describes the CSS Containment Module 3, which is supported in Chrome 52 & up. This modules offers a general way to restrict the rendering of styled content to its parent element's dimensions: contain: content

Yeah, contain: content will perform better than overflow: hidden, but has imho a different semantic purpose and meaning. Either way, it's too new to cover us from a security perspective. Either way, this is blocked in migration of indicators to use https://www.mediawiki.org/wiki/Help:Page_status_indicators. Last I checked, many major wikis still aren't using it (much) yet.

See T40848#1938655 and T40848#1913661 for the next steps after that.

Tgr renamed this task from Security: CSS "position" and "z-index" property to hijack user clicks to Security: CSS positioning can be used to break out of the content area.Feb 27 2018, 11:02 PM
Tgr added a project: User-Tgr.
Tgr updated the task description. (Show Details)

cc @Jdrewniak who's working on a DOM restructuring in new Vector, and needs to know about this issue so he can make sure the fix for this is maintained

I don't know if the attacks today (https://www.businessinsider.com/wikipedia-pages-briefly-showed-pictures-of-swastikas-2021-8) took advantage of this security issue or not but it looks like it.

Yes, it used position:fixed (diff; now revdeleted). Although I doubt that limiting the image to the content area would have reduced the impact much.

This problem came up a few times recently, so I wanted to check the current status.

I am using the following as a test case, just putting it on a wikitext page:

<div style="background: grey; position: fixed; z-index: 999999; top: 0; left: 0; bottom: 0; right: 0;"></div>

Except for legacy Vector, all skins deployed on Wikimedia wikis (including new Vector) are vulnerable :

vectorvector-2022minervamonobooktimelesscolognebluemodern
image.png (2×3 px, 108 KB)
image.png (2×3 px, 43 KB)
image.png (2×3 px, 32 KB)
image.png (2×3 px, 47 KB)
image.png (2×3 px, 34 KB)
image.png (2×3 px, 24 KB)
image.png (2×3 px, 33 KB)
Okay! (although some nav elements are transparent)FailFailFailFailFail (deprecated skin)Fail (deprecated skin)

One of the places this came up was T322702, where the proposed patch would change the behavior of legacy Vector to this [see test case in my previous comment]:

BeforeAfter
image.png (2×3 px, 108 KB)
image.png (2×3 px, 51 KB)

I left a -1 on the patch referring to this task, and I think we should find a different way to solve that (an overlay as proposed by Sam), but I'm not sure if we're still treating this as a security issue. This problem has been known for years (looks like we missed the 10th anniversary in July…), without being fixed in any other skins, and it has become newly broken in new Vector. Do we still care? (I think we should, but I'd like to hear some third opinions.)

Also – this problem has been known for years, there are many references to it in public on the wikis and in other tasks, and the fact that it's secret makes it difficult to explain to people why positioning on Vector is the way it is, and easy to forget the purpose of these styles (as evidenced by new Vector losing the fix, and the proposed patch for legacy Vector). Could we make this task public?

sbassett edited subscribers, added: sbassett; removed: ggellerman, csteipp.

Given @matmarex' notes above and some additional discussion off-Phab, I'm comfortable making this bug public due to its age, its likely risk and the confounding factors described by @matmarex above.

sbassett changed the visibility from "Custom Policy" to "Public (No Login Required)".Nov 21 2022, 7:54 PM
sbassett changed the edit policy from "Custom Policy" to "All Users".