An n-day reconstruction. ToolShell is the SharePoint chain that ran through the second half of 2025, and it is worth the writeup for a reason that has nothing to do with the RCE itself. The RCE is a deserialization bug, and deserialization bugs in .NET are not news. What makes this one worth the space is the shape of the whole thing: a chain demonstrated at a contest, an incomplete vendor patch, a bypass of that patch in the wild, and a payload whose real objective was not code execution at all. It was a key.
The chain, as it was shown
Start at Pwn2Own Berlin, May 2025. Viettel Cyber Security chained 2 on-prem SharePoint bugs into unauthenticated RCE: CVE-2025-49706, an authentication bypass, and CVE-2025-49704, a deserialization of untrusted data. Auth bypass to reach a privileged endpoint; deserialization to turn reach into execution. The pairing was named ToolShell after the endpoint it abuses.
The mechanics are simple enough to state in a sentence. A POST to /_layouts/15/ToolPane.aspx, carrying a Referer of /_layouts/SignOut.aspx, is enough to slip past the authentication check and reach the page’s deserialization path; from there the request writes an .aspx file into the web root and the server runs it. On-prem only; SharePoint Online was never in scope. Microsoft shipped fixes for both CVEs in the July 2025 cycle. That should have been the end of it.
The gap
It was not the end of it, because the patch was incomplete. Within days the same primitives came back as CVE-2025-53770 and CVE-2025-53771, which are the same 2 ideas, auth bypass and deserialization, reworked to step around the July fix rather than trigger it. Microsoft and the first responders at Eye Security, who caught the mass exploitation on 18 July, both described it plainly: the earlier patch closed the reported path, not the underlying capability. Same door, a slightly different key.
This is the pattern the desk keeps returning to, because it is the one that keeps costing people. A CVE gets reported as a specific request that does a specific bad thing. The vendor patches that request. The capability underneath, in this case “reach ToolPane.aspx unauthenticated and hand it attacker-controlled serialized data,” survives the patch because it was never the thing that got fixed. 3 CVEs deep before the deserialization path is actually shut, and that is if you count generously.
The payload was a key, not a shell
Here is the part that makes ToolShell more than another RCE. The webshell most operators found, spinstall0.aspx, does almost nothing interesting on its own. It does not open a reverse shell or drop a second stage. It reads the server’s ASP.NET machineKey: the ValidationKey and DecryptionKey that SharePoint uses to sign and encrypt __VIEWSTATE. Then it leaves.
That is the objective. With the machineKey in hand, an attacker forges their own valid, signed __VIEWSTATE payloads whenever they want, and each one deserializes into code execution through the well-worn ViewState gadget chains. No auth bypass needed anymore; the request is cryptographically valid because it is signed with the server’s own key. And, the detail that matters most: the stolen key still works after the server is patched. You can apply every fix Microsoft shipped and the attacker who pulled your machineKey in July still holds a skeleton key to your ViewState in August. The same shape shows up outside Windows entirely: in the tj-actions supply-chain compromise, the secrets exfiltrated during the window kept working long after the action itself was reverted.
Patch is not eviction
This is the same lesson as the SAP writeup from earlier this month, and the same lesson as every appliance memory-disclosure bug that leaks a session token: the patch closes the entry, it does not remove the thing the attacker already took out through it. For ToolShell the eviction step is explicit and non-optional. Rotate the ASP.NET machineKey on every affected SharePoint server, and restart IIS so the new key takes effect, after patching. A server that was patched but not re-keyed is a server the July attacker can still reach, and the guidance from CISA and Microsoft says exactly that.
The hunt is straightforward if you know what you are looking for. A POST to ToolPane.aspx with the SignOut.aspx Referer is the ingress tell; spinstall0.aspx or any freshly written .aspx under the layouts path is the drop; and after that, valid-but-unexpected __VIEWSTATE deserialization is the sign the key is already gone and being used. The first two are easy. The third is the one that needs someone actually watching the request layer, because a forged ViewState looks, by construction, exactly like a legitimate one.
The through-line
ToolShell reconstructs cleanly into 4 moving parts: a contest chain, an incomplete patch, a bypass of that patch, and a credential-theft payload that outlives all of it. None of the 4 is novel on its own. Deserialization of untrusted data is 15 years old; machineKey theft to forge ViewState is not new either. What the 2025 chain shows is how little of that has to be new to work, and how a patch shipped on time still leaves the door open if the fix stops at the reported request and the response stops at “we patched.” The interesting artifact here was never the shell. It was the key the shell walked out with.