external/anthropic-cybersecurity-skills/skills/performing-cloud-storage-forensic-acquisition/SKILL.md
Perform forensic acquisition and analysis of cloud storage services including Google Drive, OneDrive, Dropbox, and Box by collecting both API-based remote data and local sync client artifacts from endpoint devices.
npx skillsauth add seikaikyo/dash-skills performing-cloud-storage-forensic-acquisitionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Cloud storage forensic acquisition involves collecting digital evidence from services like Google Drive, OneDrive, Dropbox, and Box through both API-based remote acquisition and local endpoint artifact analysis. Modern investigations must address the challenge that cloud-synced files may exist in multiple states: locally synchronized, cloud-only (on-demand), cached, and deleted. Endpoint devices that have synchronized with cloud storage contain a wealth of metadata about locally synced files, files present only in the cloud, and even deleted items recoverable from cache folders. API-based acquisition using service-specific APIs provides direct access to remote data with valid credentials and proper legal authorization.
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
import io
import os
import json
from datetime import datetime
class GoogleDriveForensicAcquisition:
"""Forensically acquire files and metadata from Google Drive via API."""
def __init__(self, credentials_path: str, output_dir: str):
self.creds = Credentials.from_authorized_user_file(credentials_path)
self.service = build("drive", "v3", credentials=self.creds)
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
self.acquisition_log = []
def list_all_files(self, include_trashed: bool = True) -> list:
"""List all files including trashed items."""
files = []
page_token = None
query = "" if include_trashed else "trashed = false"
while True:
results = self.service.files().list(
q=query,
pageSize=1000,
fields="nextPageToken, files(id, name, mimeType, size, "
"createdTime, modifiedTime, trashed, trashedTime, "
"owners, sharingUser, permissions, md5Checksum, "
"parents, webViewLink, driveId)",
pageToken=page_token
).execute()
files.extend(results.get("files", []))
page_token = results.get("nextPageToken")
if not page_token:
break
return files
def download_file(self, file_id: str, file_name: str, mime_type: str) -> str:
"""Download a file from Google Drive preserving forensic integrity."""
output_path = os.path.join(self.output_dir, file_name)
if mime_type.startswith("application/vnd.google-apps"):
export_formats = {
"application/vnd.google-apps.document": "application/pdf",
"application/vnd.google-apps.spreadsheet": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.google-apps.presentation": "application/pdf",
}
export_mime = export_formats.get(mime_type, "application/pdf")
request = self.service.files().export_media(fileId=file_id, mimeType=export_mime)
else:
request = self.service.files().get_media(fileId=file_id)
with io.FileIO(output_path, "wb") as fh:
downloader = MediaIoBaseDownload(fh, request)
done = False
while not done:
_, done = downloader.next_chunk()
self.acquisition_log.append({
"timestamp": datetime.utcnow().isoformat(),
"file_id": file_id,
"file_name": file_name,
"output_path": output_path,
"action": "downloaded"
})
return output_path
def get_activity_log(self, file_id: str) -> list:
"""Retrieve activity/revision history for a specific file."""
revisions = self.service.revisions().list(
fileId=file_id,
fields="revisions(id, modifiedTime, lastModifyingUser, size, md5Checksum)"
).execute()
return revisions.get("revisions", [])
def export_acquisition_report(self) -> str:
"""Export acquisition log for chain of custody documentation."""
report_path = os.path.join(self.output_dir, "acquisition_log.json")
with open(report_path, "w") as f:
json.dump({
"acquisition_start": self.acquisition_log[0]["timestamp"] if self.acquisition_log else None,
"acquisition_end": datetime.utcnow().isoformat(),
"total_files": len(self.acquisition_log),
"entries": self.acquisition_log
}, f, indent=2)
return report_path
import msal
import requests
import os
import json
from datetime import datetime
class OneDriveForensicAcquisition:
"""Forensically acquire files and metadata from OneDrive via Microsoft Graph API."""
def __init__(self, client_id: str, tenant_id: str, client_secret: str, output_dir: str):
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
authority = f"https://login.microsoftonline.com/{tenant_id}"
self.app = msal.ConfidentialClientApplication(
client_id, authority=authority, client_credential=client_secret
)
token_result = self.app.acquire_token_for_client(
scopes=["https://graph.microsoft.com/.default"]
)
self.access_token = token_result.get("access_token")
self.headers = {"Authorization": f"Bearer {self.access_token}"}
self.base_url = "https://graph.microsoft.com/v1.0"
def list_user_files(self, user_id: str) -> list:
"""List all files in user's OneDrive."""
url = f"{self.base_url}/users/{user_id}/drive/root/children"
files = []
while url:
response = requests.get(url, headers=self.headers)
data = response.json()
files.extend(data.get("value", []))
url = data.get("@odata.nextLink")
return files
def download_file(self, user_id: str, item_id: str, filename: str) -> str:
"""Download a file from OneDrive."""
url = f"{self.base_url}/users/{user_id}/drive/items/{item_id}/content"
response = requests.get(url, headers=self.headers, stream=True)
output_path = os.path.join(self.output_dir, filename)
with open(output_path, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
return output_path
def get_deleted_items(self, user_id: str) -> list:
"""Retrieve items from OneDrive recycle bin."""
url = f"{self.base_url}/users/{user_id}/drive/special/recyclebin/children"
response = requests.get(url, headers=self.headers)
return response.json().get("value", [])
# Collect all cloud storage artifacts using KAPE
kape.exe --tsource C: --tdest C:\Output\CloudArtifacts --target GoogleDrive,OneDrive,Dropbox,Box
# OneDrive artifacts
# %USERPROFILE%\AppData\Local\Microsoft\OneDrive\logs\
# %USERPROFILE%\AppData\Local\Microsoft\OneDrive\settings\
# %USERPROFILE%\OneDrive\
# Google Drive artifacts
# %USERPROFILE%\AppData\Local\Google\DriveFS\
# Contains metadata SQLite databases and cached files
# Dropbox artifacts
# %USERPROFILE%\AppData\Local\Dropbox\
# %USERPROFILE%\Dropbox\.dropbox.cache\
# Contains filecache.dbx (encrypted SQLite), host.dbx, config.dbx
import sqlite3
import os
def analyze_onedrive_sync_engine(db_path: str) -> list:
"""Analyze OneDrive SyncEngineDatabase for file metadata."""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Query for all tracked files including cloud-only items
cursor.execute("""
SELECT fileName, fileSize, lastChange,
resourceID, parentResourceID, eTag
FROM od_ClientFile_Records
ORDER BY lastChange DESC
""")
files = []
for row in cursor.fetchall():
files.append({
"filename": row[0],
"size": row[1],
"last_change": row[2],
"resource_id": row[3],
"parent_id": row[4],
"etag": row[5]
})
conn.close()
return files
| Service | Local Database | Cache Location | Log Files | |---------|---------------|----------------|-----------| | OneDrive | SyncEngineDatabase.db | %LOCALAPPDATA%\Microsoft\OneDrive\cache\ | %LOCALAPPDATA%\Microsoft\OneDrive\logs\ | | Google Drive | metadata_sqlite_db | %LOCALAPPDATA%\Google\DriveFS{account}\content_cache\ | %LOCALAPPDATA%\Google\DriveFS\Logs\ | | Dropbox | filecache.dbx (encrypted) | %APPDATA%\Dropbox.dropbox.cache\ | %APPDATA%\Dropbox\logs\ | | Box | sync_db | %LOCALAPPDATA%\Box\Box\cache\ | %LOCALAPPDATA%\Box\Box\logs\ |
$ python3 cloud_forensic_acquire.py --provider google-drive --auth /tokens/gdrive_token.json \
--user [email protected] --output /acquisition/gdrive
Cloud Storage Forensic Acquisition Tool v3.2
==============================================
Provider: Google Drive
Account: [email protected]
Start Time: 2024-01-19 08:00:15 UTC
Auth Method: Admin SDK (domain-wide delegation)
[+] Enumerating files...
Total files: 2,345
Total folders: 178
Shared with me: 456
Trashed items: 89 (included in acquisition)
Total size: 14.7 GB
[+] Acquiring file contents...
Downloaded: 2,345 / 2,345 [████████████████████████████████] 100%
Errors: 0
Elapsed: 18m 32s
[+] Acquiring metadata...
File metadata: 2,345 entries
Revision history: 8,912 revisions across 1,234 files
Sharing permissions: 3,456 permission entries
Activity log: 12,345 events
[+] Acquiring trashed items...
Recovered: 89 / 89 items (234 MB)
--- Acquisition Log ---
Timestamp (UTC) | Action | File | Size | SHA-256
2024-01-19 08:00:45 | Downloaded | /My Drive/Finance/Q4_Report.xlsm | 245 KB | 7a3b8c9d...
2024-01-19 08:00:46 | Downloaded | /My Drive/Finance/Budget_2024.xlsx | 1.2 MB | 8b4c9d0e...
...
2024-01-19 08:02:12 | Trash-Recovered | /Trash/employee_list_full.csv | 4.5 MB | 9c5d0e1f...
2024-01-19 08:02:13 | Trash-Recovered | /Trash/network_diagram_v3.vsdx | 2.1 MB | 0d6e1f2a...
2024-01-19 08:02:14 | Trash-Recovered | /Trash/credentials_backup.kdbx | 128 KB | 1e7f2a3b...
--- Sharing Analysis ---
Files Shared Externally:
/My Drive/Finance/Q4_Report.xlsm → [email protected] (2024-01-16 03:10 UTC)
/My Drive/HR/employee_list_full.csv → [email protected] (2024-01-16 03:12 UTC)
/My Drive/IT/network_diagram_v3.vsdx → anonymous (link sharing, 2024-01-16 03:15 UTC)
--- Revision History (Suspicious) ---
File: /My Drive/Finance/Q4_Report.xlsm
Rev 1: 2024-01-10 09:00:00 UTC (245 KB) - Original
Rev 2: 2024-01-15 14:35:00 UTC (248 KB) - Modified (macro added)
Rev 3: 2024-01-16 03:05:00 UTC (245 KB) - Reverted (macro removed - anti-forensics)
Acquisition Summary:
Files acquired: 2,345 (14.7 GB)
Trashed items: 89 (234 MB)
Revisions: 8,912
Chain of custody hash (full archive):
SHA-256: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Output directory: /acquisition/gdrive/
Acquisition log: /acquisition/gdrive/acquisition_log.csv
Completion Time: 2024-01-19 08:18:47 UTC
development
拋棄式 HTML mockup 比稿:產出 2 到 3 個設計立場不同的變體(密度 / 版式 / 強調軸,不是換色),各附取捨說明,最後給有立場的對比結論。適用:「畫個草圖」「比較 A 版 B 版」「先看方向再做」「給我看幾種做法」。要 production 元件或設計已定案時不適用。
tools
需求不明時的意圖萃取訪談:一次一題、每題附上自己的猜測、聽出「真正想要 vs 覺得應該要」,直到能預測使用者反應(約 95% 信心)才動工。適用:需求缺少對象 / 動機 / 成功標準 / 約束,或使用者點名「訪談我」「先確認一下」「我們確定嗎」。明確自足的指示、純資訊查詢、機械性操作不適用。
development
對非平凡決策啟動新鮮 context 對抗審查(找碴不背書),在修正還便宜的時候抓出錯誤方向。適用:高風險改動(production、資安敏感邏輯、不可逆操作)、不熟的程式碼、要宣稱「這樣是安全的 / 可行的」之前。機械性操作與一行修改不適用。
testing
Reference for writing and editing agent skills well — the vocabulary and principles that make a skill predictable. Consult when authoring, reviewing, or pruning a SKILL.md.