Base project
This commit is contained in:
commit
50faafb86a
12
Readme.md
Normal file
12
Readme.md
Normal file
@ -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
|
||||
...
|
110
client.py
Normal file
110
client.py
Normal file
@ -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
|
||||
|
73
dropbox/__client__.py
Normal file
73
dropbox/__client__.py
Normal file
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user