From 50faafb86abf422801bf0a83ad99f997b63ec4e1 Mon Sep 17 00:00:00 2001 From: Giulio Date: Mon, 15 Mar 2021 13:54:18 +0100 Subject: [PATCH] Base project --- Readme.md | 12 +++++ client.py | 110 ++++++++++++++++++++++++++++++++++++++++++ dropbox/__client__.py | 73 ++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 Readme.md create mode 100644 client.py create mode 100644 dropbox/__client__.py diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..4243459 --- /dev/null +++ b/Readme.md @@ -0,0 +1,12 @@ +# Basic client for Dropbox Business +This client has been written for pentesting and offensive purposes. With a Dropbox Enterprise API Token, it is possible to list all the users and download and edit every file and folder. The client also allows to list a user's session with all the details and also to get recent activity. A 'tree' functionality returns the full file and folder structure allowing for easy grepping. + +## Dependencies + +`python3-deepmerge` + +## Configuration +Add the `token` in the client.py file. + +## Usage +... diff --git a/client.py b/client.py new file mode 100644 index 0000000..5d440b2 --- /dev/null +++ b/client.py @@ -0,0 +1,110 @@ +import dropbox +import sys +import json +from pprint import pprint +from hashlib import sha1 +import sqlite3 + +# dropbox organization api key +token = "api key" +db = "{}.sqlite".format(sha1(token.encode("ascii")).hexdigest()) + +client = dropbox.Dropbox(token) + +conn = sqlite3.connect(db) +cur = conn.cursor() + +def init_db(conn, token): + conn.execute('CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, team_member_id TEXT, email TEXT, external_id TEXT, name TEXT, groups TEXT)') + members = client.members_list() + for member in members["members"]: + if "external_id" not in member["profile"]: + member["profile"]["external_id"] = None + cur.execute("INSERT INTO users (id, team_member_id, email, external_id, name, groups) VALUES (?, ?, ?, ?, ?, ?)", (member["profile"]["account_id"], member["profile"]["team_member_id"], member["profile"]["email"], member["profile"]["external_id"], member["profile"]["name"]["display_name"], json.dumps(member["profile"]["groups"]))) + conn.commit() + +def get_team_member_id(cur, email): + return cur.execute("SELECT team_member_id FROM users where email = ?", (email,)).fetchone() + +def get_account_id(cur, email): + return cur.execute("SELECT id FROM users where email = ?", (email,)).fetchone() + +def search_account(cur, query): + query = "%{}%".format(query.replace(" ", "%")) + return cur.execute("SELECT email,name FROM users where email like ? or name like ? or external_id like ? or id like ? or team_member_id like ?", (query, query, query, query, query)).fetchall() + +def get_ids(cur, email): + id = get_account_id(conn, user) + team_member_id = get_team_member_id(conn, user) + if len(id) > 0 and len(team_member_id) > 0: + id = id[0] + team_member_id = team_member_id[0] + return id, team_member_id + else: + return None, None +try: + cur.execute("SELECT count(id) FROM users") +except: + init_db(conn, token) + + +if len(sys.argv) > 1: + cmd = sys.argv[1] + +if cmd == "search": + query = sys.argv[2] + results = search_account(conn, query) + if len(results) > 0: + for result in results: + print("{}\t\t{}".format(result[0], result[1])) +elif cmd == "info": + if len(sys.argv) == 2: + pprint(client.get_info()) + elif len(sys.argv) == 3: + user = user = sys.argv[2] + id, team_member_id = get_ids(cur, user) + pprint(client.get_current_account(team_member_id)) + else: + user = user = sys.argv[2] + id, team_member_id = get_ids(cur, user) + operation = sys.argv[3] + if operation == "activity": + activity = client.get_events(id) + for event in activity["events"]: + for assets in event["assets"]: + print(assets["path"]["contextual"]) +elif cmd == "file": + user = sys.argv[2] + operation = sys.argv[3] + path = sys.argv[4] + id, team_member_id = get_ids(cur, user) + if operation == "ls": + ls = client.list_folder(team_member_id, path, False) + for file in ls["entries"]: + print(file["path_display"]) + elif operation == "tree": + tree = client.list_folder(team_member_id, path, True) + for file in tree["entries"]: + print(file["path_display"]) + elif operation == "download": + file = client.download(team_member_id, path) + filename = path.split('/')[-1] + with open(filename, "wb") as f: + f.write(file) + elif operation == "info": + print(client.get_metadata(team_member_id, path)) + +elif cmd == "activity": + # Returns the file activity of the last 10 days + # This call wants the dbid (Dropbox ID) instead of the dbmid (Dropbox Team Member ID) + user = sys.argv[2] + id, team_member_id = get_ids(cur, user) + activity = client.get_events(id) + pprint(activity) + for event in activity["events"]: + for assets in event["assets"]: + try: + print(assets["path"]["contextual"]) + except: + pass + diff --git a/dropbox/__client__.py b/dropbox/__client__.py new file mode 100644 index 0000000..9ba487c --- /dev/null +++ b/dropbox/__client__.py @@ -0,0 +1,73 @@ +import requests +import logging +import json +from datetime import datetime, timedelta +from deepmerge import always_merger +from pprint import pprint + +BASE = 'https://api.dropbox.com/2' + +class Dropbox: + def __init__(self, token, proxy=None): + self.token = token + if proxy: + self.proxies = {'http': "socks5://{}".format(proxy)} + else: + self.proxies = {} + info = self.get_info() + if info: + self.name = info["name"] + self.team_id = info["team_id"] + self.num_users = info["num_provisioned_users"] + else: + logging.error("Wrong token") + + + def call(self, endpoint, args=None, member=None, nested=False, cursor=True, base=BASE, params=None): + if params: + params = {"arg": json.dumps(params)} + r = requests.post('{}/{}'.format(base, endpoint), headers={"Authorization": "Bearer {}".format(self.token), "Dropbox-API-Select-User": member}, json=args, proxies=self.proxies, params=params) + if r.status_code == 200: + if 'json' not in r.headers['content-type']: + return r.content + data = r.json() + result = data + while nested == False and "has_more" in data and data["has_more"] == True and cursor == True: + args = {"cursor": data["cursor"]} + data = self.call("{}/continue".format(endpoint), args, member, True) + result = always_merger.merge(result, data) + return result + else: + logging.debug(r.text) + print(r.text) + return False + + def get_info(self): + return self.call("team/get_info") + + def members_list(self): + return self.call("team/members/list") + + def list_member_devices(self, team_member_id): + return self.call("team/devices/list_member_devices", {"team_member_id": team_member_id, "include_web_sessions": True, "include_desktop_clients": True, "include_mobile_clients": True}) + + def get_current_account(self, team_member_id): + return self.call("users/get_current_account", None, team_member_id) + + def list_folder(self, team_member_id, path, recursive=False): + return self.call("files/list_folder", {"path": path, "recursive": recursive}, team_member_id) + + def list_namespaces(self): + return self.call("team/namespaces/list") + + def get_events(self, team_member_id): + end = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") + start = (datetime.now() - timedelta(days=10)).strftime("%Y-%m-%dT%H:%M:%SZ") + return self.call("team_log/get_events", {"limit": 100, "account_id": team_member_id, "time": {"start_time": start, "end_time": end}}) + + def download(self, team_member_id, path): + return self.call("files/download", params={"path": path}, member=team_member_id, base="https://content.dropboxapi.com/2") + + def get_metadata(self, team_member_id, path): + return self.call("files/get_metadata", {"path": path}, member=team_member_id) +