mirror of
https://github.com/mxrch/GHunt.git
synced 2025-12-22 13:47:07 +00:00
207 lines
8.3 KiB
Python
207 lines
8.3 KiB
Python
import os
|
|
|
|
from ghunt.helpers.utils import *
|
|
from ghunt.objects.base import DriveExtractedUser, GHuntCreds
|
|
from ghunt.apis.drive import DriveHttp
|
|
from ghunt.apis.clientauthconfig import ClientAuthConfigHttp
|
|
from ghunt import globals as gb
|
|
from ghunt.helpers import auth
|
|
from ghunt.helpers.drive import get_comments_from_file, get_users_from_file
|
|
from ghunt.knowledge import drive as drive_knownledge
|
|
|
|
import httpx
|
|
import inflection
|
|
import humanize
|
|
|
|
import inspect
|
|
from typing import *
|
|
from datetime import timedelta
|
|
|
|
|
|
def show_user(user: DriveExtractedUser):
|
|
if user.name:
|
|
print(f"Full name : {user.name}")
|
|
else:
|
|
gb.rc.print("Full name : -", style="bright_black")
|
|
print(f"Email : {user.email_address}")
|
|
if user.gaia_id:
|
|
print(f"Gaia ID : {user.gaia_id}")
|
|
else:
|
|
gb.rc.print("Gaia ID : -", style="bright_black")
|
|
if user.is_last_modifying_user:
|
|
print("[+] Last user to have modified the document !")
|
|
|
|
async def hunt(as_client: httpx.AsyncClient, file_id: str, json_file: bool=Path):
|
|
if not as_client:
|
|
as_client = get_httpx_client()
|
|
|
|
ghunt_creds = await auth.load_and_auth(as_client)
|
|
|
|
drive = DriveHttp(ghunt_creds)
|
|
file_found, file = await drive.get_file(as_client, file_id)
|
|
if not file_found:
|
|
print("[-] The file wasn't found.")
|
|
exit(os.EX_IOERR)
|
|
|
|
is_folder = file.mime_type == "application/vnd.google-apps.folder"
|
|
file_type = drive_knownledge.mime_types.get(file.mime_type)
|
|
|
|
#gb.rc.print(f"[+] {'Folder' if is_folder else 'File'} found !", style="sea_green3")
|
|
|
|
gb.rc.print("🗃️ Drive properties\n", style="deep_pink4")
|
|
|
|
print(f"Title : {file.title}")
|
|
print(f"{'Folder' if is_folder else 'File'} ID : {file.id}")
|
|
if file.md5_checksum:
|
|
print(f"MD5 Checksum : {file.md5_checksum}")
|
|
if file.file_size:
|
|
print(f"{'Folder' if is_folder else 'File'} size : {humanize.naturalsize(file.file_size)}")
|
|
|
|
if file_type:
|
|
gb.rc.print(f"\nType : {file_type} [italic]\[{file.mime_type}][/italic]")
|
|
else:
|
|
print(f"\nMime Type : {file.mime_type}")
|
|
if is_folder:
|
|
print(f"Folder link :\n=> {file.alternate_link}")
|
|
else:
|
|
print(f"Download link :\n=> {file.alternate_link}")
|
|
|
|
print(f"\n[+] Created date : {file.created_date.strftime('%Y/%m/%d %H:%M:%S (UTC)')}")
|
|
print(f"[+] Modified date : {file.modified_date.strftime('%Y/%m/%d %H:%M:%S (UTC)')}")
|
|
|
|
for perm in file.permissions:
|
|
if perm.id == "anyoneWithLink":
|
|
giving_roles = [perm.role.upper()] + [x.upper() for x in perm.additional_roles if x != perm.role]
|
|
print(f"\n[+] Sharing with link enabled ! Giving the role{'s' if len(giving_roles) > 1 else ''} {humanize_list(giving_roles)}.")
|
|
|
|
#print("\n[Source application]")
|
|
gb.rc.print("\n📱 Source application\n", style="deep_pink2")
|
|
brand_found = False
|
|
brand = None
|
|
if file.source_app_id:
|
|
print(f"App ID : {file.source_app_id}")
|
|
cac = ClientAuthConfigHttp(ghunt_creds)
|
|
brand_found, brand = await cac.get_brand(as_client, file.source_app_id)
|
|
if brand_found:
|
|
print(f"Name : {brand.display_name}")
|
|
if brand.home_page_url:
|
|
print(f"Home page : {brand.home_page_url}")
|
|
else:
|
|
gb.rc.print(f"Home page : [italic][bright_black]Not found.[/italic][/bright_black]")
|
|
else:
|
|
gb.rc.print("Not found.", style="italic")
|
|
else:
|
|
gb.rc.print("No source application.", style="italic")
|
|
|
|
if file.image_media_metadata.height and file.image_media_metadata.width:
|
|
#print("\n[Image metadata]")
|
|
gb.rc.print("\n📸 Image metadata\n", style="light_coral")
|
|
print(f"Height : {file.image_media_metadata.height}")
|
|
print(f"Width : {file.image_media_metadata.width}")
|
|
if isinstance((data := file.image_media_metadata.rotation), int):
|
|
print(f"Rotation : {data}")
|
|
|
|
if file.video_media_metadata.height and file.video_media_metadata.width:
|
|
#print("\n[Video metadata]")
|
|
gb.rc.print("\n📸 Video metadata\n", style="light_coral")
|
|
print(f"Height : {file.video_media_metadata.height}")
|
|
print(f"Width : {file.video_media_metadata.width}")
|
|
if (data := file.video_media_metadata.duration_millis):
|
|
duration = timedelta(milliseconds=int(file.video_media_metadata.duration_millis))
|
|
print(f"Duration : {humanize.precisedelta(duration)}")
|
|
|
|
#print("\n[Parents]")
|
|
gb.rc.print("\n📂 Parents\n", style="gold3")
|
|
if file.parents:
|
|
print(f"[+] Parents folders :")
|
|
for parent in file.parents:
|
|
print(f"- 📁 {parent.id}{' [Root folder]' if parent.is_root else ''}")
|
|
else:
|
|
gb.rc.print("No parent folder found.", style="italic")
|
|
|
|
if is_folder:
|
|
#print("\n[Items]")
|
|
gb.rc.print("\n🗃️ Items\n", style="gold3")
|
|
found, _, drive_childs = await drive.get_childs(as_client, file_id)
|
|
if found and drive_childs.items:
|
|
count = f"{x if (x := len(drive_childs.items)) < 1000 else '>= 1000'}"
|
|
print(f"[+] {count} items found inside this folder !")
|
|
else:
|
|
gb.rc.print("No items found.", style="italic")
|
|
|
|
#print("\n[Users]")
|
|
gb.rc.print("\n👪 Users\n", style="dark_orange")
|
|
users = get_users_from_file(file)
|
|
if (owners := [x for x in users if x.role == "owner"]):
|
|
print(f"-> 👤 Owner{'s' if len(owners) > 1 else ''}")
|
|
for user in owners:
|
|
show_user(user)
|
|
|
|
if (writers := [x for x in users if x.role == "writer"]):
|
|
print(f"\n-> 👤 Writer{'s' if len(writers) > 1 else ''}")
|
|
for user in writers:
|
|
show_user(user)
|
|
|
|
if (commenters := [x for x in users if x.role == "commenter"]):
|
|
print(f"\n-> 👤 Commenter{'s' if len(commenters) > 1 else ''}")
|
|
for user in commenters:
|
|
show_user(user)
|
|
|
|
if (readers := [x for x in users if x.role == "reader"]):
|
|
print(f"\n-> 👤 Reader{'s' if len(readers) > 1 else ''}")
|
|
for user in readers:
|
|
show_user(user)
|
|
|
|
if (nones := [x for x in users if x.role == "none"]):
|
|
print(f"\n-> 👤 User{'s' if len(nones) > 1 else ''} with no right")
|
|
for user in nones:
|
|
show_user(user)
|
|
|
|
#print("[Comments]")
|
|
gb.rc.print("\n🗣️ Comments\n", style="plum2")
|
|
comments_found, _, drive_comments = await drive.get_comments(as_client, file_id)
|
|
if comments_found and drive_comments.items:
|
|
authors = get_comments_from_file(drive_comments)
|
|
if len(drive_comments.items) > 20:
|
|
print(f"[+] Authors ({len(authors)} found, showing the top 20) :")
|
|
else:
|
|
print("[+] Authors :")
|
|
for _, author in authors[:20]:
|
|
print(f"- 🙋 {author['name']} ({author['count']} comment{'s' if author['count'] > 1 else ''})")
|
|
else:
|
|
gb.rc.print("No comments.", style="italic")
|
|
|
|
#print("\n[Capabilities]")
|
|
gb.rc.print("\n🧙 Capabilities\n", style="dodger_blue1")
|
|
capabilities = sorted([k for k,v in inspect.getmembers(file.capabilities) if v and not k.startswith("_")])
|
|
if is_folder:
|
|
if capabilities == drive_knownledge.default_folder_capabilities:
|
|
print("[-] You don't have special permissions against this folder.")
|
|
else:
|
|
print(f"[+] You have special permissions against this folder ! ✨")
|
|
for cap in capabilities:
|
|
print(f"- {inflection.humanize(cap)}")
|
|
else:
|
|
if capabilities == drive_knownledge.default_file_capabilities:
|
|
print("[-] You don't have special permissions against this file.")
|
|
else:
|
|
print(f"[+] You have special permissions against this file ! ✨")
|
|
for cap in capabilities:
|
|
print(f"- {inflection.humanize(cap)}")
|
|
|
|
if json_file:
|
|
json_results = {
|
|
"file": file if file_found else None,
|
|
"source_app": brand if brand_found else None,
|
|
"users": users,
|
|
"comments": drive_comments if comments_found else None
|
|
}
|
|
|
|
import json
|
|
from ghunt.objects.encoders import GHuntEncoder;
|
|
with open(json_file, "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(json_results, cls=GHuntEncoder, indent=4))
|
|
gb.rc.print(f"\n[+] JSON output wrote to {json_file} !", style="italic")
|
|
|
|
await as_client.aclose()
|