0x00 Preface
---
This article will continue to expand the practical features of the open-source code Zimbra_SOAP_API_Manage by adding a pre-authentication login method, sharing development details.
0x01 Introduction
---
This article will cover the following topics:
- Pre-authentication
- Calculating preauth
- SOAP implementation
- Open-source code
0x02 Pre-authentication
---
Reference: https://wiki.zimbra.com/wiki/Preauth
Simple explanation: Using preAuthKey combined with username, timestamp, and expiration time, the calculated HMAC serves as an authentication token, which can be used for user mailbox and SOAP login
By default, Zimbra does not enable pre-authentication functionality and requires manual activation.
(1) Enable pre-authentication and generate PreAuthKey
The command is as follows:
/opt/zimbra/bin/zmrov generateDomainPreAuthKey |
Here, corresponds to the current Zimbra server's domain name, which can be obtained by executing the command /opt/zimbra/bin/zmprov gad. The output in the test environment is as follows:
The corresponding command for the test environment is: /opt/zimbra/bin/zmprov generateDomainPreAuthKey mail.test.com
The output in the test environment is as follows:
preAuthKey: fbf0ace37c59e3893352c656eda3d7f25c0ce0baadc9cbf22eb03f3b256f17a7 |
(2) Read existing PreAuthKey
The command is as follows:
/opt/zimbra/bin/zmprov gd zimbraPreAuthKey |
The corresponding command for the test environment is: /opt/zimbra/bin/zmprov gd mail.test.com zimbraPreAuthKey
The output in the test environment is as follows:
zimbraPreAuthKey: fbf0ace37c59e3893352c656eda3d7f25c0ce0baadc9cbf22eb03f3b256f17a7 |
Note:
If Zimbra has multiple domains, there will be multiple PreAuthKeys
0x03 Calculate preauth
---
The reference materials provide multiple examples of calculating preauth, but the Python implementation code is incomplete. Here, the complete implementation code under Python3 is supplemented, with detailed code as follows:
from time import time import hmac, hashlib import sys def generate_preauth(target, preauth_key, mailbox): try: preauth_url = target + "/service/preauth" timestamp = int(time()*1000) data = "{mailbox}|name|0|{timestamp}".format(mailbox=mailbox, timestamp=timestamp) pak = hmac.new(preauth_key.encode(), data.encode(), hashlib.sha1).hexdigest() print("[+] Preauth url: ") print("%s?account=%s&expires=0×tamp=%s&preauth=%s"%(preauth_url, mailbox, timestamp, pak)) except Exception as e: print("[!] Error:%s"%(e))
if __name__ == "__main__": if len(sys.argv)!=4: print('GeneratePreauth') print('Use to generate the preauth key') print('Usage:') print('%s '%(sys.argv[0])) print('Eg.') print('%s https://192.168.1.1 fbf0ace37c59e3893352c656eda3d7f25c0ce0baadc9cbf22eb03f3b256f17a7 [email protected]'%(sys.argv[0])) sys.exit(0) else: generate_preauth(sys.argv[1], sys.argv[2], sys.argv[3]) |
The code will automatically generate a usable URL; accessing it via browser will log into the specified mailbox
0x04 SOAP Implementation
---
SOAP Format:
{account-identifier} {computed-preauth}
|
SOAP Format Example:
Requires timestamp and preauth as parameters. Detailed code for pre-authentication login is as follows:
import sys import requests import re import warnings warnings.filterwarnings("ignore") from time import time import hmac, hashlib
def generate_preauth(target, mailbox, preauth_key): try: preauth_url = target + "/service/preauth" timestamp = int(time()*1000) data = "{mailbox}|name|0|{timestamp}".format(mailbox=mailbox, timestamp=timestamp) pak = hmac.new(preauth_key.encode(), data.encode(), hashlib.sha1).hexdigest() print("[+] Preauth url: ") print("%s?account=%s&expires=0×tamp=%s&preauth=%s"%(preauth_url, mailbox, timestamp, pak)) return timestamp, pak except Exception as e: print("[!] Error:%s"%(e))
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" }
def auth_request_preauth(uri,username,timestamp,pak): request_body=""" {username} {pak} """ try: r=requests.post(uri+"/service/soap",headers=headers,data=request_body.format(username=username,timestamp=timestamp,pak=pak),verify=False,timeout=15) if 'authentication failed' in r.text: print("[-] Authentication failed for %s"%(username)) exit(0) elif 'authToken' in r.text: pattern_auth_token=re.compile(r"(.*?)") token = pattern_auth_token.findall(r.text)[0] print("[+] Authentication success for %s"%(username)) print("[*] authToken_low:%s"%(token)) return token else: print("[!]") print(r.text) except Exception as e: print("[!] Error:%s"%(e)) exit(0)
timestamp, pak = generate_preauth("https://192.168.1.1", "[email protected]", "fbf0ace37c59e3893352c656eda3d7f25c0ce0baadc9cbf22eb03f3b256f17a7") token = auth_request_preauth("https://192.168.1.1","[email protected]",timestamp,pak) |
The above code performs pre-authentication login and returns a usable token. Using this token, subsequent SOAP operations can be performed. Implementation code for listing folder email counts:
def getfolder_request(uri,token): request_body=""" {token} """ try: print("[*] Try to get folder") r=requests.post(uri+"/service/soap",headers=headers,data=request_body.format(token=token),verify=False,timeout=15) pattern_name = re.compile(r"name=\"(.*?)\"") name = pattern_name.findall(r.text) pattern_size = re.compile(r" n=\"(.*?)\"") size = pattern_size.findall(r.text) for i in range(len(name)): print("[+] Name:%s,Size:%s"%(name[i],size[i])) except Exception as e: print("[!] Error:%s"%(e))
getfolder_request("https://192.168.1.1",token) |
0x05 Open Source Code
---
New code has been uploaded to GitHub at the following address:
An open source project
Added functionality for using pre-authentication login
0x06 Summary
---
This article expands the Zimbra SOAP API invocation methods by adding functionality for using pre-authentication login.