渗透基础 —— Zimbra 版本探测

发布于 2025-01-15 07:17:31 字数 9192 浏览 5 评论 0

0x00 前言

本文将要介绍 Zimbra 版本探测的多种方法,通过 Python 实现自动化,记录开发细节,开源代码。

0x01 简介

本文将要介绍以下内容:

  • 实现思路
  • 实现细节
  • 开源代码

0x02 实现思路

查看 Zimbra 版本的方法有很多,各有优缺点,具体方法如下:

1.通过 Web 管理页面

通过浏览器访问 7071 管理页面,在主页面会显示当前 Zimbra 版本

例如我的测试环境显示为:Zimbra Version: 9.0.0_GA_4273.NETWORK

通过该方法获得的版本为准确版本

2.通过执行命令

su zimbra
/opt/zimbra/bin/zmcontrol -v

例如我的测试环境显示为:Release 9.0.0.GA.3924.UBUNTU16.64 UBUNTU16_64 NETWORK edition, Patch 9.0.0_P24.1.

对于输出结果,需要注意以下问题:

  • Release 9.0.0.GA.3924 这个对应一开始安装包的版本,不会随更新补丁而改变
  • Patch 9.0.0_P24.1 为补丁版本 ,当升级时,这个版本号会改变

注:

Zimbra 补丁更新可参考:https://wiki.zimbra.com/wiki/Zimbra_Releases/9.0.0/patch_installation

3.通过 Zimbra SOAP API

默认配置下, zimbraSoapExposeVersion 属性为 FLASE ,查询命令:

zmprov gs `hostname` | grep ExposeVersion

返回结果:

zimbraImapExposeVersionOnBanner: FALSE
zimbraLmtpExposeVersionOnBanner: FALSE
zimbraPop3ExposeVersionOnBanner: FALSE
zimbraReverseProxyImapExposeVersionOnBanner: FALSE
zimbraReverseProxyPop3ExposeVersionOnBanner: FALSE
zimbraSoapExposeVersion: FALSE

需要将 zimbraSoapExposeVersion 属性设置为 TRUE 后,可以通过 Zimbra SOAP API 获得版本,修改属性的命令为:

su zimbra
/opt/zimbra/bin/zmprov mcf zimbraSoapExposeVersion TRUE

发送的 SOAP 格式示例:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
       <soap:Header>
           <context xmlns="urn:zimbra">
               <authToken>{token}</authToken>
           </context>
       </soap:Header>
       <soap:Body>
         <GetVersionInfoRequest xmlns="urn:zimbraAccount"> 
         </GetVersionInfoRequest>
       </soap:Body>
    </soap:Envelope>

默认配置下的返回结果:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"><soap:Header><context xmlns="urn:zimbra"><change token="2"/></context></soap:Header><soap:Body><soap:Fault><soap:Code><soap:Value>soap:Sender</soap:Value></soap:Code><soap:Reason><soap:Text>permission denied: Version info is not available.</soap:Text></soap:Reason><soap:Detail><Error xmlns="urn:zimbra"><Code>service.PERM_DENIED</Code><Trace>qtp2008966511-673279:1675321733266:bb7c9a24ece078fe</Trace></Error></soap:Detail></soap:Fault></soap:Body></soap:Envelope>

开启 zimbraSoapExposeVersion 后的返回结果:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"><soap:Header><context xmlns="urn:zimbra"><change token="2"/></context></soap:Header><soap:Body><GetVersionInfoResponse xmlns="urn:zimbraAccount"><info release="20220506180442" host="zre-ubuntu16-64.eng.zimbra.com" buildDate="20220506-1841" version="9.0.0_GA_4273.NETWORK"/></GetVersionInfoResponse></soap:Body></soap:Envelope>

通过该方法获得的版本为准确版本

4.通过 imap 协议

需要 Zimbra 开放 143 端口

执行命令示例:

nc 192.168.1.1 143
A001 ID NIL

返回结果:

* ID ("NAME" "Zimbra" "VERSION" "9.0.0_GA_4273" "RELEASE" "20220506180442")

通过该方法获得的版本为准确版本

5.通过 imap over ssl 协议

需要 Zimbra 开放 993 端口

执行命令示例:

openssl s_client -connect 192.168.1.1:993
show id ("a" "a")

返回结果:

* ID ("NAME" "Zimbra" "VERSION" "9.0.0_GA_4273" "RELEASE" "20220506180442")

通过该方法获得的版本为准确版本

6.通过特定 url

特定 url 中会包含安装信息

注:

该 url 不唯一,访问位置示例: https://192.168.1.1/js/zimbraMail/share/model/ZmSettings.js

返回结果示例:

    this.registerSetting("CLIENT_DATETIME",                    {type:ZmSetting.T_CONFIG, defaultValue:"20220324-0623"});
    this.registerSetting("CLIENT_RELEASE",                    {type:ZmSetting.T_CONFIG, defaultValue:"20220324053424"});
    this.registerSetting("CLIENT_VERSION",                    {type:ZmSetting.T_CONFIG, defaultValue:"9.0.0_GA_4258"});

CLIENT_DATETIMECLIENT_RELEASE 同该文件的创建时间保持一致,通过该方法获得的版本仅供参考,无法作为版本探测的准确依据

0x03 实现细节

综合以上探测方法,为了适应多种环境,在程序实现上选取了通过 imap 协议、通过 imap over ssl 协议和通过特定 url 三种方法实现

1.通过 imap 协议

完整示例代码:

def getversionimap(ip):
    try:
        print("[*] Try to connect: " + ip + ":143")
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.settimeout(5)
        s.connect((ip, 143))
        s.sendall(''.encode())
        response = s.recv(1024)
        if "OK" in response.decode('UTF-8'):
            print("    OK")
        else:
            print(response.decode('UTF-8'))
            s.close()
            sys.exit(0)
        s.sendall('A001 ID NIL\r\n'.encode())
        response = s.recv(1024)
        if "Zimbra" in response.decode('UTF-8'):
            versiondata=re.compile(r"VERSION\" \"(.*?)\"")
            version = versiondata.findall(response.decode('UTF-8'))[0]

            releasedata=re.compile(r"RELEASE\" \"(.*?)\"")
            release = releasedata.findall(response.decode('UTF-8'))[0]
            print("[+] Version: " + version)
            print("    Release: " + release)
            return release
        else:
            print(response.decode('UTF-8'))
            s.close()
    except Exception as e:
        print(e)
        return ""

2.通过 imap over ssl 协议

需要将 ip 转为 hostname 作为参数,示例代码:

hostname = socket.gethostbyaddr(ip)
print(hostname[0])

完整示例代码:

def getversionimapoverssl(ip):
    try:
        hostname = socket.gethostbyaddr(ip)
        print("[*] Try to connect: " + hostname[0] + ":993")
        context = ssl.create_default_context()
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s = context.wrap_socket(s, server_hostname=hostname[0])
        s.settimeout(5)
        s.connect((ip, 993))
        s.sendall(''.encode())
        response = s.recv(1024)
        if "OK" in response.decode('UTF-8'):
            print("    Success")
        else:
            print(response.decode('UTF-8'))
            s.close()
            sys.exit(0)
        s.sendall('A001 ID NIL\r\n'.encode())
        response = s.recv(1024)
        if "Zimbra" in response.decode('UTF-8'):
            versiondata=re.compile(r"VERSION\" \"(.*?)\"")
            version = versiondata.findall(response.decode('UTF-8'))[0]

            releasedata=re.compile(r"RELEASE\" \"(.*?)\"")
            release = releasedata.findall(response.decode('UTF-8'))[0]
            print("[+] Version: " + version)
            print("    Release: " + release)
            return release
        else:
            print(response.decode('UTF-8'))
            s.close()
    except Exception as e:
        print(e)
        return ""

存在部分环境无法将 ip 转为 hostname ,导致报错: [Errno 11004] host not found ,所以在程序判断逻辑上优先使用 imap 协议

3.通过特定 url

完整示例代码:

def getversionweb(ip):
    try:  
        url = "https://" + ip + "/js/zimbraMail/share/model/ZmSettings.js"
        print("[*] Try to access: " + url)
        headers={
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36",
        }
        response = requests.get(url, headers=headers, verify=False, timeout=5)

        if response.status_code == 200 and 'CLIENT_RELEASE' in response.text:
            print("    Success")
            VERSION_name = re.compile(r"CLIENT_VERSION\",                    {type:ZmSetting.T_CONFIG, defaultValue:\"(.*?)\"}\);")
            CLIENT_VERSION = VERSION_name.findall(response.text)

            RELEASE_name = re.compile(r"CLIENT_RELEASE\",                    {type:ZmSetting.T_CONFIG, defaultValue:\"(.*?)\"}\);")
            CLIENT_RELEASE = RELEASE_name.findall(response.text)

            print("[+] Version: " + CLIENT_VERSION[0])
            print("    Release: " + CLIENT_RELEASE[0])    
        else:
            print("[-]")
            print(response.status_code)
            print(response.text)
    except Exception as e: 
        print(e)

0x04 开源代码

完整的实现代码已上传至 github,地址如下:https://github.com/3gstudent/Homework-of-Python/blob/master/Zimbra_GetVersion.py

代码首先尝试通过特定 url 获得版本信息,再通过 imap 协议读取版本信息,如果失败,最后通过 imap over ssl 协议读取版本信息

0x05 小结

本文介绍了 Zimbra 版本探测的多种方法,比较优缺点,选取有效的方法并通过 Python 实现自动化,记录开发细节,开源代码,作为一个很好的学习示例。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

泛滥成性

暂无简介

文章
评论
1009 人气
更多

推荐作者

IDC-hncloud

文章 0 评论 0

薆情海

文章 0 评论 0

mb_VjXiXQg5

文章 0 评论 0

爱,才寂寞

文章 0 评论 0

BE WATER

文章 0 评论 0

微信用户

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文