渗透基础 —— Zimbra 版本探测
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_DATETIME
和 CLIENT_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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论