Bug 1263741

Summary: CVE-2015-5233 - reports show/destroy not restricted by host authorization
Product: Red Hat Satellite Reporter: Bryan Kearney <bkearney>
Component: SecurityAssignee: Daniel Lobato Garcia <dlobatog>
Status: CLOSED ERRATA QA Contact: Kedar Bidarkar <kbidarka>
Severity: high Docs Contact:
Priority: unspecified    
Version: 6.0.4CC: bbuckingham, chpeters, cwelton, dlobatog, jcallaha, mmccune
Target Milestone: UnspecifiedKeywords: Triaged
Target Release: Unused   
Hardware: Unspecified   
OS: Unspecified   
URL: http://projects.theforeman.org/issues/11579
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2015-12-15 09:19:41 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1262443    

Description Bryan Kearney 2015-09-16 14:52:03 UTC
Foreman 1.5.0 or higher are vulnerable to an authorization issue that allows users to view and delete reports for hosts that they don't have access to.

Reports (from tools such as Puppet) are stored in Foreman and associated to the host they came from.  Users can be granted permissions to view and/or destroy reports, and also separate permissions to view certain hosts.  The UI and API only list reports where the user has permission to view both reports and the host it was from.

The security issue is that both the show and destroy actions for viewing and deleting individual reports do not limit access to the hosts that the user has permission to view.  A user with permission to view or destroy reports can do so for any host if they know the ID, or can easily view the last report for a given host.

Thanks to Daniel Lobato Garcia of Red Hat for reporting this to foreman-security.

Comment 1 Bryan Kearney 2015-09-16 14:52:04 UTC
Created from redmine issue http://projects.theforeman.org/issues/11579

Comment 2 Bryan Kearney 2015-09-16 14:52:06 UTC
Upstream bug assigned to dlobatog

Comment 3 Bryan Kearney 2015-09-16 14:58:26 UTC
Moving to POST since upstream bug http://projects.theforeman.org/issues/11579 has been closed
-------------
Daniel Lobato Garcia
Applied in changeset commit:293036dfa71ae70624663647f1ef70798bf53d3e.

Comment 6 Corey Welton 2015-11-04 21:03:07 UTC
We are not sure if we have fully tested this and/or if we are seeing what we expect.

If we create a role with view_ and destroy_report roles, and 'where host != $hostname', we can still view report for said host, albeit it doesn't appear we can delete reports.

If we create a role with view_ and destroy_report roles, and put user in a different group than the group which owns $hostname, and try to visit the URL which would take user to role, we get content headers for a table but no content.

Ergo: 

* for the first scenario, explicitly excluding a host we manage to still view reports, albeit apparently not delete them.

* for the second scenario, it does appear that we're unable to view (or delete) reports where user is blocked, on an organizational level, from otherwise interacting with a host/its reports.


Are we on the right track here?  Have we tested this and found remaining deficiencies, or are we not testing the right thing(s) in either scenario?

Comment 8 Daniel Lobato Garcia 2015-11-09 08:10:03 UTC
Hi Corey, I think you're testing a different thing. The original bug had to do with two sets of permissions, 'view_hosts' and 'view_reports', with "unlimited"
 filter. If you can view a host through 'view_hosts' and you have the `view_reports` permission you should be able to see reports. If you have 'view_reports' but 'view_hosts' only enabled for certain hosts, due to a filter, organization, etc.. you should only view reports for hosts in the limited filter. This last point is what the CVE is about.

> If we create a role with view_ and destroy_report roles, and 'where host !=
> $hostname', we can still view report for said host, albeit it doesn't appear
> we can delete reports.

Sounds like a bug, mind to submit it to foreman-security. It's a different bug that has to do with the Report filters, the original one was about Host filters.

> 
> If we create a role with view_ and destroy_report roles, and put user in a
> different group than the group which owns $hostname, and try to visit the
> URL which would take user to role, we get content headers for a table but no
> content.

Different bug, file it through http://projects.theforeman.org/issues/ , sounds like an usability problem, not a security issue

> Are we on the right track here?  Have we tested this and found remaining
> deficiencies, or are we not testing the right thing(s) in either scenario?

Please see this comment http://projects.theforeman.org/issues/11579#note-1 to understand what to test. Just create a 'view_reports' permission, then a 'view_hosts' permission with limited access, and try to break it as mentioned in the bug (providing an ID, checking the last report..)

Comment 13 Kedar Bidarkar 2015-11-12 10:16:42 UTC
I had earlier tried it with 'hostgroup' itself, but while updating , may be just updated without the actual exact name of the filter and used 'host_groups'.

I tried filtering using many other entities, but face the same issue as mentioned earlier.

when filtered with content_source filter , I get the below issue.

2015-11-12 04:44:22 [I] Processing by ReportsController#index as HTML
2015-11-12 04:44:22 [I]   Parameters: {"search"=>"eventful = true"}
2015-11-12 04:44:22 [I]   Rendered reports/_list.html.erb (9.6ms)
2015-11-12 04:44:22 [I]   Rendered reports/index.html.erb within layouts/application (10.3ms)
2015-11-12 04:44:22 [W] Operation FAILED: PGError: ERROR:  missing FROM-clause entry for table "smart_proxies"
LINE 1: ...2) AND "hosts"."type" IN ('Host::Managed') AND ((("smart_pro...
                                                             ^
: SELECT  "reports"."id" AS t0_r0, "reports"."host_id" AS t0_r1, "reports"."reported_at" AS t0_r2, "reports"."created_at" AS t0_r3, "reports"."updated_at" AS t0_r4, "reports"."status" AS t0_r5, "reports"."metrics" AS t0_r6, "hosts"."id" AS t1_r0, "hosts"."name" AS t1_r1, "hosts"."ip" AS t1_r2, "hosts"."last_compile" AS t1_r3, "hosts"."last_freshcheck" AS t1_r4, "hosts"."last_report" AS t1_r5, "hosts"."updated_at" AS t1_r6, "hosts"."source_file_id" AS t1_r7, "hosts"."created_at" AS t1_r8, "hosts"."mac" AS t1_r9, "hosts"."root_pass" AS t1_r10, "hosts"."serial" AS t1_r11, "hosts"."puppet_status" AS t1_r12, "hosts"."domain_id" AS t1_r13, "hosts"."architecture_id" AS t1_r14, "hosts"."operatingsystem_id" AS t1_r15, "hosts"."environment_id" AS t1_r16, "hosts"."subnet_id" AS t1_r17, "hosts"."ptable_id" AS t1_r18, "hosts"."medium_id" AS t1_r19, "hosts"."build" AS t1_r20, "hosts"."comment" AS t1_r21, "hosts"."disk" AS t1_r22, "hosts"."installed_at" AS t1_r23, "hosts"."model_id" AS t1_r24, "hosts"."hostgroup_id" AS t1_r25, "hosts"."owner_id" AS t1_r26, "hosts"."owner_type" AS t1_r27, "hosts"."enabled" AS t1_r28, "hosts"."puppet_ca_proxy_id" AS t1_r29, "hosts"."managed" AS t1_r30, "hosts"."use_image" AS t1_r31, "hosts"."image_file" AS t1_r32, "hosts"."uuid" AS t1_r33, "hosts"."compute_resource_id" AS t1_r34, "hosts"."puppet_proxy_id" AS t1_r35, "hosts"."certname" AS t1_r36, "hosts"."image_id" AS t1_r37, "hosts"."organization_id" AS t1_r38, "hosts"."location_id" AS t1_r39, "hosts"."type" AS t1_r40, "hosts"."otp" AS t1_r41, "hosts"."realm_id" AS t1_r42, "hosts"."compute_profile_id" AS t1_r43, "hosts"."provision_method" AS t1_r44, "hosts"."content_source_id" AS t1_r45, "hosts"."primary_interface" AS t1_r46, "hosts"."grub_pass" AS t1_r47, "hosts"."discovery_rule_id" AS t1_r48, "hosts"."content_view_id" AS t1_r49, "hosts"."lifecycle_environment_id" AS t1_r50 FROM "reports" LEFT OUTER JOIN "hosts" ON "hosts"."id" = "reports"."host_id" AND "hosts"."type" IN ('Host::Managed') WHERE "reports"."host_id" IN (SELECT "hosts"."id" FROM "hosts"  WHERE "hosts"."organization_id" IN (1) AND "hosts"."location_id" IN (2) AND "hosts"."type" IN ('Host::Managed') AND ((("smart_proxies"."name" = 'xyz-sat6.redhat.com') AND ("hosts"."organization_id" = '1') AND ("hosts"."location_id" = '2'))) ORDER BY "hosts"."name" ASC NULLS FIRST ) AND (((("reports"."status" >> 0 & 16777215) > 0))) ORDER BY "reports"."reported_at" DESC NULLS LAST  LIMIT 20 OFFSET 0
2015-11-12 04:44:22 [I]   Rendered common/500.html.erb within layouts/application (6.4ms)
2015-11-12 04:44:22 [I]   Rendered layouts/base.html.erb (1.5ms)
2015-11-12 04:44:22 [I] Completed 500 Internal Server Error in 264ms (Views: 10.7ms | ActiveRecord: 1.2ms)

Comment 20 Kedar Bidarkar 2015-12-01 11:30:37 UTC
Tested with 'view_hosts' only enabled for certain hosts and the limited filter with which tested for are:

a) host-group
b) organization
c) domain
d) arch
e) environment

 only view reports for these hosts were visible.

VERIFIED with Satellite-6.1.0-RHEL-6-20151125.0

Comment 22 errata-xmlrpc 2015-12-15 09:19:41 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHSA-2015:2622