def purgeService(service): if scripts.checkAPINoPrint(): header = {"Accept": "application/json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) input("Request to be made: " + "https://api.fastly.com/service/" + str(service) + "/purge_all" + "\nPress enter to continue...") r = requests.post("https://api.fastly.com/service/" + str(service) + "/purge_all", headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: pprint.pprint(r.json()) input("Press ENTER to continue...") else: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def purgeURL(url): if scripts.checkAPINoPrint(): header = {"Accept": "application/json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) header.update({"Fastly-Soft-Purge": "1"}) url = str(input("Purge URL: ")) while "Not a valid response.": reply = str(input("Correct URL [Y/n]: ")).lower().strip() if reply == 'y': break elif reply == 'n': scripts.clear() purgeMenu() break r = requests.request("PURGE", str(url), headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: pprint.pprint(r.json()) input("Press ENTER to continue...") else: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def disableWAF(): print( scripts.bcolors.FAIL + scripts.bcolors.UNDERLINE + "EMERGENCY DISABLE: THIS IS TO BE USED IN AN EMERGENCY ONLY\n(Requires Superuser permissions)" + scripts.bcolors.ENDC + scripts.bcolors.ENDC) if scripts.checkAPINoPrint(): dfObj = listWAFIDsNoPrompt() try: inVar = int( input("\n\nEnter index of WAF to display [Enter to exit]: ")) print(str(dfObj['WAF ID'].iloc[inVar])) except: e = input( "Not a valid number. Press enter to continue or E to exit...") if e.strip(' ').lower() == 'e': scripts.clear() scripts.WAFMenu() scripts.clear() disableWAF() print(scripts.bcolors.WARNING + scripts.bcolors.UNDERLINE + "EMERGENCY DISABLE: THIS IS TO BE USED IN AN EMERGENCY ONLY" + scripts.bcolors.ENDC + scripts.bcolors.ENDC) while "Not a valid response.": reply = str( input("Request: https://api.fastly.com/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/disable\nCorrect service " + str(dfObj['Name'].iloc[inVar]) + " [Y/n]: ")).lower().strip() if reply[0] == 'y': break if reply[0] == 'n': scripts.clear() disableWAF() break header = {"Accept": "application/vnd.api+json"} header.update({"Content-Type": "application/vnd.api+json"}) header.update({"Fastly-Key": scripts.getKeyFromConfig()}) r = requests.patch("https://api.fastly.com/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/disable", headers=header) if r.status_code == 202: print(scripts.bcolors.OKGREEN + "Disabled WAF" + scripts.bcolors.ENDC) pprint.pprint(r.json()['data']) input("Press ENTER to return to menu...") else: input(scripts.bcolors.WARNING + "Error with request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def listWAFRules(): pandas.set_option('display.max_rows', 20000) if scripts.checkAPINoPrint(): header = {"Accept": "application/vnd.api+json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) r = requests.get("https://api.fastly.com/wafs/rules", headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: pages = int(json_normalize(r.json()['meta'])['total_pages']) df_all_rows = pandas.DataFrame() for x in range(pages): x += 1 print("Parsing page " + str(x) + " of " + str(pages) + " total pages") r = requests.get( "https://api.fastly.com/wafs/rules?page[number]=" + str(x), headers=header) with scripts.utils.DataFrameFromDict(r.json()['data']) as df: df['ID'] = df['id'] df['Rule ID'] = df['attributes.rule_id'] df['Description'] = df['attributes.message'] df['Severity'] = df['attributes.severity'] pandas.set_option('display.max_colwidth', -1) df_all_rows = df_all_rows.append(df, ignore_index=True) df_all_rows.reset_index(drop=True, inplace=True) filter = input("Enter filter for rules [all]: ") if filter != "": print(scripts.bcolors.OKBLUE + scripts.bcolors.UNDERLINE + "FASTLY WAF RULES" + scripts.bcolors.ENDC + scripts.bcolors.ENDC) mask = df_all_rows.apply( lambda row: row.astype(str).str.contains( str(filter), case=False, na=False, regex=False).any(), axis=1) pydoc.pager(str(df_all_rows[mask])) else: print(scripts.bcolors.OKBLUE + scripts.bcolors.UNDERLINE + "FASTLY WAF RULES" + scripts.bcolors.ENDC + scripts.bcolors.ENDC) pydoc.pager(str(df_all_rows)) input("Press ENTER to return to menu...") else: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def getRuleByID(ruleid): header={"Accept":"application/vnd.api+json"} header.update({"Fastly-Key":scripts.getKeyFromConfig()}) r=requests.get("https://api.fastly.com/wafs/rules/" + ruleid,headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: with scripts.utils.DataFrameFromDict(r.json()['data']) as df: df['ID'] = df['id'] df['Rule ID'] = df['attributes.rule_id'] df['Description'] = df['attributes.message'] df['Severity'] = df['attributes.severity'] # print(df) return df else: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC)
def getDetails(df): pandas.set_option('display.max_colwidth', -1) print(scripts.bcolors.OKBLUE + scripts.bcolors.UNDERLINE + "FASTLY SERVICES" + scripts.bcolors.ENDC + scripts.bcolors.ENDC) print(df) try: inVar = int(input("\n\nEnter index of service to view details: ")) print(str(df['Name'].iloc[inVar])) print(str(df['ID'].iloc[inVar])) except: e = input( "Not a valid number. Press enter to continue or E to exit...") if e.strip(' ').lower() == 'e': clear() mainMenu() clear() getDetails(df) header = {"Accept": "application/json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) r = requests.get("https://api.fastly.com/service/" + str(df['ID'].iloc[inVar]) + "/details", headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: services = r.json() # pprint.pprint(services['versions']) print("Active/Deployed Version: " + str(services['active_version']['number'])) with DataFrameFromDict(services['versions']) as df2: df2['Version'] = df2['number'] df2['Created On'] = df2['created_at'] df2['Updated On'] = df2['updated_at'] df2['Locked'] = df2['locked'] df2['Staging'] = df2['staging'] df2['Testing'] = df2['testing'] df2['Comment'] = df2['comment'] print(df2.to_string(index=False)) while "Not a valid response.": reply = str(input("View another service [Y/n]: ")).lower().strip() if reply == 'y': clear() getDetails(df) if reply == 'n': mainMenu()
def listServicesNoPrint(): if scripts.checkAPINoPrint(): print("This may take a while. Enumerating services...") header = {"Accept": "application/json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) r = requests.get("https://api.fastly.com/service", headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: services = r.json() with DataFrameFromDict(services) as df: df['ID'] = df['id'] df['Name'] = df['name'] df['Version'] = df['version'] df.insert(3, 'Domain(s)', None) # print(df) for x in range(len(df.index)): if not df['Version'].isnull().iloc[x]: id = str(df['ID'].iloc[x]) # print("https://api.fastly.com/service/" + id + "/domain") r2 = requests.get("https://api.fastly.com/service/" + id + "/domain", headers=header) # pprint.pprint(r2.json()) returns = json_normalize(r2.json()) if r2.json(): returnlist = returns['name'].tolist() df.at[x, 'Domain(s)'] = ", ".join(returnlist) pandas.set_option('display.max_colwidth', -1) return df else: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def listPublicIPs(): if scripts.checkAPINoPrint(): header = {"Accept": "application/json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) r = requests.get("https://api.fastly.com/public-ip-list", headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 200: pprint.pprint(r.json()) input("Press ENTER to continue...") else: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def enableWAF(): print( scripts.bcolors.WARNING + scripts.bcolors.UNDERLINE + "EMERGENCY ENABLE: THIS IS TO BE USED IN AN EMERGENCY ONLY (only works on emergency disabled WAF)\n(Requires Superuser permissions)" + scripts.bcolors.ENDC + scripts.bcolors.ENDC) if scripts.checkAPINoPrint(): dfObj = listWAFIDsNoPrompt() try: inVar = int(input("\n\nEnter index of WAF to display: ")) str(dfObj['WAF ID'].iloc[inVar]) except: e = input( "Not a valid number. Press enter to continue or E to exit...") if e.strip(' ').lower() == 'e': scripts.clear() scripts.WAFMenu() scripts.clear() enableWAF() header = {"Accept": "application/vnd.api+json"} header.update({"Content-Type": "application/vnd.api+json"}) header.update({"Fastly-Key": scripts.getKeyFromConfig()}) r = requests.patch("https://api.fastly.com/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/enable", headers=header) if r.status_code == 202: print(scripts.bcolors.OKGREEN + "Enabled WAF" + scripts.bcolors.ENDC) pprint.pprint(r.json()['data']) input("Press ENTER to return to menu...") else: input(scripts.bcolors.WARNING + "Error with request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def modifyRules(): pandas.set_option('display.max_rows', 1000) if scripts.checkAPINoPrint(): dfObj = scripts.WAF.listWAFIDsNoPrompt() # print(dfObj) try: inVar = int(input("\n\nEnter index of WAF to modify: ")) except: e = input( "Not a valid number. Press enter to continue or E to exit...") if e.strip(' ').lower() == 'e': scripts.clear() scripts.WAFMenu() scripts.clear() modifyRules() ruleids = str( input( "Enter rule ID's to modify (Example: 1010010,931100,931110): ") ).lower().strip() action = str( input("Enter action to perform on rules (disabled, log, block):") ).lower().strip() ruleList = ruleids.split(",") body = {} datatemp = {} attributes = {} for rid in ruleList: print(str(rid)) wrid = str(dfObj['WAF ID'].iloc[inVar]) + "-" + str(rid) datatemp.update({"id": wrid}) datatemp.update({"type": "rule_status"}) attributes.update({"status": action}) datatemp.update({"attributes": attributes}) body.update({"data": datatemp}) #print(json.dumps(data)) header = {"Accept": "application/vnd.api+json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) #r=requests.get("https://api.fastly.com/service/" + str(dfObj['Service ID'].iloc[inVar]) + "/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/rules/" + str(rid) + "/rule_status",headers=header) #pprint.pprint(r.json()['data']) header.update({"Content-Type": "application/vnd.api+json"}) #print(json.dumps(header)) #print(json.dumps(body)) #print("https://api.fastly.com/service/" + str(dfObj['Service ID'].iloc[inVar]) + "/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/rules/" + str(row['Num ID']) + "/rule_status") r = requests.patch("https://api.fastly.com/service/" + str(dfObj['Service ID'].iloc[inVar]) + "/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/rules/" + str(rid) + "/rule_status", data=str(json.dumps(body)), headers=header) if r.status_code == 200: pprint.pprint(r.json()['data']) else: print(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + scripts.bcolors.ENDC) while "Not a valid response.": reply = str(input("Modify another set [Y/n]: ")).lower().strip() if reply == 'y': modifyRules() if reply == 'n': scripts.WAFMenu() else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def OWASP(): pandas.set_option('display.max_rows', 1000) if scripts.checkAPINoPrint(): dfObj = listWAFIDsNoPrompt() # print(dfObj) try: inVar = int(input("\n\nEnter index of WAF to display: ")) print("https://api.fastly.com/service/" + str(dfObj['Service ID'].iloc[inVar]) + "/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/owasp") except: e = input("Not a valid number. Press enter to continue or E to exit...") if e.lower() == 'e': return scripts.clear() OWASP() header={"Accept":"application/vnd.api+json"} header.update({"Fastly-Key":scripts.getKeyFromConfig()}) r=requests.get("https://api.fastly.com/service/" + str(dfObj['Service ID'].iloc[inVar]) + "/wafs/" + str(dfObj['WAF ID'].iloc[inVar]) + "/owasp",headers=header) # pprint.pprint(r.json()['data']) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 404: # * no waf for that service pass elif r.status_code == 200: with scripts.utils.DataFrameFromDict(r.json()['data']) as df: owaspid = str(df['id'].iloc[0]) df['Allowed HTTP Versions'] = df['attributes.allowed_http_versions'] df['Allowed Methods'] = df['attributes.allowed_methods'] df['Allowed Request Content Type'] = df['attributes.allowed_request_content_type'] df['Allowed Request Content Type Charset'] = df['attributes.allowed_request_content_type_charset'] df['Arg Length'] = df['attributes.arg_length'] df['Arg Namr Length'] = df['attributes.arg_name_length'] df['Combined File Sizes'] = df['attributes.combined_file_sizes'] df['Created At'] = df['attributes.created_at'] df['Critical Anomaly Score'] = df['attributes.critical_anomaly_score'] df['CRS Validate UTF8 Encoding'] = df['attributes.crs_validate_utf8_encoding'] df['Error Anomaly Score'] = df['attributes.error_anomaly_score'] df['High Risk Country Codes'] = df['attributes.high_risk_country_codes'] df['HTTP Violation Score Threshold'] = df['attributes.http_violation_score_threshold'] df['Inbound Anomaly Score Threshold'] = df['attributes.inbound_anomaly_score_threshold'] df['LFI Score Threshold'] = df['attributes.lfi_score_threshold'] df['Max File Size'] = df['attributes.max_file_size'] df['Max Num Args'] = df['attributes.max_num_args'] df['Notice Anomaly Score'] = df['attributes.notice_anomaly_score'] df['Paranoia Level'] = df['attributes.paranoia_level'] df['PHP Injection Score Threshold'] = df['attributes.php_injection_score_threshold'] df['RCE Score Threshold'] = df['attributes.rce_score_threshold'] df['Restricted Extensions'] = df['attributes.restricted_extensions'] df['Restricted Headers'] = df['attributes.restricted_headers'] df['RFI Score Threshold'] = df['attributes.rfi_score_threshold'] df['Session Fixation Score Threshold'] = df['attributes.session_fixation_score_threshold'] df['SQL Injection Score Threshold'] = df['attributes.sql_injection_score_threshold'] df['Total Arg Length'] = df['attributes.total_arg_length'] df['Updated At'] = df['attributes.updated_at'] df['Warning Anomaly Score'] = df['attributes.warning_anomaly_score'] df['XSS Score Threshold'] = df['attributes.xss_score_threshold'] # df.insert(2, 'Severity', None) # df.insert(3, 'Description', None) # if x == 0: # df_all_rows = df # for x in range(len(df.index)): # obj = getRuleByID(str(df['ID'].iloc[x])) # df.at[x,'Severity'] = obj['Severity'].iloc[0] # df.at[x,'Description'] = obj['Description'].iloc[0] # pandas.set_option('display.max_colwidth', -1) # df_all_rows = df_all_rows.append(df,ignore_index = True) print("\n\n" + scripts.bcolors.OKBLUE + scripts.bcolors.UNDERLINE + "OWASP CONFIG for " + owaspid + scripts.bcolors.ENDC + scripts.bcolors.ENDC) print(df.iloc[0]) input("Press ENTER to return to menu...") else: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) else: input(scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC)
def listWAFIDsNoPrompt(): print("This takes a while, querying each service for WAF status...") pandas.set_option('display.max_rows', 1000) if scripts.checkAPINoPrint(): services = scripts.listServicesNoPrint() if services is not None: dfObj = pandas.DataFrame() for x in range(len(services.index)): if not services['Version'].isnull().iloc[x]: header = {"Accept": "application/vnd.api+json"} header.update({"Fastly-Key": scripts.getKeyFromConfig()}) # print("https://api.fastly.com/service/" + str(services['ID'].iloc[x]) + "/version/" + str(services['Version'].iloc[x]) + "/wafs") r = requests.get( "https://api.fastly.com/service/" + str(services['ID'].iloc[x]) + "/version/" + str(services['Version'].iloc[x]) + "/wafs", headers=header) if r.status_code == 401: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) elif r.status_code == 404: # * no waf for that service pass elif r.status_code == 200: with scripts.utils.DataFrameFromDict( r.json()['data']) as df: if df.empty != True: df.insert(0, 'Name', str(services['Name'].iloc[x])) df.insert(1, 'Service ID', str(services['ID'].iloc[x])) # ! took out domains since it made the print out wider than the console and messed up formatting # df.insert(2, 'Domain(s)', str(services['Domain(s)'].iloc[x])) # df['Domain(s)'] = df['Domain(s)'].str.wrap(40) df['WAF ID'] = df['id'] df['Version'] = df['attributes.version'] df['Disabled'] = df['attributes.disabled'] df['Last Push'] = df['attributes.last_push'] df['Logged Rules'] = df[ 'attributes.rule_statuses_log_count'] df['Blocked Rules'] = df[ 'attributes.rule_statuses_block_count'] df['Disabled Rules'] = df[ 'attributes.rule_statuses_disabled_count'] if df.empty != True: dfObj = dfObj.append(df, ignore_index=True) else: input(scripts.bcolors.WARNING + "Error with services request.\nStatus: " + str(r.status_code) + "\nPress ENTER to continue..." + scripts.bcolors.ENDC) dfObj.reset_index(drop=True, inplace=True) print(dfObj, flush=True) return dfObj else: input(scripts.bcolors.WARNING + "Error with request. Press ENTER to continue..." + scripts.bcolors.ENDC) else: input( scripts.bcolors.WARNING + "Error with API Key, generate a new one. Press ENTER to continue..." + scripts.bcolors.ENDC) return None