Problem/Motivation
This has been publicly disclosed, hence we are fixing in public instead of in the private security team tracker.
reported by Fernando_Arnaboldi
An attacker may force an admin to check for updates due to a cross site request forgery vulnerability on the update functionality
- Drupal 6: affected
http://mysite/?q=admin/reports/updates/check
- Drupal 7: affected
?q=admin/reports/updates/check
?q=admin/reports/updates
Proposed resolution
Add CSRF token protection to those menu routes
Remaining tasks
write patch
User interface changes
n/a
API changes
n/a
Data model changes
n/a
Comment | File | Size | Author |
---|---|---|---|
#37 | 2646328-2-37.patch | 5.39 KB | alexpott |
#37 | 32-37-interdiff.txt | 1.81 KB | alexpott |
#32 | interdiff.txt | 3.1 KB | Wim Leers |
#32 | 2646328-32.patch | 8.02 KB | Wim Leers |
#31 | twig_args_bubble.patch | 2.24 KB | Wim Leers |
Comments
Comment #2
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThis affects Drupal 8 too (though without the "?q=" in the URL).
Comment #3
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedComment #4
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedIt should be as simple as this (plus some test fixes to change code that visits the URL directly to click the appropriate link instead).
However, when I tested this manually it didn't work, even after a cache clear. The token is added to the link, but when you click on it the token is treated as invalid and you get "access denied". I debugged a bit but then stopped for now. Looking in the issue queue perhaps it's due to #2630920: _csrf_token is broken due to cacheability metadata integration, results in rendered links without valid CSRF tokens (or more likely one of the issues linked therein).
Comment #5
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedAlthough the impact of this CSRF is low, I think it still is a security improvement - tagging accordingly.
Comment #7
aaronott CreditAttribution: aaronott commentedPlease forgive me, I'm not sure the process when backport patches are required. I just read through https://www.drupal.org/core/backport-policy and am still a bit confused in this situation.
For this issue it looks like a simple change to the update.routing.yml file obviously not present in D7. I have a patch that I would like to submit for D7. Please let me know if I need a separate D7 issue for this or not.
Thanks,
Comment #8
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commented@aaronott - thanks for working on it! I think you should go ahead and post your patch here in this issue. Because of the backport policy we make sure to focus on fixing the issue in Drupal 8 first (and then Drupal 7 afterwards), but it still will be useful to have a Drupal 7 patch now, especially if you already wrote it.
Also if it turns out the proper fix for this in Drupal 8 is blocked on the other issue I linked to (which it might be) then we may need an interim solution for Drupal 8 which would probably be similar to the Drupal 7 one.
Comment #9
aaronott CreditAttribution: aaronott commentedThanks @David_Rothstein, Here is the patch I mentioned for D7.
Comment #10
Ayesh CreditAttribution: Ayesh commentedAttaching a patch for 8.x. We add the CSRF requirement in the router, so all links generated should automatically have the token.
Comment #12
Ayesh CreditAttribution: Ayesh commentedComment #13
Ayesh CreditAttribution: Ayesh commentedI'm sorry I went ahead and even submitted the patch, and then realized there is an exactly same patch already. I could reproduce the same access denied error.
It turns out Url::fromRoute() gives the proper URL. _update_no_data() functions run-cron link is broken as well, so it is definitely the other issue David mentioned.
Comment #15
mgiffordComment #16
aaronott CreditAttribution: aaronott commentedSince the fix for D7 is unrelated to the fix for D8, should I create a separate ticket for the D7 issue?
Comment #17
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedIn what way are they unrelated?
Given that #2630920: _csrf_token is broken due to cacheability metadata integration, results in rendered links without valid CSRF tokens doesn't look like it will be fixed soon, and that it appears to block doing this issue in the "proper" way for Drupal 8, I think what we should do here is what I suggested in #8 - make the Drupal 8 patch use the old-style CSRF protection method (in other words, make it do manual token checking just like the Drupal 7 patch), with a @todo to replace that with the correct method once the linked issue is fixed.
That way we can still fix it in both Drupal 7 and 8 without it getting blocked on other issues.
Comment #18
alexpottWe can just fix the preprocess so that the metadata bubbles properly and the
_csrf_token
works. #2630920: _csrf_token is broken due to cacheability metadata integration, results in rendered links without valid CSRF tokens wouldn't fix this anyway.Comment #20
alexpottSo the issue with going to
admin/reports/updates
to click on the tokenised link is that it'll refresh the update information because we callupdate_get_available(TRUE)
inUpdateController::updateStatus()
. This changes some tests. So we can fix it by going to the url at the right time in the test. However this means thatadmin/reports/updates
is also a path for a brute force attack described in the summary.Comment #21
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedNice, I guess it was the "or more likely one of the issues linked therein" that I mentioned in #4, then. Switching this to point to the correct issue (I think?).
Based on a quick glance the patch looks like a reasonable workaround to me.
My recollection is that update_get_available(TRUE) does a lot less than the "Check manually" link though. I think the former only fetches project data if it's not in the local cache, whereas the latter always fetches the project data no matter what.
Overall the brute force attack here is quite weak either way though.
Comment #22
alexpott@David_Rothstein yeah I agree that we probably shouldn't worry about admin/reports/updates as this just processes a queue.
Comment #23
alexpottLet's make it more explicit what we're doing to make the tests pass.
Comment #25
dawehnerIdeally we would enforce a router rebuild, as its required to make the path secure. I don't remember exactly whether update.php does one, when there are no pending updates.
Its really nice IMHO that core makes it really easy to add CSRF protection to specific pages.
Comment #26
alexpottOh well just going back to the approach in #20 for
\Drupal\update\Tests\UpdateContribTest::testUpdateBrokenFetchURL()
.Comment #27
alexpottDiscussed with @catch and @xjm. We agreed this is a major bug as an admin could be set an email with image links in to do such a check.
Comment #28
alexpottFinally got round to adding the update.
Comment #29
alexpottCan actually just have an empty update otherwise we'll rebuild the routes twice :(
Comment #30
Wim LeersComment #31
Wim LeersThis should not be necessary. The code original code should work.
This is indeed related to #2575519: Twig template variables containing result of Drupal::url() and Drupal:l:() don't bubble up their cacheability and attachment metadata (e.g. token placeholder).
Rather than making this change, we should fix the root cause.
ThemeManager
does this:I.e. we allow preprocess functions to explicitly set
#attached
and#cache
. In this case, our preprocess function is not doing that. Instead, it is assigning a value to one of the variables that itself contains cacheability metadata and attachments!So, where these variables are being used, we must update Twig to do the necessary bubbling, just when those variables are actually printed. The attached patch does that.
Comment #32
Wim LeersAnd here is it integrated with the patch in this issue.
Comment #33
dawehnerNote: We have the renderer already. I'm also wondering whether a dedicated method which just allows to add bubbleable metadata would make sense.
Comment #34
Wim LeersI don't understand what you mean. You dislike the protected helper method I added?
Comment #35
dawehnerSomething like
$renderer->addBubbleableMetadata
, but yeah nevermind about it.Comment #37
alexpottRerolled now #2575519: Twig template variables containing result of Drupal::url() and Drupal:l:() don't bubble up their cacheability and attachment metadata (e.g. token placeholder) landed.
Comment #38
Wim LeersLooks perfect!
Supernit: missing leading slash.
Comment #42
catchCommitted/pushed to 8.2.x and cherry-picked to 8.1.x. Thanks!
Opened the 7.x backport issue here: #2731187: CSRF in update module manual check links.