├── README.md ├── check_for_root_detection.py ├── install_burp_cert.py └── repackage_apk_for_burp.py /README.md: -------------------------------------------------------------------------------- 1 | # Android-App-Testing 2 | Scripts to help test Android apps 3 | 4 | ## check_for_root_detection.py 5 | Recurses through smali files and looks for strings commonly associated with root detection mechansims. Prints the filepath, method name, and detected string. Should also print the file where the method is invoked, but is it bit buggy, and doesn't always work...not sure why and not too important at the moment. 6 | 7 | ## install_burp_cert.py 8 | Automates the process of installing a Burp Suite certificate on a rooted Android device prior to Android Nougat. Installs a cert as a system trusted CA. I noticed a few intermittent SSL error on certain sites when testing on Marshmallow, but works perfect on an emulated KitKat. I only have tested this on emulated devices. Requires PyOpenSSL, as well as having ADB installed in your path and Burp running, and a connected device reachable with ADB. Mostly based on this [blogpost](https://blog.ropnop.com/configuring-burp-suite-with-android-nougat/). 9 | 10 | ## repackage_apk_for_burp.py 11 | Automates the process of making apps work with Burp Suite in Android devices from Nougat forward. Decompiles an APK, adds a network-security-config and Burp's CA cert to the project and recompiles. Only tested on emulated Nougat. Requires [apktool](https://ibotpeaches.github.io/Apktool/install/), keytool and jarsigner (available in the JDK), to be in your path, and requires Burp to be running (or you can supply a path to the cacert.der). Mostly based on this [blogpost](https://blog.ropnop.com/configuring-burp-suite-with-android-nougat/). 12 | -------------------------------------------------------------------------------- /check_for_root_detection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | __author__ = "Jake Miller (@LaconicWolf)" 4 | __date__ = "20190726" 5 | __version__ = "0.01" 6 | __description__ = '''\ 7 | A script that recursively searches smali files for the presence of root detection strings. 8 | First, decode an APK with apktool "apktool d example.apk". Next, move to the newly created 9 | directory and run this script. 10 | ''' 11 | 12 | import sys 13 | 14 | if not sys.version.startswith('3'): 15 | print('\n[-] This script will only work with Python3. Sorry!\n') 16 | exit() 17 | 18 | import os 19 | import re 20 | import threading 21 | from queue import Queue 22 | 23 | 24 | def find_smali_files(root_dir): 25 | """Recursively looks for *.smali files and returns 26 | a list containing the full file path. 27 | """ 28 | smali_files = [] 29 | for root, dirnames, filenames in os.walk(root_dir): 30 | for filename in filenames: 31 | if filename.endswith('.smali'): 32 | smali_files.append(os.path.join(root, filename)) 33 | return smali_files 34 | 35 | 36 | def search_text_for_root_detection_strings(textfile): 37 | """Reads and searches a specified textfile for presence 38 | of root detection strings. Returns the name 39 | of the text file and matches if a match is found. Based on: 40 | https://stackoverflow.com/questions/1101380/determine-if-running-on-a-rooted-device 41 | and: 42 | https://stackoverflow.com/questions/12286928/fastest-way-in-python-to-search-for-multiple-items-in-a-body-of-text 43 | """ 44 | root_detection_strings = [ 45 | "/system/app/Superuser.apk", "/sbin/su", 46 | "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", 47 | "/data/local/bin/su", "/system/sd/xbin/su", 48 | "/system/bin/failsafe/su", "/data/local/su", "/su/bin/su", 49 | "test-keys", '"/system/xbin/which", "su"', "'/system/xbin/which', 'su'", 50 | ] 51 | with open(textfile) as fh: 52 | contents = fh.read() 53 | regex = re.compile('|'.join(re.escape(x) for x in root_detection_strings)) 54 | found = regex.findall(contents) 55 | if found: 56 | for item in found: 57 | method_name, matched_string = find_parent_method(contents, item) 58 | with print_lock: 59 | print("{}, {}, {}".format(textfile, method_name, matched_string)) 60 | method_paths.append(make_method_path(textfile, method_name)) 61 | 62 | 63 | def find_parent_method(file_contents, string): 64 | """Return the name of a method where a string is found.""" 65 | start_method_indices = [c.start() for c in re.finditer('\.method', file_contents)] 66 | end_method_indices = [c.start() for c in re.finditer('\.end method', file_contents)] 67 | for index in zip(start_method_indices, end_method_indices): 68 | method = file_contents[index[0]:index[1]] 69 | if string in method: 70 | method_name = method.split('\n')[0] 71 | return method_name, string 72 | 73 | 74 | def find_method_invocation(filename, methods=None): 75 | """Reads a file to check if a method is called. 76 | """ 77 | methods = methods if methods else method_paths 78 | with open(filename) as fh: 79 | contents = fh.read() 80 | regex = re.compile('|'.join(re.escape(x) for x in methods)) 81 | found = regex.findall(contents) 82 | if found: 83 | for item in found: 84 | method_name, matched_string = find_parent_method(contents, item) 85 | with print_lock: 86 | print(("[+] The method {},\n" 87 | #" contained the string {},\n" 88 | " was called in the file {},\n" 89 | " from the method {}").format(item, filename, method_name.split(' ')[-1])) 90 | 91 | 92 | def make_method_path(file_path, method): 93 | """Transforms/combines a file path and method name to easier 94 | search for its invocation. 95 | """ 96 | if os.sep == '\\': 97 | file_path = file_path.replace('\\', '/') 98 | if file_path.startswith('./smali/'): 99 | file_path = file_path.replace('./smali/', '') 100 | if file_path.endswith('.smali'): 101 | file_path = file_path.replace('.smali', '') 102 | method = method.split(' ')[-1] 103 | return file_path + ';->' + method 104 | 105 | 106 | def manage_root_detect_queue(): 107 | """Manages the smali file queue and calls the 108 | search_text_for_root_detection_strings function. 109 | """ 110 | while True: 111 | current_filename = file_queue.get() 112 | search_text_for_root_detection_strings(current_filename) 113 | file_queue.task_done() 114 | 115 | 116 | def manage_method_invoke_queue(): 117 | """Manages the smali file queue and calls the 118 | search_text_for_root_detection_strings function. 119 | """ 120 | while True: 121 | current_filename = file_queue.get() 122 | find_method_invocation(current_filename) 123 | file_queue.task_done() 124 | 125 | 126 | def main(): 127 | root_dir = '.' 128 | print('[*] Checking for .smali files...') 129 | smali_file_list = find_smali_files(root_dir) 130 | if smali_file_list: 131 | print('[+] Found {} .smali files.'.format(len(smali_file_list))) 132 | else: 133 | print('[-] No .smali files found while searching recursively from {}.'.format(os.getcwd())) 134 | exit() 135 | print('[*] Searching files for strings that are commonly used for root detection...') 136 | 137 | for i in range(20): 138 | t = threading.Thread(target=manage_root_detect_queue) 139 | t.daemon = True 140 | t.start() 141 | 142 | for current_file in smali_file_list: 143 | file_queue.put(current_file) 144 | file_queue.join() 145 | 146 | print("[*] Searching .smali files for the method invocations...") 147 | 148 | for i in range(20): 149 | t = threading.Thread(target=manage_method_invoke_queue) 150 | t.daemon = True 151 | t.start() 152 | 153 | for current_file in smali_file_list: 154 | file_queue.put(current_file) 155 | file_queue.join() 156 | 157 | if __name__ == '__main__': 158 | 159 | method_names = [] 160 | method_paths = [] 161 | #method_call_tree = {} 162 | 163 | print_lock = threading.Lock() 164 | file_queue = Queue() 165 | 166 | main() 167 | -------------------------------------------------------------------------------- /install_burp_cert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | if not sys.version.startswith('3'): 6 | print('\n[-] This script will only work with Python3. Sorry!\n') 7 | exit() 8 | 9 | import urllib.request 10 | import subprocess 11 | import os 12 | import shutil 13 | import argparse 14 | 15 | try: 16 | import OpenSSL 17 | except ImportError: 18 | print("[-] This script requires pyOpenSSL. Try 'python3 -m pip install pyopenssl' or consult the Internet for installation instructions.") 19 | exit() 20 | 21 | __author__ = "Jake Miller (@LaconicWolf)" 22 | __date__ = "20190705" 23 | __version__ = "0.01" 24 | __description__ = '''A script to help install a Burp proxy certificate on Android devices prior to Nougat. 25 | Largely based on https://blog.ropnop.com/configuring-burp-suite-with-android-nougat/''' 26 | 27 | 28 | def check_for_tools(name): 29 | """Checks to see whether the tool name is in current directory or in the PATH""" 30 | if is_in_dir(name) or is_in_path(name): 31 | return True 32 | else: 33 | return False 34 | 35 | 36 | def check_for_burp(host, port): 37 | """Checks to see if Burp is running.""" 38 | url = ("http://{}:{}/".format(host, port)) 39 | try: 40 | resp = urllib.request.urlopen(url) 41 | except urllib.error.URLError as e: 42 | return False 43 | if b"Burp Suite" in resp.read(): 44 | return True 45 | else: 46 | return False 47 | 48 | 49 | def download_burp_cert(host, port): 50 | """Downloads the Burp Suite certificate.""" 51 | url = ("http://{}:{}/cert".format(host, port)) 52 | file_name = 'cacert.cer' 53 | # Download the file from url and save it locally under file_name: 54 | try: 55 | with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file: 56 | data = response.read() # a bytes object 57 | out_file.write(data) 58 | return file_name 59 | except Exception as e: 60 | print('[-] An error occurred: {}'.format(e)) 61 | exit() 62 | 63 | 64 | def is_in_path(name): 65 | """Check whether name is on PATH and marked as executable. 66 | https://stackoverflow.com/questions/11210104/check-if-a-program-exists-from-a-python-script/34177358 67 | """ 68 | return shutil.which(name) is not None 69 | 70 | 71 | def is_in_dir(name, directory='.'): 72 | """Checks whether a file exists in a specified directory.""" 73 | files = (os.listdir(directory)) 74 | for file in files: 75 | if file.lower() == name.lower(): 76 | if os.path.isfile(file): 77 | return True 78 | 79 | 80 | def get_devices(): 81 | """Uses adb to get a list of attached devices. The output string is formatted 82 | to a list, and returned. 83 | """ 84 | devices = subprocess.getoutput("adb devices").split('List of devices attached\n')[1] 85 | if not devices: 86 | return False 87 | else: 88 | device_list = devices.split('\n') 89 | return device_list 90 | 91 | 92 | def get_build_version_info(device_id): 93 | """Returns the build release as an integer""" 94 | ver = subprocess.getoutput("adb -s {} shell getprop ro.build.version.release".format(device_id)) 95 | return int(ver[0]) 96 | 97 | 98 | def select_device(device_list): 99 | """Prompts a user to select a device from a list of devices.""" 100 | print('[*] The following devices were found:') 101 | print('{}'.format('\n'.join(device_list))) 102 | while True: 103 | device = input('[*] Please enter the device you\'d like to use or type \'quit\' to exit:\n') 104 | if device.lower() == 'quit': 105 | exit() 106 | if device and device.lower() in [d.lower() for d in device_list]: 107 | return device 108 | else: 109 | print('[-] {} is not a valid choice. Type \'quit\' to exit, or select from the following options: \n{}'.format(device, '\n'.join(device_list))) 110 | 111 | 112 | def check_for_root(device_id): 113 | """Uses ADB to see if we have root privileges.""" 114 | uid = subprocess.getoutput("adb -s {} shell id".format(device_id)) 115 | if "uid=0(root)" in uid: 116 | return True 117 | else: 118 | return False 119 | 120 | 121 | def get_root(device_id): 122 | """Uses ADB to try to get root privileges.""" 123 | output = subprocess.getoutput("adb -s {} root".format(device_id)) 124 | 125 | 126 | def convert_der_to_pem(filename): 127 | """Converts a der to a pem and writes to the filesystem.""" 128 | with open(filename, 'rb') as fh: 129 | der = fh.read() 130 | cert = OpenSSL.crypto.load_certificate(type=OpenSSL.crypto.FILETYPE_ASN1, buffer=der) 131 | 132 | # OpenSSL and pyOpenSSL coming up with different hashes. Hardcoding it for now. 133 | pem_hash = '9a5ba575' # str(cert.subject_name_hash()) 134 | pem_bytes = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) 135 | pem_filename = pem_hash + ".0" 136 | with open(pem_filename, 'wb') as fh: 137 | fh.write(pem_bytes) 138 | return pem_filename 139 | 140 | 141 | def remount_system(device_id): 142 | """Uses ADB to remount the /system as writable""" 143 | output = subprocess.getoutput("adb -s {} remount".format(device_id)) 144 | if 'remount succeeded' in output: 145 | return True 146 | elif 'remount failed' in output: 147 | return False 148 | 149 | 150 | def move_pem_to_device(pem_file, device_id): 151 | output = subprocess.getoutput("adb -s {} push {} /sdcard/".format(device_id, pem_file)) 152 | output = subprocess.getoutput("adb -s {} shell mv /sdcard/{} /system/etc/security/cacerts/".format(device_id, pem_file)) 153 | if 'Read-only file system' in output: 154 | return False 155 | elif 'failed on' in output: 156 | cpout = subprocess.getoutput("adb -s {} shell cp /sdcard/{} /system/etc/security/cacerts/".format(device_id, pem_file)) 157 | if 'failed' in cpout or 'Read-only' in cpout: 158 | return False 159 | else: 160 | return True 161 | else: 162 | return True 163 | 164 | 165 | def change_perms(filename, device_id): 166 | """Changes perms to 644 for cert file name.""" 167 | output = subprocess.getoutput("adb -s {} shell chmod 644 /system/etc/security/cacerts/{}".format(device_id, filename)) 168 | 169 | 170 | def reboot_device(device_id): 171 | """Reboots a device.""" 172 | output = subprocess.getoutput("adb -s {} shell reboot".format(device_id)) 173 | 174 | 175 | def main(): 176 | """Checks for tools and burp, determines information about any devices, 177 | and attempts to convert and install the Burp cert on a specific device. 178 | """ 179 | 180 | # Check for adb and burp. May check for openssl in the future... 181 | required_tools = ("adb",) 182 | missing_tools = [] 183 | for tool in required_tools: 184 | if not check_for_tools(tool): 185 | missing_tools.append(tool) 186 | burp = check_for_burp(burp_host, burp_port) 187 | 188 | if not burp or missing_tools: 189 | if not burp: 190 | print('[-] Unable to connect to Burp at {}:{}. Please ensure the Burp web UI is running and available.'.format(burp_host, burp_port)) 191 | for tool in missing_tools: 192 | print("[-] {} could not be found in the current directory or in your PATH. Please ensure either of these conditions are met.".format(tool)) 193 | 194 | # Check for connected devices 195 | print('[*] Checking for connected devices') 196 | connected_devices = get_devices() 197 | if not connected_devices: 198 | print("[-] No devices/emulators found. Make sure adb is running and that a device is connected.") 199 | exit() 200 | connected_devices = [d.split('\t')[0] for d in connected_devices] 201 | connected_devices = list(filter(None, connected_devices)) 202 | if not connected_devices: 203 | print("[-] No devices/emulators found. Make sure adb is running and that a device is connected.") 204 | if len(connected_devices) > 1: 205 | device = select_device(connected_devices) 206 | else: 207 | device = ''.join(connected_devices) 208 | 209 | # Checks to see if the device is at the API level where the cert 210 | # installation will even matter. After Marshmallow, this won't matter, 211 | # we can just exit if it is build 7 or later. 212 | print("[*] Checking API level.") 213 | rel = get_build_version_info(device) 214 | if rel >= 7: 215 | print("[-] The build version on this device will not respect a user installed certificate. Try repackaging the APK.") 216 | exit() 217 | 218 | # Checks for root. This also requires a rooted device. Otherwise exit. 219 | print("[*] Checking for root privileges.") 220 | is_rooted = check_for_root(device) 221 | if is_rooted: 222 | print('[*] Root privileges verified.') 223 | else: 224 | get_root(device) 225 | is_rooted = check_for_root(device) 226 | if is_rooted: 227 | print("[+] Device rooted using 'adb -s {} root'.".format(device)) 228 | else: 229 | print("[-] Unable to use 'adb -s {} root' to root the device.".format(device)) 230 | print('[-] Root is required for this method. Exiting.') 231 | exit() 232 | 233 | # Attempt to download and convert the cert from der to pem 234 | print("[*] Attempting to add the Burp CA cert to the device") 235 | print("[*] Downloading cert from http://{}:{}".format(burp_host, burp_port)) 236 | certname = download_burp_cert(burp_host, burp_port) 237 | print("[*] Converting Burp Cert to pem") 238 | pem_file = convert_der_to_pem(certname) 239 | print("[*] PEM file written to {}".format(pem_file)) 240 | 241 | # Adds the cert. Have to make /system writable first. Requires 242 | # an emulated device to be started with -writable-system. 243 | print("[*] Attempting to add cert to device") 244 | print("[*] Remounting /system") 245 | if not remount_system(device): 246 | print('[-] Unable to mount /system as read write. If running an emulator, please include -writable-system in the command line arguments (emulator -avd -writable-system)') 247 | exit() 248 | print('[*] Moving {} to the device'.format(pem_file)) 249 | if not move_pem_to_device(pem_file, device): 250 | print('[-] Unable to mount /system as read write. If running an emulator, please include -writable-system in the command line arguments (emulator -avd -writable-system)') 251 | exit() 252 | 253 | # Change permissions on the file to 644, and then restart. 254 | print('[*] Changing permissions on the file.') 255 | change_perms(pem_file, device) 256 | print('[+] Success. Rebooting device. The cert should work without error after reboot.') 257 | reboot_device(device) 258 | 259 | 260 | if __name__ == '__main__': 261 | parser = argparse.ArgumentParser() 262 | parser.add_argument("-pr", "--proxy", 263 | nargs='?', 264 | const="127.0.0.1:8080", 265 | default="127.0.0.1:8080", 266 | help="Specify a proxy to use (default 127.0.0.1:8080)") 267 | args = parser.parse_args() 268 | 269 | if args.proxy.startswith('http'): 270 | if '://' not in args.proxy: 271 | print("[-] Unknown format for proxy. Please specify only a host and port (-pr 127.0.0.1:8080") 272 | exit() 273 | args.proxy = ''.join(args.proxy.split("//")[1:]) 274 | burp_host = args.proxy.split(":")[0] 275 | burp_port = int(args.proxy.split(":")[1]) 276 | main() -------------------------------------------------------------------------------- /repackage_apk_for_burp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | if not sys.version.startswith('3'): 6 | print('\n[-] This script will only work with Python3. Sorry!\n') 7 | exit() 8 | 9 | import subprocess 10 | import os 11 | import shutil 12 | import argparse 13 | import urllib.request 14 | 15 | 16 | __author__ = "Jake Miller (@LaconicWolf)" 17 | __date__ = "20190705" 18 | __version__ = "0.01" 19 | __description__ = '''A script to repackage an APK file to allow a user-installed SSL certificate.''' 20 | 21 | 22 | def check_for_tools(name): 23 | """Checks to see whether the tool name is in current directory or in the PATH""" 24 | if is_in_dir(name) or is_in_path(name): 25 | return True 26 | else: 27 | return False 28 | 29 | 30 | def is_in_path(name): 31 | """Check whether name is on PATH and marked as executable. 32 | https://stackoverflow.com/questions/11210104/check-if-a-program-exists-from-a-python-script/34177358 33 | """ 34 | return shutil.which(name) is not None 35 | 36 | 37 | def is_in_dir(name, directory='.'): 38 | """Checks whether a file exists in a specified directory.""" 39 | files = (os.listdir(directory)) 40 | for file in files: 41 | if file.lower() == name.lower(): 42 | if os.path.isfile(file): 43 | return True 44 | 45 | 46 | def apktool_decompile(filename): 47 | """Uses APKTool to decompile an APK""" 48 | output = subprocess.getoutput("apktool d {} -o {}".format(filename, filename.replace('.apk', '_out'))) 49 | if 'Exception in' in output: 50 | print('[-] An error occurred when decompiling the APK.') 51 | print(output) 52 | try: 53 | os.rmdir(filename.replace('.apk', '_out')) 54 | except: 55 | pass 56 | return False 57 | else: 58 | return True 59 | 60 | 61 | def apktool_build(filepath): 62 | """Uses APKTool to create a new APK""" 63 | output = subprocess.getoutput("apktool b {}".format(filepath)) 64 | try: 65 | os.listdir(filepath + os.sep + 'dist') 66 | except FileNotFoundError: 67 | print('[-] An error occurred when rebuilding the APK.') 68 | print(output) 69 | return False 70 | return True 71 | 72 | 73 | def do_keytool(keystore_name): 74 | """Uses keytool to generate a key.""" 75 | newline = os.linesep 76 | 77 | '''Ugly hack to get around answering interactive prompts 78 | The output would have been something like: 79 | Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 10,000 days 80 | for: CN=Y, OU=Unknown, O=Y, L=Unknown, ST=Y, C=Unknown 81 | [Storing test.keystore]''' 82 | commands = ['Y', 'Y', 'Y', 'Y', 'Y', 'Y'] 83 | FNULL = open(os.devnull, 'w') 84 | p = subprocess.Popen(['keytool', '-genkey', '-v', '-keystore', 85 | keystore_name, '-storepass', 'password', 86 | '-alias', 'android', '-keypass', 'password', 87 | '-keyalg', 'RSA', '-keysize', '2048', '-validity', 88 | '10000'], 89 | stdin=subprocess.PIPE, stderr=subprocess.STDOUT, 90 | stdout=FNULL, universal_newlines=True) 91 | p.communicate(newline.join(commands)) 92 | keystore_present = True 93 | 94 | 95 | def do_jarsigner(filepath, keystore): 96 | """Uses APKTool to create a new APK""" 97 | output = subprocess.getoutput("jarsigner -verbose -keystore {} -storepass password -keypass password {} android".format(keystore, filepath)) 98 | if 'jar signed.' not in output: 99 | print("[-] An error occurred during jarsigner: \n{}".format(output)) 100 | else: 101 | print("[*] Signed!") 102 | 103 | def add_network_security_config(basedir): 104 | """Adds a network security config file that allows user 105 | certificates. 106 | """ 107 | data = '''\ 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | ''' 120 | with open(os.path.join(basedir, 'res', 'xml', 'network_security_config.xml'), 'w') as fh: 121 | fh.write(data) 122 | 123 | 124 | def do_network_security_config(directory): 125 | """Checks for a network security config file in the project. 126 | If present, reads the file and adds a line to allow user certs. 127 | If not present, creates one to allow user certs. 128 | """ 129 | # Still need to add the line if the file already exists 130 | if 'xml' in os.listdir(os.path.join(directory, 'res')): 131 | if 'network_security_config.xml' in os.listdir(os.path.join(directory, 'res', 'xml')): 132 | filepath = os.path.join(directory, 'res', 'xml', 'network_security_config.xml') 133 | with open(filepath) as fh: 134 | contents = fh.read() 135 | new_contents = contents.replace('', '\n \n ') 136 | with open(filepath, 'w') as fh: 137 | fh.write(new_contents) 138 | return True 139 | else: 140 | print('[*] Adding network_security_config.xml to {}.'.format(os.path.join(directory, 'res', 'xml'))) 141 | add_network_security_config(directory) 142 | else: 143 | print('[*] Creating {} and adding network_security_config.xml.'.format(os.path.join(directory, 'res', 'xml'))) 144 | os.mkdir(os.path.join(directory, 'res', 'xml')) 145 | add_network_security_config(directory) 146 | 147 | 148 | def check_for_burp(host, port): 149 | """Checks to see if Burp is running.""" 150 | url = ("http://{}:{}/".format(host, port)) 151 | try: 152 | resp = urllib.request.urlopen(url) 153 | except Exception as e: 154 | return False 155 | if b"Burp Suite" in resp.read(): 156 | return True 157 | else: 158 | return False 159 | 160 | 161 | def download_burp_cert(host, port): 162 | """Downloads the Burp Suite certificate.""" 163 | url = ("http://{}:{}/cert".format(host, port)) 164 | file_name = 'cacert.der' 165 | # Download the file from url and save it locally under file_name: 166 | try: 167 | with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file: 168 | data = response.read() # a bytes object 169 | out_file.write(data) 170 | cert_present = True 171 | return file_name 172 | except Exception as e: 173 | print('[-] An error occurred: {}'.format(e)) 174 | exit() 175 | 176 | 177 | def edit_manifest(filepath): 178 | '''Adds android:networkSecurityConfig="@xml/network_security_config" 179 | to the manifest''' 180 | with open(filepath) as fh: 181 | contents = fh.read() 182 | new_contents = contents.replace("