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:

mail.test.com

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:


[email protected]
b248f6cfd027edd45c5369f8490125204772f844

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.