Skip to content

Commit

Permalink
Geolocation module!
Browse files Browse the repository at this point in the history
  • Loading branch information
mxrch committed Jun 6, 2024
1 parent d6dca8d commit ca2ddf5
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 19 deletions.
67 changes: 67 additions & 0 deletions ghunt/apis/geolocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from ghunt.objects.base import GHuntCreds
from ghunt.errors import *
import ghunt.globals as gb
from ghunt.objects.apis import GAPI
from ghunt.parsers.geolocate import GeolocationResponse

import httpx

from typing import *
import inspect
import json


class GeolocationHttp(GAPI):
def __init__(self, creds: GHuntCreds, headers: Dict[str, str] = {}):
super().__init__()

if not headers:
headers = gb.config.headers

base_headers = {}

headers = {**headers, **base_headers}

self.hostname = "www.googleapis.com"
self.scheme = "https"

self.authentication_mode = None # sapisidhash, cookies_only, oauth or None
self.require_key = "geolocation" # key name, or None

self._load_api(creds, headers)

async def geolocate(self, as_client: httpx.AsyncClient, bssid: str, body: dict) -> Tuple[bool, GeolocationResponse]:
endpoint_name = inspect.currentframe().f_code.co_name

verb = "POST"
base_url = f"/geolocation/v1/geolocate"
data_type = "json" # json, data or None

if bssid:
payload = {
"considerIp": False,
"wifiAccessPoints": [
{
"macAddress": "00:25:9c:cf:1c:ad"
},
{
"macAddress": bssid
},
]
}
else:
payload = body

self._load_endpoint(endpoint_name)
req = await self._query(as_client, verb, endpoint_name, base_url, None, payload, data_type)

# Parsing
data = json.loads(req.text)

resp = GeolocationResponse()
if "error" in data:
return False, resp

resp._scrape(data)

return True, resp
47 changes: 37 additions & 10 deletions ghunt/cli.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
from rich_argparse import RichHelpFormatter

import argparse
from typing import *
import sys
from pathlib import Path


def parse_and_run():
parser = argparse.ArgumentParser()
RichHelpFormatter.styles["argparse.groups"] = "misty_rose1"
RichHelpFormatter.styles["argparse.metavar"] = "light_cyan1"
RichHelpFormatter.styles["argparse.args"] = "light_steel_blue1"
RichHelpFormatter.styles["argparse.prog"] = "light_pink1 bold italic"


parser = argparse.ArgumentParser(formatter_class=RichHelpFormatter)
subparsers = parser.add_subparsers(dest="module")

### Login module
parser_login = subparsers.add_parser('login', help="Authenticate GHunt to Google.")
parser_login = subparsers.add_parser('login', help="Authenticate GHunt to Google.", formatter_class=RichHelpFormatter)
parser_login.add_argument('--clean', action='store_true', help="Clear credentials local file.")

### Email module
parser_email = subparsers.add_parser('email', help="Get information on an email address.")
parser_email = subparsers.add_parser('email', help="Get information on an email address.", formatter_class=RichHelpFormatter)
parser_email.add_argument("email_address")
parser_email.add_argument('--json', type=str, help="File to write the JSON output to.")
parser_email.add_argument('--json', type=Path, help="File to write the JSON output to.")

### Gaia module
parser_gaia = subparsers.add_parser('gaia', help="Get information on a Gaia ID.")
parser_gaia = subparsers.add_parser('gaia', help="Get information on a Gaia ID.", formatter_class=RichHelpFormatter)
parser_gaia.add_argument("gaia_id")
parser_gaia.add_argument('--json', type=str, help="File to write the JSON output to.")
parser_gaia.add_argument('--json', type=Path, help="File to write the JSON output to.")

### Drive module
parser_drive = subparsers.add_parser('drive', help="Get information on a Drive file or folder.")
parser_drive = subparsers.add_parser('drive', help="Get information on a Drive file or folder.", formatter_class=RichHelpFormatter)
parser_drive.add_argument("file_id", help="Example: 1N__vVu4c9fCt4EHxfthUNzVOs_tp8l6tHcMBnpOZv_M")
parser_drive.add_argument('--json', type=str, help="File to write the JSON output to.")
parser_drive.add_argument('--json', type=Path, help="File to write the JSON output to.")

### Geolocate module
parser_geolocate = subparsers.add_parser('geolocate', help="Geolocate a BSSID.", formatter_class=RichHelpFormatter)
geolocate_group = parser_geolocate.add_mutually_exclusive_group(required=True)
geolocate_group.add_argument("-b", "--bssid", help="Example: 30:86:2d:c4:29:d0")
geolocate_group.add_argument("-f", "--file", type=Path, help="File containing a raw request body, useful to put many BSSIDs. ([italic light_steel_blue1][link=https://developers.google.com/maps/documentation/geolocation/requests-geolocation?#sample-requests]Reference format[/link][/italic light_steel_blue1])")
parser_geolocate.add_argument('--json', type=Path, help="File to write the JSON output to.")

### Parsing
args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])
args = None
if not sys.argv[1:]:
parser.parse_args(["--help"])
else:
for mod in ["email", "gaia", "drive", "geolocate"]:
if sys.argv[1] == mod and not sys.argv[2:]:
parser.parse_args([mod, "--help"])

args = parser.parse_args(args)
process_args(args)

def process_args(args: argparse.Namespace):
Expand All @@ -44,4 +68,7 @@ def process_args(args: argparse.Namespace):
asyncio.run(gaia.hunt(None, args.gaia_id, args.json))
case "drive":
from ghunt.modules import drive
asyncio.run(drive.hunt(None, args.file_id, args.json))
asyncio.run(drive.hunt(None, args.file_id, args.json))
case "geolocate":
from ghunt.modules import geolocate
asyncio.run(geolocate.main(None, args.bssid, args.file, args.json))
2 changes: 1 addition & 1 deletion ghunt/helpers/banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def show_banner():
[/][green]Y88b d88P [/][blue]888 888[/][green] Y88b 888[/][yellow] 888 888[/][red] Y88b.
[/][green] "Y8888P88 [/][blue]888 888[/][green] "Y88888[/][yellow] 888 888[/][red] "Y888[/red] v2
[bold]By: mxrch (🐦 [deep_sky_blue1]@mxrchreborn[/deep_sky_blue1])
[bold]By: mxrch (🐦 [deep_sky_blue1][link=https://x.com/mxrchreborn]@mxrchreborn[/link][/deep_sky_blue1])
[indian_red1]Support my work on GitHub Sponsors ! 💖[/indian_red1][/bold]
"""

Expand Down
3 changes: 2 additions & 1 deletion ghunt/knowledge/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"apis_explorer": {"key": "AIzaSyAa8yy0GdcGPHdtD083HiGGx_S0vMPScDM", "origin": "https://explorer.apis.google.com"},
"calendar": {"key": "AIzaSyBNlYH01_9Hc5S1J9vuFmu2nUqBZJNAXxs", "origin": "https://calendar.google.com"},
"youtubei": {"key": "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w", "origin": "https://youtube.com"},
"drive": {"key": "AIzaSyD_InbmSFufIEps5UAt2NmB_3LvBH3Sz_8", "origin": "https://drive.google.com"}
"drive": {"key": "AIzaSyD_InbmSFufIEps5UAt2NmB_3LvBH3Sz_8", "origin": "https://drive.google.com"},
"geolocation": {"key": "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg", "origin": "https://geo-devrel-javascript-samples.web.app"} # https://developers.google.com/maps/documentation/javascript/overview
}
2 changes: 1 addition & 1 deletion ghunt/modules/drive.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def show_user(user: DriveExtractedUser):
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=None):
async def hunt(as_client: httpx.AsyncClient, file_id: str, json_file: bool=Path):
if not as_client:
as_client = get_httpx_client()

Expand Down
3 changes: 2 additions & 1 deletion ghunt/modules/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import httpx

from typing import *
from pathlib import Path


async def hunt(as_client: httpx.AsyncClient, email_address: str, json_file: bool=None):
async def hunt(as_client: httpx.AsyncClient, email_address: str, json_file: Path=None):
if not as_client:
as_client = get_httpx_client()

Expand Down
3 changes: 2 additions & 1 deletion ghunt/modules/gaia.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import httpx

from typing import *
from pathlib import Path


async def hunt(as_client: httpx.AsyncClient, gaia_id: str, json_file: bool=None):
async def hunt(as_client: httpx.AsyncClient, gaia_id: str, json_file: Path=None):
if not as_client:
as_client = get_httpx_client()

Expand Down
60 changes: 60 additions & 0 deletions ghunt/modules/geolocate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from ghunt import globals as gb
from ghunt.helpers.utils import get_httpx_client
from ghunt.apis.geolocation import GeolocationHttp
from ghunt.helpers import auth

import httpx
from geopy.geocoders import Nominatim

from typing import *
from pathlib import Path
import json


async def main(as_client: httpx.AsyncClient, bssid: str, input_file: Path, json_file: Path=None):
# Verifying args
body = None
if input_file:
if not input_file.exists():
exit(f"[-] The input file \"{input_file}\" doesn't exist.")
with open(input_file, "r", encoding="utf-8") as f:
try:
body = json.load(f)
except json.JSONDecodeError:
exit("[-] The input file is not a valid JSON file.")

if not as_client:
as_client = get_httpx_client()

ghunt_creds = await auth.load_and_auth(as_client)

geo_api = GeolocationHttp(ghunt_creds)
found, resp = await geo_api.geolocate(as_client, bssid=bssid, body=body)
if not found:
exit("[-] The location wasn't found.")

geolocator = Nominatim(user_agent="nominatim")
location = geolocator.reverse(f"{resp.location.latitude}, {resp.location.longitude}", timeout=10)
raw_address = location.raw['address']
address = location.address

gb.rc.print("📍 Location found !\n", style="plum2")
gb.rc.print(f"🛣️ [italic]Accuracy : {resp.accuracy} meters[/italic]\n")
gb.rc.print(f"Latitude : {resp.location.latitude}", style="bold")
gb.rc.print(f"Longitude : {resp.location.longitude}\n", style="bold")
gb.rc.print(f"🏠 Estimated address : {address}\n")
gb.rc.print(f"🗺️ [italic][link=https://www.google.com/maps/search/?q={resp.location.latitude},{resp.location.longitude}]Open in Google Maps[/link][/italic]\n", style=f"cornflower_blue")

if json_file:
from ghunt.objects.encoders import GHuntEncoder;
with open(json_file, "w", encoding="utf-8") as f:
f.write(json.dumps({
"accuracy": resp.accuracy,
"latitude": resp.location.latitude,
"longitude": resp.location.longitude,
"address": raw_address,
"pretty_address": address
}, cls=GHuntEncoder, indent=4))
gb.rc.print(f"[+] JSON output wrote to {json_file} !", style="italic")

await as_client.aclose()
17 changes: 17 additions & 0 deletions ghunt/parsers/geolocate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from ghunt.objects.apis import Parser
from ghunt.objects.base import Position

from typing import *


class GeolocationResponse(Parser):
def __init__(self):
self.accuracy: int = 0
self.location: Position = Position()

def _scrape(self, base_model_data: dict[str, any]):
self.accuracy = base_model_data.get("accuracy")

location = base_model_data.get("location")
self.location.longitude = location.get("lng")
self.location.latitude = location.get("lat")
4 changes: 2 additions & 2 deletions ghunt/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
metadata = {
"version": "2.1.6",
"name": "BlackHat Edition"
"version": "2.2.0",
"name": "Wardriving Edition"
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "ghunt"
version = "2.1.6"
version = "2.2.0"
authors = [
{name = "mxrch", email = "[email protected]"},
]
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ humanize==4.4.0
inflection==0.5.1
jsonpickle==2.2.0
packaging==23.0
jsonpickle==2.2.0
jsonpickle==2.2.0
rich_argparse==1.5.0

0 comments on commit ca2ddf5

Please sign in to comment.