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("šļø 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)}.")
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:
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:
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)}")
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:
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")
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)
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")
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()