unauthorized container GET returns 404 when account not found

Bug #1415957 reported by Alistair Coles
8
This bug affects 1 person
Affects Status Importance Assigned to Milestone
OpenStack Object Storage (swift)
Fix Released
Undecided
Alistair Coles
OpenStack Security Advisory
Won't Fix
Undecided
Unassigned

Bug Description

A GET on a swift container will return 404 if the account is not found (e.g. not created) regardless of request authorization. If the request is unauthorized or unauthenticated then the response should be 403 or 401.

Steps to reproduce the behavior are shown below (same has been observed with keystoneauth). The root cause is that the proxy container controller returns 404 as soon as finding no account, rather than delegating response to the auth middleware via a call to swift.authorize.

I have marked this as a security vulnerability because although no user data is exposed, it does reveal user's (non)activity, or that a particular project ID is not in use. I'm not sure if others would see that as a 'vulnerability' but I'm erring on side of caution and I will defer to the security team to make that call.

Proposed fix attached to this bug report. Note that a functional test is not appropriate because you need to be sure that the account does not exist to usefully test, and we can't be sure of that on a cluster under test.

Example:

A GET (or HEAD) on a container in a non-existent account will return 404.

swift@saio-1:~/swift$ git log --oneline -n 1
91d04ce Merge "Make ThreadPools deallocatable."

swift@saio-1:~/swift$ ../bin/resetswift
<snip>

swift@saio-1:~/swift$ swift-init restart main
<snip>

# using tempauth...

swift@saio-1:~/swift$ curl http://localhost:8080/info
{"formpost": {}, "container_quotas": {}, "tempauth": {"account_acls": true}, "tempurl": {"methods": ["GET", "HEAD", "PUT", "POST", "DELETE"]}, "ratelimit": {"container_ratelimits": []}, "slo": {"max_manifest_segments": 1000, "min_segment_size": 4, "max_manifest_size": 2097152}, "account_quotas": {}, "swift": {"max_file_size": 5368709122, "account_listing_limit": 10000, "account_autocreate": true, "max_meta_count": 90, "max_meta_value_length": 256, "container_listing_limit": 10000, "max_meta_overall_size": 4096, "version": "2.2.1.18.g1f1cdce", "max_meta_name_length": 128, "max_header_size": 8192, "policies": [{"default": true, "name": "Policy-0"}, {"name": "partpow15"}], "max_object_name_length": 1024, "max_account_name_length": 256, "strict_cors_mode": true, "allow_account_management": false, "max_container_name_length": 256}}swift@saio-1:~/swift$

# get tokens for two accounts...

swift@saio-1:~/swift$ curl http://localhost:8080/auth/v1.0 -H 'X-Auth-User: test:tester' -H 'X-Auth-Key: testing' -i
HTTP/1.1 200 OK
X-Storage-Url: http://localhost:8080/v1/AUTH_test
X-Auth-Token: AUTH_tkf1fef50ff23d484ea9fd9493b675fc38
Content-Type: text/html; charset=UTF-8
X-Storage-Token: AUTH_tkf1fef50ff23d484ea9fd9493b675fc38
Content-Length: 0
X-Trans-Id: txb488f6de9b514879a2f96-0054c26a5a
Date: Fri, 23 Jan 2015 15:35:54 GMT

swift@saio-1:~/swift$ curl http://localhost:8080/auth/v1.0 -H 'X-Auth-User: test2:tester2' -H 'X-Auth-Key: testing2' -i
HTTP/1.1 200 OK
X-Storage-Url: http://localhost:8080/v1/AUTH_test2
X-Auth-Token: AUTH_tk24af27299d7a48d5b68cbf7fdca7d433
Content-Type: text/html; charset=UTF-8
X-Storage-Token: AUTH_tk24af27299d7a48d5b68cbf7fdca7d433
Content-Length: 0
X-Trans-Id: txdb47515622e647848d71c-0054c26a61
Date: Fri, 23 Jan 2015 15:36:01 GMT

# 404 when test:tester token tries to GET a container in non-existent account for test2

swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_test2/container -H 'X-Auth-Token: AUTH_tkf1fef50ff23d484ea9fd9493b675fc38' -iHTTP/1.1 404 Not Found
Content-Length: 70
Content-Type: text/html; charset=UTF-8
X-Trans-Id: txcb1de75956b64dffa877a-0054c26a89
Date: Fri, 23 Jan 2015 15:36:41 GMT

<html><h1>Not Found</h1><p>The resource could not be found.</p></html>swift@saio-1:~/swift$

# 404 when test2:tester2 token tries to GET a container in non-existent account for test2

swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_test2/container -H 'X-Auth-Token: AUTH_tk24af27299d7a48d5b68cbf7fdca7d433' -i
HTTP/1.1 404 Not Found
Content-Length: 70
Content-Type: text/html; charset=UTF-8
X-Trans-Id: txb5f34f1dd1a34ce084ce6-0054c26aab
Date: Fri, 23 Jan 2015 15:37:15 GMT

<html><h1>Not Found</h1><p>The resource could not be found.</p></html>swift@saio-1:~/swift$

# test2:tester2 creates a container which causes account to autcreated

swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_test2/blah -H 'X-Auth-Token: AUTH_tk24af27299d7a48d5b68cbf7fdca7d433' -i -X PUT
HTTP/1.1 201 Created
Content-Length: 0
Content-Type: text/html; charset=UTF-8
X-Trans-Id: txcb17dbe687c645e4aa767-0054c26abd
Date: Fri, 23 Jan 2015 15:37:33 GMT

# Now 403 when test:tester token tries to GET a container in account for test2

swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_test2/container -H 'X-Auth-Token: AUTH_tkf1fef50ff23d484ea9fd9493b675fc38' -i
HTTP/1.1 403 Forbidden
Content-Length: 73
Content-Type: text/html; charset=UTF-8
X-Trans-Id: tx3b706920f7d743fb99772-0054c26ac1
Date: Fri, 23 Jan 2015 15:37:37 GMT

<html><h1>Forbidden</h1><p>Access was denied to this resource.</p></html>

# Still 404 when test2:tester2 token tries to GET a container in non-existent account for test2

swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_test2/container -H 'X-Auth-Token: AUTH_tk24af27299d7a48d5b68cbf7fdca7d433' -i
HTTP/1.1 404 Not Found
Content-Length: 70
Content-Type: text/html; charset=UTF-8
X-Trans-Id: tx5e61c4ea46d3488bbb9a4-0054c26ae0
Date: Fri, 23 Jan 2015 15:38:08 GMT

<html><h1>Not Found</h1><p>The resource could not be found.</p></html>

Revision history for this message
Alistair Coles (alistair-coles) wrote :
Revision history for this message
Alistair Coles (alistair-coles) wrote :

The attached script will illustrate the behavior and verify the fix (assumes tempauth).

Changed in ossa:
status: New → Incomplete
Revision history for this message
Tristan Cacqueray (tristan-cacqueray) wrote :

Since this report concerns a possible security risk, an incomplete security advisory task has been added while the core security reviewers for the affected project or projects confirm the bug and discuss the scope of any vulnerability along with potential solutions.

This is an un-authenticated user enumaration type of vulnerability... However it seems to be affecting swift tempauth, as the Auth_ part of the url should contain tenant UUID. Thus I would suggest a C1 class (https://wiki.openstack.org/wiki/Vulnerability_Management#Incident_report_taxonomy).

Thought ?

Revision history for this message
Alistair Coles (alistair-coles) wrote :
Download full text (4.5 KiB)

This bug is also seen with keystone auth. I mentioned that above but here's an example (below).The root cause is the swift proxy not calling back to auth middleware, whatever auth middleware that might be.

````
# using keystoneauth...

swift@saio-1:~/swift$ curl http://localhost:8080/info
{"ratelimit": {"container_ratelimits": []}, "container_quotas": {}, "account_quotas": {}, "tempurl": {"methods": ["GET", "HEAD", "PUT", "POST", "DELETE"]}, "slo": {"max_manifest_segments": 1000, "min_segment_size": 4, "max_manifest_size": 2097152}, "swift": {"max_file_size": 5368709122, "account_listing_limit": 10000, "account_autocreate": true, "max_meta_count": 90, "max_meta_value_length": 256, "container_listing_limit": 10000, "max_meta_overall_size": 4096, "version": "2.2.1.18.g1f1cdce", "max_meta_name_length": 128, "max_header_size": 8192, "policies": [{"default": true, "name": "Policy-0"}, {"name": "partpow15"}], "max_object_name_length": 1024, "max_account_name_length": 256, "strict_cors_mode": true, "allow_account_management": false, "max_container_name_length": 256}, "keystoneauth": {}}

# get tokens and id for test:tester and test2:tester2...

swift@saio-1:~/swift$ keystone --os-username tester --os-tenant-name test --os-password testing --os-auth-url http://u132.localdomain:35357/v2.0 token-get+-----------+----------------------------------+
| Property | Value |
+-----------+----------------------------------+
| expires | 2015-01-23T16:12:33Z |
| id | ecf0570fdcd84ae388348c6fddbc799f |
| tenant_id | cfb8d9d45212408b90bc0776117aec9e |
| user_id | 3e1b511d0c0841d5af382a4932799631 |
+-----------+----------------------------------+

swift@saio-1:~/swift$ keystone --os-username tester2 --os-tenant-name test2 --os-password testing2 --os-auth-url http://u132.localdomain:35357/v2.0 token-get
+-----------+----------------------------------+
| Property | Value |
+-----------+----------------------------------+
| expires | 2015-01-23T16:12:37Z |
| id | 5fcfa357064b4587a78d1ab85d1f9ce9 |
| tenant_id | 14a4d630abea491eb21a7a50864f6003 |
| user_id | 4311a96625aa46119782f66af57ad634 |
+-----------+----------------------------------+

# 404 response for GET on container in non-existent account for 'test2' using token for 'test'.

swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_14a4d630abea491eb21a7a50864f6003/container -H 'X-Auth-Token: ecf0570fdcd84ae388348c6fddbc799f' -iHTTP/1.1 404 Not Found
Content-Length: 70
Content-Type: text/html; charset=UTF-8
X-Trans-Id: tx23e7a33c22e844a7932b9-0054c26642
Date: Fri, 23 Jan 2015 15:18:26 GMT

# 404 response for GET on container in non-existent account for 'test2' using token for 'test2'.

<html><h1>Not Found</h
swift@saio-1:~/swift$ curl http://localhost:8080/v1/AUTH_14a4d630abea491eb21a7a50864f6003/container -H 'X-Auth-Token: 5fcfa357064b4587a78d1ab85d1f9ce9' -i
HTTP/1.1 404 Not Found
Content-Length: 70
Content-Type: text/html; charset=UTF-8
X-Trans-Id: txc86061d731b64381bb973-0054c2666f
Date: Fri, 23 Jan 2015 15:19:11 GMT

<html><h1>Not Found</h1><p>The resource could not be found.</p></html>swift@saio-1:~/sw...

Read more...

Revision history for this message
Tristan Cacqueray (tristan-cacqueray) wrote :

@Alistair, What I meant is that uuid enumeration is not considered realistic because of the huge size of uuid space compared to username'. (2**128 possible uuid). Even sampling based estimation of the number of accounts seems also impossible to achieve.

Anyway this can surely be fixed, thus I suggested a C1 class type of bug, meaning we remove the advisory task and open this report for public review.

Though I might underrate tempauth usage (e.g., used for custom auth system).

Revision history for this message
clayg (clay-gerrard) wrote :

Why do we even *do* a preflight request for the account when looking up the container? Object get doesn't need to check if the container exists before it gets the object (I mean... it *will* call container_info to get the container acl's before calling authorize - but that's sort of the right thing to do).

Is this some sort of special case handling for when an account get's deleted (like 410 marked for reaper) - because if 410 makes account_info come back empty the PUT path for container makes the account autocreate handling look inefficient or possibly wrong?

I'm ok with adding the second authorize callback return from GETorHEAD as the most obviously correct fix - but can we expand testing to consider the 410 account deleted. I think PUT/DELETE/POST are ok, because they're not decorated delay_denial - but maybe it'd be worth a for verb loop just to be sure...

Revision history for this message
Alistair Coles (alistair-coles) wrote :

@Tristan ok, thanks for clarification.

@Clay I'll attach a new version of the patch with increase coverage (i.e. verify that any unauth'd container request will return whatever the swift.authorize returns, and that any auth'd request will return 404, if the account is not found.

Re 410's for deleted accounts - the account_info function doesn't discriminate between 404 and 410 from the account - anything other than success from the account HEAD results in None,None,None returned from account_info which the container controller maps to a 404.

As for why the container request is predicated by account existence, that is interesting and related to this bug too [1] but is probably a separate discussion as to what the 'correct' API behavior should be when accessing resources in a 'non-existent' account.

[1] https://review.openstack.org/#/q/status:open+project:openstack/swift+branch:master+topic:bug/1381541,n,z

Revision history for this message
Alistair Coles (alistair-coles) wrote :

new patch with tests for other unauth'd request methods, and test verifying that all auth'd container requests are 404'd when the account is not found/deleted.

Revision history for this message
Tristan Cacqueray (tristan-cacqueray) wrote :

After discussing this with notmyname, UUID are not particularly secret and could be stolen and used to detect the lack of swift usage for a particular account... Though this does not sound very valuable for an attacker.

I still suggest a C1 type of bug and propose we open it by next week if no objections.

Changed in ossa:
status: Incomplete → Won't Fix
Revision history for this message
Jeremy Stanley (fungi) wrote :

I agree with C1 for this report (not considered a practical vulnerability).

information type: Private Security → Public
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to swift (master)

Fix proposed to branch: master
Review: https://review.openstack.org/154164

Changed in swift:
status: New → In Progress
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix merged to swift (master)

Reviewed: https://review.openstack.org/154164
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=a82bfb25ba180dbde700d2f9108acf1c85f5d61f
Submitter: Jenkins
Branch: master

commit a82bfb25ba180dbde700d2f9108acf1c85f5d61f
Author: Alistair Coles <email address hidden>
Date: Thu Jan 29 14:26:01 2015 +0000

    Make container GET call authorize when account not found

    When an account was not found, ContainerController would
    return 404 unconditionally for a container GET or HEAD request,
    without checking that the request was authorized.

    This patch modifies the GETorHEAD method to first call any
    callback method registered under 'swift.authorize' in the
    request environ and prefer any response from that over the 404.

    Closes-Bug: 1415957

    Change-Id: I4f41fd9e445238e14af74b6208885d83698cc08d

Changed in swift:
status: In Progress → Fix Committed
Revision history for this message
clayg (clay-gerrard) wrote :

backport?

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to swift (feature/ec)

Fix proposed to branch: feature/ec
Review: https://review.openstack.org/158087

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix merged to swift (feature/ec)
Download full text (15.3 KiB)

Reviewed: https://review.openstack.org/158087
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=c84d50f43fdfbdc72ea9c0867769cfa7fdbb09c0
Submitter: Jenkins
Branch: feature/ec

commit db29ffc98384a9f61c2d4cc48bb2faf9f5dd0478
Author: Samuel Merritt <email address hidden>
Date: Fri Feb 20 11:04:24 2015 -0800

    Make proxy_logging close the WSGI iterator

    PEP 333 says that the WSGI framework will call .close() on the
    iterator returned by a WSGI application once it's done, provided such
    a method exists. So, if our code wraps an iterator, then we have to
    call .close() on it once we're done with it. proxy_logging wasn't.

    Since WSGIContext gets it right, I looked at making proxy_logging use
    WSGIContext. However, WSGIContext is all about forcing the first chunk
    out of the iterator so that it can capture the final HTTP status and
    headers; it doesn't help if you want to look at every chunk.
    proxy_logging wants every chunk so it can count the bytes sent.

    This didn't hurt anything in Swift, but pconstantine was complaining
    in IRC that our failure to call .close() was goofing up some other
    middleware he had.

    Change-Id: Ic6ea0795ccef6cda2b5c6737697ef7d58eac9ab4

commit 4ca08cc395e686265574366497a6869e94eebcb2
Author: Alistair Coles <email address hidden>
Date: Tue Feb 17 10:27:44 2015 +0000

    Update guest VM OS recommendation in SAIO doc

    The target development platform has changed to Ubuntu 14.04 [1].
    This patch makes the suggested SAIO platform the same.

    Also, remove pointer to wiki page for other platform install
    instructions that either redirects back to this SAIO doc or
    to another wike page and then a dead link.

    [1] I0a96bcf692bb240f3ab5aab7fefd294a07735a83

    DocImpact

    Change-Id: I9f96104b5437c1f1f28f924c048ef83cf03338f4

commit 7bc09dfdea0893a49e50005b22b426ae21c11d22
Author: Arnaud JOST <email address hidden>
Date: Wed Feb 18 11:56:11 2015 +0100

    Add support of x-remove- headers for container-sync

    If the used tool to send header doesn't support empty headers (older versions
    of curl), x-remove can be used to remove metadata.
    sync-key and sync-to metadata, used by container-sync, can now be removed using
    x-remove headers.

    Change-Id: I0edb4d5425a99d20a973aa4fceaf9af6c2ddecc0

commit 949804eda4a85c3c960b0baa090e16f1aa48e95e
Author: John Dickinson <email address hidden>
Date: Mon Feb 16 14:00:24 2015 -0800

    update the getting started doc

    Change-Id: I0a96bcf692bb240f3ab5aab7fefd294a07735a83

commit dd1a05f52765a6273906b5d6ce55f81e269bad12
Author: OpenStack Proposal Bot <email address hidden>
Date: Mon Feb 16 06:30:54 2015 +0000

    Imported Translations from Transifex

    For more information about this automatic import see:
    https://wiki.openstack.org/wiki/Translations/Infrastructure

    Change-Id: I013976c6192a8bff891c9050f829ae7a1e2fec59

commit 7acc2911296f48f10165282eee6bfe8e5c817a69
Author: John Dickinson <email address hidden>
Date: Sun Feb 15 17:14:31 2015 -0800

    added swift_source to ratelimit info calls

    Change-Id: I2b4cc...

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to swift (feature/crypto)

Fix proposed to branch: feature/crypto
Review: https://review.openstack.org/158370

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix merged to swift (feature/crypto)
Download full text (17.0 KiB)

Reviewed: https://review.openstack.org/158370
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=24d3f51386965197c36d506228e34ca1c4100165
Submitter: Jenkins
Branch: feature/crypto

commit db29ffc98384a9f61c2d4cc48bb2faf9f5dd0478
Author: Samuel Merritt <email address hidden>
Date: Fri Feb 20 11:04:24 2015 -0800

    Make proxy_logging close the WSGI iterator

    PEP 333 says that the WSGI framework will call .close() on the
    iterator returned by a WSGI application once it's done, provided such
    a method exists. So, if our code wraps an iterator, then we have to
    call .close() on it once we're done with it. proxy_logging wasn't.

    Since WSGIContext gets it right, I looked at making proxy_logging use
    WSGIContext. However, WSGIContext is all about forcing the first chunk
    out of the iterator so that it can capture the final HTTP status and
    headers; it doesn't help if you want to look at every chunk.
    proxy_logging wants every chunk so it can count the bytes sent.

    This didn't hurt anything in Swift, but pconstantine was complaining
    in IRC that our failure to call .close() was goofing up some other
    middleware he had.

    Change-Id: Ic6ea0795ccef6cda2b5c6737697ef7d58eac9ab4

commit 4ca08cc395e686265574366497a6869e94eebcb2
Author: Alistair Coles <email address hidden>
Date: Tue Feb 17 10:27:44 2015 +0000

    Update guest VM OS recommendation in SAIO doc

    The target development platform has changed to Ubuntu 14.04 [1].
    This patch makes the suggested SAIO platform the same.

    Also, remove pointer to wiki page for other platform install
    instructions that either redirects back to this SAIO doc or
    to another wike page and then a dead link.

    [1] I0a96bcf692bb240f3ab5aab7fefd294a07735a83

    DocImpact

    Change-Id: I9f96104b5437c1f1f28f924c048ef83cf03338f4

commit 7bc09dfdea0893a49e50005b22b426ae21c11d22
Author: Arnaud JOST <email address hidden>
Date: Wed Feb 18 11:56:11 2015 +0100

    Add support of x-remove- headers for container-sync

    If the used tool to send header doesn't support empty headers (older versions
    of curl), x-remove can be used to remove metadata.
    sync-key and sync-to metadata, used by container-sync, can now be removed using
    x-remove headers.

    Change-Id: I0edb4d5425a99d20a973aa4fceaf9af6c2ddecc0

commit 949804eda4a85c3c960b0baa090e16f1aa48e95e
Author: John Dickinson <email address hidden>
Date: Mon Feb 16 14:00:24 2015 -0800

    update the getting started doc

    Change-Id: I0a96bcf692bb240f3ab5aab7fefd294a07735a83

commit dd1a05f52765a6273906b5d6ce55f81e269bad12
Author: OpenStack Proposal Bot <email address hidden>
Date: Mon Feb 16 06:30:54 2015 +0000

    Imported Translations from Transifex

    For more information about this automatic import see:
    https://wiki.openstack.org/wiki/Translations/Infrastructure

    Change-Id: I013976c6192a8bff891c9050f829ae7a1e2fec59

commit 7acc2911296f48f10165282eee6bfe8e5c817a69
Author: John Dickinson <email address hidden>
Date: Sun Feb 15 17:14:31 2015 -0800

    added swift_source to ratelimit info calls

    Change-Id: I2...

Thierry Carrez (ttx)
Changed in swift:
milestone: none → 2.3.0-rc1
status: Fix Committed → Fix Released
Thierry Carrez (ttx)
Changed in swift:
milestone: 2.3.0-rc1 → 2.3.0
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.