zh54321/SharePointDumper

GitHub: zh54321/SharePointDumper

一款基于 PowerShell 的 SharePoint 站点枚举与文件提取工具,通过 Microsoft Graph API 递归下载用户有权访问的文件并生成详细的 HTTP 请求日志用于安全审计。

Stars: 152 | Forks: 16

# SharePointDumper SharePointDumper 是一款基于 PowerShell 的提取和审计工具,可通过 Microsoft Graph 枚举用户有权访问的 SharePoint 站点并通过 SharePoint 下载文件。 它专为 SOC / DLP 测试、紫队演练和非高级红队评估而设计,因此会生成详细报告,包括下载的文件和每个 HTTP 请求(Graph + SharePoint),以便进行 SIEM 关联分析。 该工具不执行身份验证。相反,它需要一个已获取的 OAuth2 访问令牌,并具有适当的 Microsoft Graph 权限(`Sites.Read.All` 或 `Sites.ReadWrite.All`)。 如果您需要获取此类令牌的帮助,可以使用 [EntraTokenAid](https://github.com/zh54321/EntraTokenAid)(下方包含示例),它可以简化生成可用访问令牌的过程。 ## ✨ 功能 - 通过 Microsoft Graph 枚举 SharePoint 站点、驱动器、文件夹和文件 - 递归转储驱动器和文件夹(使用 SharePoint 预身份验证 URL) - 无需强制外部依赖(无 Microsoft Graph PowerShell 模块等) - 自定义 User-Agent - 全局下载限制:最大文件数和最大总大小 - 站点和文件扩展名的包含/排除过滤 - 可调节的请求节流和随机抖动 - 可选的公网 IP 查询 - 支持简单 HTTP 代理 - 结构化报告,包括: - 摘要(持续时间、限制、过滤器、公网 IP) - 访问过的 SharePoint 站点 - 完整的 HTTP 请求日志(CSV 或 JSON) - SharePoint 下载令牌的脱敏处理,以及可选的明文记录功能 - 优雅的 Ctrl+C 处理,在当前文件后停止,但仍会在退出前写入完整报告和 HTTP 日志 - 恢复模式,重新枚举但跳过已下载的文件 - 可选的自动访问令牌刷新(需要 EntraTokenAid https://github.com/zh54321/EntraTokenAid.git) ## 📦 图片 执行: ![alt text](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/977f1ebf6c181817.png) 示例 CSV API 日志(可以是 CSV 或 JSON): ![alt text](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/b715df296a181818.png) 示例文件下载日志(可以是 CSV 或 JSON) ![alt text](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/98426c220d181818.png) ## 📦 安装 ### 克隆仓库 ``` git clone https://github.com/zh54321/SharePointDumper.git cd SharePointDumper ``` ## 🔑 前提条件 SharePointDumper 需要一个有效的 Microsoft Graph 访问令牌,并具有委托权限以枚举站点和文件: - `Sites.Read.All` **或** - `Sites.ReadWrite.All` 此外,OAuth 客户端必须被允许调用 SharePoint API。 已有超过 23 个 Microsoft 第一方应用程序具有预同意的 `Sites.Read…` 权限。它们可以从 SharePoint 下载文件,并且可用于获取有效令牌,而无需额外的租户配置。
可用的第一方客户端 | 应用名称 | 客户端 ID | FOCI | EntraTokenAid 身份验证命令 | |---|---|---|---| | Microsoft Teams | 1fec8e78-bce4-4aaf-ab1b-5451cc387264 | TRUE | `$tokens = Invoke-Auth -ClientID '1fec8e78-bce4-4aaf-ab1b-5451cc387264' -RedirectUrl 'https://login.microsoftonline.com/common/oauth2/nativeclient'` | | Outlook Mobile | 27922004-5251-4030-b22d-91ecd9a37ea4 | TRUE | `$tokens = Invoke-Auth -ClientID '27922004-5251-4030-b22d-91ecd9a37ea4' -RedirectUrl 'x-msauth-outlook-prod://com.microsoft.Office.Outlook'` | | OneDrive SyncEngine | ab9b8c07-8f02-4f72-87fa-80105867a763 | TRUE | `$tokens = Invoke-Auth -ClientID 'ab9b8c07-8f02-4f72-87fa-80105867a763' -RedirectUrl 'https://login.microsoftonline.com/common/oauth2/nativeclient'` | | OneDrive iOS App | af124e86-4e96-495a-b70a-90f90ab96707 | TRUE | `$tokens = Invoke-Auth -ClientID 'af124e86-4e96-495a-b70a-90f90ab96707' -RedirectUrl 'ms-onedrive-auth://com.microsoft.skydrive.shareextension'` | | Microsoft Bing Search | cf36b471-5b44-428c-9ce7-313bf84528de | TRUE | `$tokens = Invoke-Auth -ClientID 'cf36b471-5b44-428c-9ce7-313bf84528de' -RedirectUrl 'msauth.com.microsoft.bing://auth'` | | OneDrive | b26aadf8-566f-4478-926f-589f601d9c74 | TRUE | `$tokens = Invoke-Auth -ClientID 'b26aadf8-566f-4478-926f-589f601d9c74' -RedirectUrl 'urn:ietf:wg:oauth:2.0:oob'` | | SharePoint | d326c1ce-6cc6-4de2-bebc-4591e5e13ef0 | TRUE | `$tokens = Invoke-Auth -ClientID 'd326c1ce-6cc6-4de2-bebc-4591e5e13ef0' -RedirectUrl 'msauth://code/ms-sharepoint-auth%3A%2F%2Fcom.microsoft.sharepoint'` | | SharePoint Android | f05ff7c9-f75a-4acd-a3b5-f4b6a870245d | TRUE | `$tokens = Invoke-Auth -ClientID 'f05ff7c9-f75a-4acd-a3b5-f4b6a870245d' -RedirectUrl 'msauth://com.microsoft.sharepoint/gSoqzhbCjkyvI%2Fl7kC7IdG7KbPU%3D'` | | OfficeHome | 4765445b-32c6-49b0-83e6-1d93765276ca | FALSE | `$tokens = Invoke-Auth -ClientID '4765445b-32c6-49b0-83e6-1d93765276ca' -RedirectUrl 'https://scuprodprv.www.microsoft365.com/spalanding' -Origin 'https://doesnotmatter'` | | SharePoint Online Web Client Extensibility | 08e18876-6177-487e-b8b5-cf950c1e598c | FALSE | `$tokens = Invoke-Auth -ClientID '08e18876-6177-487e-b8b5-cf950c1e598c' -RedirectUrl 'https://onedrive.cloud.microsoft/_forms/spfxsinglesignon.aspx' -Origin 'https://doesnotmatter'` | | Microsoft Teams Web Client | 5e3ce6c0-2b1f-4285-8d4b-75ee78787346 | FALSE | `$tokens = Invoke-Auth -ClientID '5e3ce6c0-2b1f-4285-8d4b-75ee78787346' -RedirectUrl 'https://teams.cloud.microsoft/convene/townhall' -Origin 'https://doesnotmatter'` | | Portfolios | f53895d3-095d-408f-8e93-8f94b391404e | FALSE | `$tokens = Invoke-Auth -ClientID 'f53895d3-095d-408f-8e93-8f94b391404e' -RedirectUrl 'https://project.microsoft.com/msal-redirect2' -Origin 'https://doesnotmatter'` | | Microsoft Remote Assist | fca5a20d-55aa-4395-9c2f-c6147f3c9ffa | FALSE | `$tokens = Invoke-Auth -ClientID 'fca5a20d-55aa-4395-9c2f-c6147f3c9ffa' -RedirectUrl 'http://localhost:13824'` | | ProcessSimpleGCC | 38a893b6-d74c-4786-8fe7-bc3b4318e881 | FALSE | `$tokens = Invoke-Auth -ClientID '38a893b6-d74c-4786-8fe7-bc3b4318e881' -RedirectUrl 'https://.gov.powerautomate.us/auth-redirect.html' -Origin 'https://doesnotmatter'` | | WindowsShareExperienceProd | a8759234-4b8b-4d94-8c0a-ee1ab73af270 | FALSE | `$tokens = Invoke-Auth -ClientID 'a8759234-4b8b-4d94-8c0a-ee1ab73af270' -RedirectUrl 'ms-appx-web://Microsoft.AAD.BrokerPlugin/a8759234-4b8b-4d94-8c0a-ee1ab73af270'` | | Office voice transcript generator AAD | d2eb9fef-f34c-40ec-b6a3-4bf524065158 | FALSE | `$tokens = Invoke-Auth -ClientID 'd2eb9fef-f34c-40ec-b6a3-4bf524065158' -RedirectUrl 'msauth.com.microsoft.Office.TestVoice.TestVoice2://auth'` | | Azure OpenAI Studio | dc807dec-d211-4b3f-bc8a-43b3443c4874 | FALSE | `$tokens = Invoke-Auth -ClientID 'dc807dec-d211-4b3f-bc8a-43b3443c4874' -RedirectUrl 'https://dev.oai.azure.com' -Origin 'https://doesnotmatter'` | | Azure AI Studio App | cb2ff863-7f30-4ced-ab89-a00194bcf6d9 | FALSE | `$tokens = Invoke-Auth -ClientID 'cb2ff863-7f30-4ced-ab89-a00194bcf6d9' -RedirectUrl 'https://int.ai.azure.com/agents' -Origin 'https://doesnotmatter'` | | Azure Machine Learning Workbench Web App | d7304df8-741f-47d3-9bc2-df0e24e2071f | FALSE | `$tokens = Invoke-Auth -ClientID 'd7304df8-741f-47d3-9bc2-df0e24e2071f' -RedirectUrl 'https://dev.ml.azure.com/redirect' -Origin 'https://doesnotmatter'` | | M365ChatClient | c0ab8ce9-e9a0-42e7-b064-33d422df41f1 | FALSE | `$tokens = Invoke-Auth -ClientID 'c0ab8ce9-e9a0-42e7-b064-33d422df41f1' -RedirectUrl 'https://fa000000128.resources.office.net/f7024bdc-7caf-4ca8-807d-2908f09640d6/1.0.2412.19011/en-us_web/login.html' -Origin 'https://doesnotmatter'` | | Microsoft Loop App | a187e399-0c36-4b98-8f04-1edc167a0996 | FALSE | `$tokens = Invoke-Auth -ClientID 'a187e399-0c36-4b98-8f04-1edc167a0996' -RedirectUrl 'https://hosted.loop.cloud.dev.microsoft/authLanding.html' -Origin 'https://doesnotmatter'` | | Viva Goals | c8423563-e8f6-49f2-924b-90d9f664378a | FALSE | `$tokens = Invoke-Auth -ClientID 'c8423563-e8f6-49f2-924b-90d9f664378a' -RedirectUrl 'https://goals-ppe.microsoft.com/aad/consume' -Origin 'https://doesnotmatter'` | | Microsoft Mesh | eea619ad-603a-4b03-a386-860fcc7410d1 | FALSE | `$tokens = Invoke-Auth -ClientID 'eea619ad-603a-4b03-a386-860fcc7410d1' -RedirectUrl 'http://localhost:13824'` |
### 令牌生命周期建议 如果可能,请使用 **CAE 启用**的访问令牌,该令牌有效期最长为 **24 小时**,可减少长时间转储过程中的中断。 ### 使用 EntraTokenAid 获取令牌 您可以使用 **EntraTokenAid** 通过 PowerShell 获取合适的访问令牌 (默认请求 CAE 令牌): ``` # 克隆模块 git clone https://github.com/zh54321/EntraTokenAid.git Import-Module EntraTokenAid/EntraTokenAid.psm1 # 获取令牌 $tokens = Invoke-Auth -ClientID 'b26aadf8-566f-4478-926f-589f601d9c74' -RedirectUrl 'urn:ietf:wg:oauth:2.0:oob' # 可选:存储在变量中以兼容以下示例 $AccessToken = $tokens.access_token ``` ## 🚀 使用方法 ### 基本用法 ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -UserAgent "Not SharePointDumper" ``` ### 过滤站点 ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -IncludeSites "Finance","Projects" ``` ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -ExcludeSites "HR","Legal" ``` ### 过滤文件扩展名 ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -IncludeExtensions pdf,docx ``` ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -ExcludeExtensions jpg,bmp ``` ### 限制下载 ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -MaxFiles 500 ``` ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -MaxTotalSizeMB 100 ``` ### 减慢请求速度 ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -RequestDelaySeconds 2 ``` 带抖动: ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -RequestDelaySeconds 2 -Variation 3 ``` ### 恢复(令牌过期后的示例) ``` .\Invoke-SharePointDumper.ps1 -AccessToken $accesstoken -Resume -OutputFolder .\20251121_1551_MyTenant ``` 在恢复模式下,`-OutputFolder` **必须**指向您之前运行的文件夹。 ### 使用自动令牌刷新 SharePointDumper 可以使用刷新令牌和 EntraTokenAid 自动刷新过期的访问令牌(HTTP 401)。 ``` # 强制要求:必须导入模块以使工具能够使用 Invoke-Refresh 命令 Import-Module .\EntraTokenAid\EntraTokenAid.psm1 # 可选:使用 EntraTokenAid 获取令牌(其他方法也可以) $tokens = Invoke-Auth -ClientId 'b26aadf8-566f-4478-926f-589f601d9c74' -RedirectUrl 'urn:ietf:wg:oauth:2.0:oob' # 使用刷新支持运行 SharePointDumper .\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -RefreshToken $tokens.refresh_token -RefreshClientId 'b26aadf8-566f-4478-926f-589f601d9c74' ``` 注意:令牌刷新尝试**不会**记录在 API 日志中。 ## 参数 | 参数 | 必需 | 类型 | 说明 | |----------|----------|------|-------------| | `AccessToken` | 是 | String | OAuth2 访问令牌。工具内部不执行身份验证。 | | `OutputFolder` | 否 | String | 转储目录。默认值:`"."`。在 `-Resume` 模式下,必须指向一个**已存在的**先前转储文件夹。 | | `UserAgent` | 否 | String | 所有 HTTP 请求的自定义 User-Agent。默认值:SharePointDumper | | `Proxy` | 否 | String | 可选的 HTTP 代理(例如,`http://127.0.0.1:8080`)。 | | `IncludeExtensions` | 否 | String[] | 仅下载这些扩展名。 | | `ExcludeExtensions` | 否 | String[] | 跳过这些扩展名(如果使用了 `IncludeExtensions` 则忽略)。 | | `IncludeSites` | 否 | String[] | 仅处理与这些值匹配的站点。 | | `ExcludeSites` | 否 | String[] | 跳过与这些值匹配的站点。 | | `DisableIpLookup` | 否 | Switch | 不查询 `ifconfig.me` 获取公网 IP。 | | `RequestDelaySeconds` | 否 | Double | 每个 HTTP 请求之前的基准延迟。 | | `Variation` | 否 | Double | 添加到基准延迟的随机抖动。 | | `MaxFiles` | 否 | Int | 最大下载文件数。默认值:无限制。 | | `MaxTotalSizeMB` | 否 | Double | 最大总下载大小。默认值:无限制。 | | `Resume` | 否 | Switch | 跳过已下载的文件。需要 `-OutputFolder`。 | | `ApiLogFormat` | 否 | String | `Csv`(默认)、`Json` 或 `None`。 | | `ApiLogPath` | 否 | String | API 日志文件的自定义输出路径。 | | `FileLogFormat` | 否 | String | `Csv`(默认)、`Json` 或 `None`。 | | `FileLogPath` | 否 | String | 文件日志文件的自定义输出路径。 | | `LogDownloadTokens` | 否 | Switch | 记录完整的 SharePoint 下载 URL,包括 **tempauth** 令牌(不安全)。默认:令牌被**脱敏**。 | | `RefreshToken` | 否 | String | OAuth2 刷新令牌,用于在 HTTP 401 时自动续订访问令牌。 | | `RefreshClientId` | 否 | String | 与 -RefreshToken 一起使用的客户端 ID。 | | `RefreshTenant` | 否 | String | 刷新端点的租户授权(默认:common)。 | ## 🧾 输出结构 ### 目录结构 ``` 20261115_MySecureTenant/ ├── SharePointDumper_Report_20261115_080333.txt ├── SharePointDumper_ApiLog_20261115_080333.csv ├── SharePointDumper_FileLog_20261115_080333.csv ├── Site A/ │ └── Documents/ │ ├── file1.docx │ └── file2.pdf └── Site B/ └── Site Assets/ ``` ### 日志 - **报告**:高级摘要(`Report_*.txt`) - **API 日志**:每个 Graph + SharePoint HTTP 请求 (`ApiLog_*.csv` 或 `.json`) - **恢复运行**生成: - `SharePointDumper_ReportResume_*` - `SharePointDumper_ApiLogResume_*` ## ⚠️ 身份验证错误 ### 401 – 未授权 访问令牌无效或已过期。 ### 403 – 禁止 访问令牌有效,但缺少所需的 Microsoft Graph 或 SharePoint 权限。 ## 🔍 检测 SharePointDumper 活动记录在: - **统一审核日志 (UAL):** SharePoint 搜索和下载活动。 - **Graph 活动日志:** 记录所有站点和驱动器枚举请求(`/organization`、`/sites`、`/drives`)。 示例 UAL: ![asd](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/7d9dfaedd4181819.png) 示例 Graph 活动日志: ![asd](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/cc7fa8902b181821.png) ## 📝 许可证 根据 **MIT 许可证**发布。
标签:AI合规, AMSI绕过, Azure AD, DLP测试, Entra ID, Graph API, HTTP日志, IPv6, IR测试, Libemu, M365安全, Microsoft Graph, OAuth2, PowerShell, SharePoint, SOC工具, SOC测试, StruQ, 威胁检测, 数据外泄, 文件下载, 无线安全, 权限枚举, 漏洞发现, 站点枚举, 紫队工具, 网络安全审计, 递归下载