[Openstack-security] [Bug 1765834] Re: Need to verify content of v4-signed PUTs

OpenStack Infra 1765834 at bugs.launchpad.net
Wed Oct 23 17:44:40 UTC 2019


Reviewed:  https://review.opendev.org/689883
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=423f96293b6eb43504b5370e893c280697ed23c9
Submitter: Zuul
Branch:    stable/rocky

commit 423f96293b6eb43504b5370e893c280697ed23c9
Author: Tim Burke <tim.burke at gmail.com>
Date:   Tue Dec 11 15:29:35 2018 -0800

    Verify client input for v4 signatures
    
    This is a combination of 2 commits.
    
    ==========
    
    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).
    
    Closes-Bug: 1765834
    (cherry picked from commit 3a8f5dbf9c49fdf1cf2d0b7ba35b82f25f88e634)
    
    ----------
    
    s3api: Allow clients to upload with UNSIGNED-PAYLOAD
    
    (Some versions of?) awscli/boto3 will do v4 signatures but send a
    Content-MD5 for end-to-end validation. Since a X-Amz-Content-SHA256
    is still required to calculate signatures, it uses UNSIGNED-PAYLOAD
    similar to how signatures work for pre-signed URLs.
    
    Look for UNSIGNED-PAYLOAD and skip SHA256 validation if set.
    
    (cherry picked from commit 82e446a8a0c0fd6a81f06717b76ed3d1be26a281)
    (cherry picked from commit 6ed165cf3f65329beaef9977a5fec24ce3ac0b39)
    
    ==========
    
    Change-Id: I61eb12455c37376be4d739eee55a5f439216f0e9


** Tags added: in-stable-rocky

-- 
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:
  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