├── Mac OsX Forensics.pdf ├── kcpass.py ├── alias.py ├── plist_user.py ├── wifi.py ├── cups_ipp.py ├── utmpx.py ├── plist_artifacts.py ├── asl.py ├── keychains.py ├── mac_recent.py └── bsm.py /Mac OsX Forensics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moxilo/mac-osx-forensics/HEAD/Mac OsX Forensics.pdf -------------------------------------------------------------------------------- /kcpass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | """ KCPassword Xor """ 14 | 15 | # MSc Project in Royal Holloway, University of London. 16 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 17 | 18 | import sys 19 | 20 | magic_static='7d895223d2bcddeaa3b91f' 21 | pwd = '' 22 | 23 | if len(sys.argv) != 2: 24 | print 'Please write as a first argv the hexadecimal kcpassword value:' 25 | print 'Example: python {} 1ceb3147d2172f1140ff63bf'.format(sys.argv[0]) 26 | exit(1) 27 | 28 | kcpasswd=sys.argv[1] 29 | print u'\n\tKcpasswd: 0x{}.'.format(kcpasswd) 30 | print u'\tMagic Xor: 0x{}.'.format(magic_static) 31 | 32 | tam_xor = len(magic_static) 33 | tam = len(kcpasswd) - tam_xor 34 | magic = magic_static 35 | while tam > 0: 36 | tam -= tam_xor 37 | magic += magic_static 38 | print u'\tUsed Magic Xor: 0x{}.'.format(magic) 39 | 40 | i = 0 41 | while i < len(kcpasswd): 42 | charkc = kcpasswd[i] + kcpasswd[i+1] 43 | charkch = int(charkc, 16) 44 | charm = magic[i] + magic[i+1] 45 | charmh = int(charm, 16) 46 | r = charkch ^ charmh 47 | pwd += chr(r) 48 | if r == 0: 49 | print '\n\tThe password is: "{}".\n'.format(pwd) 50 | break 51 | i += 2 52 | -------------------------------------------------------------------------------- /alias.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import binascii 4 | import construct 5 | import datetime 6 | import sys 7 | 8 | from binplist import binplist 9 | 10 | HFS_to_Epoch = 2082844800 11 | s_alias = construct.Struct( 12 | 'plist_alias', 13 | construct.Padding(4), 14 | construct.UBInt16('length'), 15 | construct.Padding(6), 16 | construct.UBInt32('timestamp1'), 17 | construct.Padding(18), 18 | construct.UBInt32('timestamp2'), 19 | construct.Padding(20)) 20 | 21 | s_type = construct.UBInt16('type') 22 | 23 | s_volume = construct.Struct( 24 | 'volume', 25 | construct.UBInt16('volume1_length'), 26 | construct.UBInt16('characters1'), 27 | construct.String( 28 | 'volume1', 29 | lambda ctx: ctx.characters1 * 2), 30 | construct.Padding(2), 31 | construct.UBInt16('volume2_length'), 32 | construct.UBInt16('characters2'), 33 | construct.String( 34 | 'volume2', 35 | lambda ctx: ctx.characters2 * 2)) 36 | 37 | s_mount_point = construct.PascalString( 38 | 'mount_point', 39 | length_field = construct.UBInt16('length')) 40 | 41 | def __init__(): 42 | f = open (sys.argv[1], 'rb') 43 | plist = binplist.BinaryPlist(f, False, False) 44 | try: 45 | parsed_plist = plist.Parse() 46 | except binplist.FormatError: 47 | print "Error!" 48 | exit() 49 | ''' 50 | system_items = parsed_plist['systemitems'] 51 | elem_list = system_items['VolumesList'] 52 | favorite_items = parsed_plist['favorites'] 53 | elem_list.extend(favorite_items['VolumesList']) 54 | ''' 55 | favorite_items = parsed_plist['favorites'] 56 | elem_list = favorite_items['VolumesList'] 57 | for volume in elem_list: 58 | if 'Alias' in volume: 59 | try: 60 | data = volume['Alias'] 61 | s = s_alias.parse(data) 62 | data = data[s_alias.sizeof():] 63 | 64 | # Search for 0x000e volume ID 65 | type = s_type.parse(data) 66 | data = data[s_type.sizeof():] 67 | while type != 14 and data != '': 68 | type = s_type.parse(data) 69 | data = data[s_type.sizeof():] 70 | except: 71 | print "Fail!" 72 | continue 73 | 74 | # If not volume ID 75 | if data == '': 76 | print "Fail!" 77 | continue 78 | v = s_volume.parse(data) 79 | time = datetime.datetime.fromtimestamp( 80 | s.timestamp1 - HFS_to_Epoch).strftime('%Y-%m-%d %H:%M:%S') 81 | print u'\n\tFile name: {}'.format(v.volume1) 82 | print u'\tVolume name: {}'.format(v.volume2) 83 | print u'\tTime: {}'.format(time) 84 | if s.timestamp1 != s.timestamp2: 85 | time = datetime.datetime.fromtimestamp( 86 | s.timestamp2 - HFS_to_Epoch).strftime('%Y-%m-%d %H:%M:%S') 87 | print u'\tSecond time: {}'.format(time) 88 | 89 | type = s_type.parse(data) 90 | data = data[s_type.sizeof():] 91 | while type != 19 and data != '': 92 | type = s_type.parse(data) 93 | data = data[s_type.sizeof():] 94 | if data == '': 95 | continue 96 | mount_point = s_mount_point.parse(data) 97 | print u'\tMount point: {}'.format(mount_point) 98 | __init__() 99 | -------------------------------------------------------------------------------- /plist_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | """ Mac OS X 10.8 and 10.9 User Accounts """ 14 | 15 | # MSc Project in Royal Holloway, University of London. 16 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 17 | 18 | from binplist import binplist 19 | import xml.etree.ElementTree 20 | import sys 21 | import os 22 | import binascii 23 | 24 | # Author: Kiddi G. 25 | class FooFile(object): 26 | def __init__(self, data): 27 | self._data = data 28 | self._offset = 0 29 | 30 | def tell(self): 31 | return self._offset 32 | 33 | def seek(self, offset, whence=os.SEEK_SET): 34 | if whence == os.SEEK_SET: 35 | self._offset = offset 36 | elif whence == os.SEEK_CUR: 37 | self._offset += offset 38 | elif whence == os.SEEK_END: 39 | self._offset = len(self._data) + offset 40 | 41 | def read(self, size=0): 42 | if self._offset > len(self._data): 43 | return '' 44 | 45 | if not size: 46 | data_to_return = self._data[self._offset:] 47 | self._offset = len(self._data) 48 | return data_to_return 49 | 50 | if size >= len(self._data[self._offset:]): 51 | data_to_return = self._data[self._offset:] 52 | self._offset = len(self._data) 53 | return data_to_return 54 | 55 | data_to_return = self._data[self._offset:self._offset + size] 56 | self._offset += size 57 | return data_to_return 58 | 59 | def close(self): 60 | pass 61 | 62 | 63 | name = sys.argv[1] 64 | 65 | fd = open(name, 'rb') 66 | plist = binplist.BinaryPlist(fd, False, False) 67 | parsed_plist = plist.Parse() 68 | account = parsed_plist['name'][0] 69 | name = parsed_plist['realname'][0] 70 | uid = parsed_plist['uid'][0] 71 | gid = parsed_plist['gid'][0] 72 | shell = parsed_plist['shell'][0] 73 | password = parsed_plist['authentication_authority'] 74 | ShadowHashData = parsed_plist['ShadowHashData'] 75 | foo = FooFile(ShadowHashData[0]) 76 | plist_file = binplist.BinaryPlist(file_obj=foo) 77 | top_level = plist_file.Parse()['SALTED-SHA512-PBKDF2'] 78 | salt = binascii.hexlify(top_level['salt']) 79 | entropy = binascii.hexlify(top_level['entropy']) 80 | iterations = top_level['iterations'] 81 | 82 | policy = parsed_plist['passwordpolicyoptions'][0] 83 | # TODO: beautiful hack because bplist return a string instead of dict 84 | list_timestamp = [] 85 | key = '' 86 | # XML extraction. 87 | xml_policy = xml.etree.ElementTree.fromstring(policy) 88 | for dic in xml_policy.iterfind('dict'): 89 | for elem in dic: 90 | if elem.tag == u'key': 91 | key = elem.text 92 | elif elem.tag == u'date': 93 | list_timestamp.append([key, elem.text]) 94 | 95 | print u'\nUser: {}'.format(account) 96 | print u'UID: {}'.format(uid) 97 | print u'GID: {}'.format(gid) 98 | print u'Shell: {}'.format(shell) 99 | print u'Policy:'.format() 100 | for time_elem in list_timestamp: 101 | print u' {} at {}.'.format(time_elem[0], time_elem[1]) 102 | print u'Available Passwords:'.format() 103 | for password in parsed_plist['authentication_authority']: 104 | if password.startswith(';ShadowHash'): 105 | print u' Mac OS X user password:' 106 | print u' Iterations: {}'.format(iterations) 107 | print u' Salt: {}'.format(salt) 108 | print u' Entropy: {}'.format(entropy) 109 | elif password.startswith(';Kerberos'): 110 | listKerberos = password.split(';') 111 | print u' Kerberos:' 112 | print u' Version: {}'.format(listKerberos[1]) 113 | print u' Hash: {}'.format(listKerberos[4]) 114 | else: 115 | print u' {}'.format(password) 116 | print u''.format() 117 | print u''.format() 118 | 119 | # from pbkdf2 import crypt 120 | # password = 'abcd' 121 | # result = crypt(password, salt, iterations).split('$') 122 | 123 | fd.close() 124 | -------------------------------------------------------------------------------- /wifi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ The wifi.log parser. """ 15 | 16 | # MSc Project in Royal Holloway, University of London. 17 | __author__ = 'Joaquin Moreno Garijo (Joaquin.MorenoGarijo.2013@live.rhul.ac.uk)' 18 | 19 | # README: 20 | # # The goal of this tools is only for developing pourpose. 21 | # The full documented and well implemented version is going to be in PLASO: 22 | # https://code.google.com/p/plaso/ 23 | # http://plaso.kiddaland.net/ 24 | 25 | 26 | # Disclaimer: it only was probed in 10.9. 27 | 28 | import os 29 | import re 30 | import sys 31 | import time 32 | 33 | # Print all records (True/False) 34 | ALL = False 35 | 36 | # Default place of the file 37 | DIRNAME = '/private/var/log' 38 | FILENAME = 'wifi.log' 39 | 40 | def getEpochTime(timestamp): 41 | pattern = '%d %b %Y %H:%M:%S' 42 | aux = time.strptime(timestamp, pattern) 43 | return int(time.mktime(aux)) 44 | 45 | def getAction(agent, action, msg): 46 | if 'airportd' in agent: 47 | if 'airportdProcessDLILEvent' in action: 48 | interface = msg.split()[0] 49 | return 'Interface {} turn up.'.format(interface) 50 | elif 'doAutoJoin' in action: 51 | ssid = 'Unknown' 52 | exp_reg = re.match(r'Already\sassociated\sto\s(.*)\.\sBailing', msg) 53 | if exp_reg: 54 | ssid = exp_reg.group(1) 55 | return 'Wifi connected to SSID {}'.format(ssid) 56 | elif 'processSystemPSKAssoc' in action: 57 | ssid = 'Unknown' 58 | for i in re.findall(r'(?<=\[ssid=).*?(?=, bssid=)', msg): 59 | ssid = i 60 | bssid = 'Unknown' 61 | for i in re.findall(r'(?<=bssid=).*?(?=, security=)', msg): 62 | bssid = i 63 | security = 'Unknown' 64 | for i in re.findall(r'(?<=security=).*?(?=, rssi=)', msg): 65 | security = i 66 | aux = 'New wifi configured. BSSID: ' 67 | return '{}{}, SSID: {}, Security: {}.'.format( 68 | aux, bssid, ssid, security) 69 | return None 70 | 71 | def printData(timestamp, epoch, agent, action, message): 72 | print '[{}] ({}) Agent: {} Action: {} [{}]'.format(timestamp, epoch, agent, action, message) 73 | 74 | def __init__(): 75 | #TODO: Common problem in syslog format logs. In PLASO version is much more better :-). 76 | year = time.strftime("%Y", time.gmtime()) 77 | print '\n\n\t[IMPORTANT] The year {} is selected as a year of the timestamp!!!!\n\n'.format(year) 78 | 79 | if len(sys.argv) > 2: 80 | print 'Use: python {0} wififile'.format(sys.argv[0]) 81 | exit(1) 82 | 83 | try: 84 | if len(sys.argv) == 1: 85 | path = os.path.join(DIRNAME, FILENAME) 86 | elif len(sys.argv) == 2: 87 | path = sys.argv[1] 88 | f = open(path, 'r') 89 | except: 90 | print u'[Error] File [{}] not found.'.format(path) 91 | exit(1) 92 | 93 | Day_Name = r'(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s?' 94 | Moth_Name = r'(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+' 95 | Day_Number = r'(\d{1,2})\s+' 96 | Time = r'([0-9]{2}:[0-9]{2}:[0-9]{2})\.[0-9]{3}\s+' 97 | Agent = r'\<([^\>]+)\>\s+' 98 | Function = r'([^:]+):\s+' 99 | Message = r'([^\n]+)' 100 | Expression = Day_Name + Moth_Name + Day_Number + Time + Agent + Function + Message 101 | 102 | for line in f: 103 | resul = re.match(Expression, line) 104 | 105 | if resul: 106 | dn = resul.group(1) 107 | mn = resul.group(2) 108 | dn = resul.group(3) 109 | t = resul.group(4) 110 | ag = resul.group(5) 111 | act = resul.group(6) 112 | msg = resul.group(7) 113 | 114 | timestamp = u'{} {} {} {}'.format(dn, mn, year, t) 115 | #print '[{}] {} @ {} @ {}'.format(timestamp, ag, act, msg) 116 | 117 | action = getAction(ag, act, msg) 118 | if action: 119 | epoch = getEpochTime(timestamp) 120 | printData(timestamp, epoch, ag, action, msg) 121 | elif ALL: 122 | printData(timestamp, epoch, ag, 'Unknown', msg) 123 | 124 | f.close() 125 | 126 | __init__() 127 | -------------------------------------------------------------------------------- /cups_ipp.py: -------------------------------------------------------------------------------- 1 | #!/usr/python 2 | # -*- coding: utf-8 -*- 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Cups Reading Control Files.""" 15 | 16 | # IMPORTANT: DIRTY PARSE... 17 | 18 | # MSc Project in Royal Holloway, University of London. 19 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 20 | 21 | import datetime 22 | import construct 23 | import sys 24 | 25 | header = construct.Padding(11) 26 | attr_id = construct.UBInt8('type') 27 | attr_text = construct.CString('text') 28 | attr_time = construct.Struct( 29 | 'time', 30 | construct.UBInt32('timestamp'), 31 | construct.UBInt16('other')) 32 | 33 | class ControlFile(object): 34 | 35 | def __init__(self): 36 | self.crt_time = 0 37 | self.proc_time = 0 38 | self.comp_time = 0 39 | self.data = [] 40 | 41 | def printValue(name, value): 42 | # print u'{}: {}'.format(name, value) 43 | if type(name) != str and type(name) != unicode: 44 | return 45 | elif name == u'printer-uri': 46 | document.data.append(u'URI: {}'.format(value)) 47 | elif name == u'job-uuid': 48 | document.data.append(u'Job ID: {}'.format(value)) 49 | elif name == u'copies': 50 | document.data.append(u'Copies: {}'.format(value)) 51 | elif name == u'DestinationPrinterID': 52 | document.data.append(u'Printer ID: {}'.format(value)) 53 | elif name == u'job-originating-user-name': 54 | document.data.append(u'User: {}'.format(value[:-1])) 55 | elif name == u'job-name': 56 | document.data.append(u'Job name: {}'.format(value[:-1])) 57 | elif name == u'document-format': 58 | document.data.append(u'Document format: {}'.format(value[:-1])) 59 | elif name == u'job-originating-host-name': 60 | document.data.append(u'Computer name: {}'.format(value[:-1])) 61 | elif name == u'com.apple.print.JobInfo.PMApplicationName': 62 | document.data.append(u'Application: {}'.format(value[:-1])) 63 | elif name == u'com.apple.print.JobInfo.PMJobOwner': 64 | document.data.append(u'Owner: {}'.format(value[:-1])) 65 | elif name.startswith('com.apple.print.PrintSettings'): 66 | if name == u'com.apple.print.PrintSettings.PMCopies': 67 | document.data.append(u'Copies: {}'.format(value[:-1])) 68 | elif name == u'time-at-creation': 69 | document.crt_time = u'{}'.format(value) 70 | elif name == u'time-at-processing': 71 | document.proc_time = u'{}'.format(value) 72 | elif name == u'time-at-completed': 73 | document.comp_time = u'{}'.format(value) 74 | 75 | def compare(text1, text2): 76 | i = 0 77 | while(i < len(text1) and i < len(text2)): 78 | if text1[i] != text2[i]: 79 | print u'Difference in {}: "{}" "{}"'.format(i, text1[i], text2[i]) 80 | break 81 | i += 1 82 | 83 | def getTime(epoch): 84 | return datetime.datetime.fromtimestamp( 85 | float(epoch)).strftime('%Y-%m-%d %H:%M:%S') 86 | 87 | def Pair(f): 88 | name = Parse(f) 89 | if name == None: 90 | return False 91 | value = Parse(f) 92 | if value == '': 93 | value = Parse(f) 94 | if value == None: 95 | return False 96 | printValue(name, value) 97 | return True 98 | 99 | def Parse(f): 100 | try: 101 | id = attr_id.parse_stream(f) 102 | if id == 4: 103 | time = attr_time.parse_stream(f) 104 | return time.timestamp 105 | else: 106 | text = attr_text.parse_stream(f) 107 | return u'{}'.format(text) 108 | except: 109 | return None 110 | 111 | f = open(sys.argv[1],'rb') 112 | header.parse_stream(f) 113 | 114 | 115 | document = ControlFile() 116 | more = Pair(f) 117 | while(more): 118 | more = Pair(f) 119 | 120 | if document.crt_time: 121 | print u'Creation time: {} ({}).'.format( 122 | getTime(document.crt_time), document.crt_time) 123 | if document.proc_time: 124 | print u'Process time: {} ({}).'.format( 125 | getTime(document.proc_time), document.proc_time) 126 | if document.comp_time: 127 | print u'Completed time: {} ({}).'.format( 128 | getTime(document.comp_time), document.comp_time) 129 | print u'\n'.join(document.data) 130 | print u'------------------------' 131 | 132 | f.close() 133 | 134 | -------------------------------------------------------------------------------- /utmpx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ UTMPX file parsed """ 15 | 16 | # MSc Project in Royal Holloway, University of London. 17 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 18 | 19 | # README: 20 | # The goal of this tools is only for developing purpose. 21 | # The full documented and well implemented version is going to be in PLASO: 22 | # https://code.google.com/p/plaso/ 23 | # http://plaso.kiddaland.net/ 24 | 25 | 26 | # Disclaimer: it only was probed in 10.9. 27 | 28 | import construct 29 | import os 30 | import re 31 | import sys 32 | import time 33 | 34 | # Magic header 35 | MAGIC = 'utmpx-1.00' 36 | MAGIC_HEX = '75746d70782d312e3030' 37 | 38 | # [HEADER] [STRUCT]* 39 | 40 | # Header of the UTMPX file 41 | MAC_UTMPX_HEADER = construct.Struct( 42 | 'header', 43 | construct.Bytes('magic', 10), 44 | construct.Padding(286), 45 | construct.ULInt16('id'), 46 | construct.Padding(622), 47 | construct.ULInt32('unknown1'), 48 | construct.ULInt32('unknown2'), 49 | construct.ULInt32('timestamp'), 50 | construct.Padding(324)) 51 | 52 | # Struct from one entry 53 | MAC_UTMPX_STRUCT = construct.Struct( 54 | 'utmpx_mac', 55 | construct.String('user', 256), 56 | construct.ULInt32('id'), 57 | construct.String('tty_name', 32), 58 | construct.ULInt32('pid'), 59 | construct.ULInt32('status_type'), 60 | construct.ULInt32('timestamp'), 61 | construct.ULInt32('microsecond'), 62 | construct.String('hostname', 256), 63 | construct.Padding(64)) 64 | 65 | # Status of the session 66 | MAC_STATUS_TYPE = { 67 | 0 : 'EMPTY', 68 | 1 : 'RUN_LVL', 69 | 2 : 'BOOT_TIME', 70 | 3 : 'OLD_TIME', 71 | 4 : 'NEW_TIME', 72 | 5 : 'INIT_PROCESS', 73 | 6 : 'LOGIN_PROCESS', 74 | 7 : 'USER_PROCESS', 75 | 8 : 'DEAD_PROCESS'} 76 | 77 | # By default where the file is. 78 | DIRNAME = '/private/var/run' 79 | FILENAME = 'utmpx' 80 | 81 | # Printing the file Header values. 82 | # 83 | # Args: 84 | # header: the struct header of the file. 85 | # path: the path of the file. 86 | def printHeader(header, path): 87 | txt_time = time.strftime("%a, %d %b %Y %H:%M:%S +0000", 88 | time.gmtime(header.timestamp)) 89 | print '\n UTMPX File: [{}]\n'.format(path) 90 | print ' Header:' 91 | print '\tID: {}'.format(header.id) 92 | print '\tUptimeTime: {} ({})\n'.format( 93 | txt_time, header.timestamp) 94 | 95 | # Print an entry (Session). 96 | # 97 | # Args: 98 | # entry: number of the entry. 99 | # user: user of the session. 100 | # terminal: name of the terminal. 101 | # hostname: the name of the host (source) 102 | # name_status: the text representation of the status. 103 | # num_status: the numerical representation of the status. 104 | # timestamp: the epoch timestamp. 105 | def printEntry(entry, user, terminal, hostname, name_status, num_status, timestamp): 106 | print '\tEntry: {}'.format(entry) 107 | print '\t* User: {}'.format(user) 108 | print '\t* Terminal: {}'.format(terminal) 109 | print '\t* Hostname: {}'.format(hostname) 110 | print '\t* Status: {0} ({1:#04x})'.format(name_status, num_status) 111 | print '\t* Timestamp: {} ({})'.format( 112 | time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(timestamp)), 113 | timestamp) 114 | print '\t------------------------------' 115 | 116 | # Read an entry. 117 | # 118 | # Args: 119 | # f: the utmpx file name. 120 | # entry_number: the number of the entry it reads. 121 | def ReadEntry(f, entry_number): 122 | data = f.read(MAC_UTMPX_STRUCT.sizeof()) 123 | # End of file 124 | if len(data) != MAC_UTMPX_STRUCT.sizeof(): 125 | return False 126 | try: 127 | entry = MAC_UTMPX_STRUCT.parse(data) 128 | except: 129 | print 'Unable to parse Mac OS X UTMPX event.' 130 | return True 131 | user, _, _ = entry.user.partition('\x00') 132 | if not user: 133 | user = 'N/A' 134 | terminal, _, _ = entry.tty_name.partition('\x00') 135 | if not terminal: 136 | terminal = 'N/A' 137 | hostname, _, _ = entry.hostname.partition('\x00') 138 | if not hostname: 139 | hostname = 'localhost' 140 | name_status = MAC_STATUS_TYPE.get(entry.status_type, 'N/A') 141 | printEntry(entry_number, user, terminal, hostname, name_status, entry.status_type, entry.timestamp) 142 | return True 143 | 144 | # Main 145 | def __init__(): 146 | try: 147 | if len(sys.argv) == 1: 148 | path = os.path.join(DIRNAME, FILENAME) 149 | elif len(sys.argv) == 2: 150 | path = sys.argv[1] 151 | else: 152 | path = '' 153 | f = open(path, 'rb') 154 | except IOError: 155 | print u'File {} not found'.format(path) 156 | exit(1) 157 | except: 158 | print u'Usage: python {} [file]'.format(sys.argv[0]) 159 | exit(1) 160 | 161 | try: 162 | header = MAC_UTMPX_HEADER.parse_stream(f) 163 | except: 164 | print 'Not a Mac UTMPX Header, unable to parse.' 165 | exit(1) 166 | 167 | if header.magic != MAGIC: 168 | print 'Not a valid Mac Os X UTMPX Header.' 169 | exit(1) 170 | 171 | printHeader(header, path) 172 | 173 | entry_number = 1 174 | result = ReadEntry(f, entry_number) 175 | while result: 176 | entry_number += 1 177 | result = ReadEntry(f, entry_number) 178 | 179 | 180 | f.close() 181 | 182 | __init__() 183 | -------------------------------------------------------------------------------- /plist_artifacts.py: -------------------------------------------------------------------------------- 1 | #!/usr/python 2 | # -*- coding: utf-8 -*- 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Mac OS X Artifacts.""" 15 | 16 | # MSc Project in Royal Holloway, University of London. 17 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 18 | 19 | import sys 20 | import plistlib 21 | from binplist import binplist 22 | 23 | name = sys.argv[1] 24 | fd = open(name, 'rb') 25 | plist = binplist.BinaryPlist(fd, False, False) 26 | 27 | print u'File: {}'.format(name) 28 | 29 | try: 30 | parsed_plist = plist.Parse() 31 | except binplist.FormatError, e: 32 | try: 33 | parsed_plist = plistlib.readPlist(name) 34 | except: 35 | print "Not a valid plist file." 36 | exit(1) 37 | 38 | # Time Machine // com.apple.TimeMachine.plist 39 | try: 40 | destinations = parsed_plist['Destinations'] 41 | except KeyError: 42 | destinations = None 43 | if destinations: 44 | for destination in destinations: 45 | uuid = destination['DestinationUUIDs'] 46 | if not uuid: 47 | uuid = 'Unknown device' 48 | print u'TimeMachine Device: {}'.format(uuid) 49 | for date in destination['SnapshotDates']: 50 | print u'Backup at {}'.format(date) 51 | 52 | # Bluetooth Device // com.apple.Bluetooth.plist 53 | try: 54 | devices = parsed_plist['DeviceCache'] 55 | except KeyError: 56 | devices = None 57 | if devices: 58 | for mac_device in devices: 59 | device = devices[mac_device] 60 | try: 61 | name = device['Name'] 62 | except KeyError: 63 | name = 'Unknown' 64 | timestamp = device['LastInquiryUpdate'] 65 | print u'Last bluetooth {}({}) connected at {}'.format( 66 | name, mac_device, timestamp) 67 | 68 | # Airport Wifi stored // com.apple.airport.preferences.plist 69 | try: 70 | wifis = parsed_plist['RememberedNetworks'] 71 | except KeyError: 72 | wifis = None 73 | if wifis: 74 | for wifi in wifis: 75 | timestamp = wifi['LastConnected'] 76 | ssid = wifi['SSIDString'] 77 | security = wifi['SecurityType'] 78 | print u'Last connection at {} in Wifi "{}" with security "{}"'.format( 79 | timestamp, ssid, security) 80 | 81 | # Mac OS X Updates // com.apple.SoftwareUpdate.plist 82 | try: 83 | updatefull = parsed_plist['LastFullSuccessfulDate'] 84 | print u'Full Mac OS X update at {}'.format(updatefull) 85 | except KeyError: 86 | pass 87 | try: 88 | update = parsed_plist['LastSuccessfulDate'] 89 | print u'Partial Mac OS X update at {}'.format(update) 90 | except KeyError: 91 | pass 92 | 93 | # /Users/user/Library/Preferences/ 94 | # Associate extension application // com.apple.spotlight.plist 95 | try: 96 | extensions = parsed_plist['UserShortcuts'] 97 | except KeyError: 98 | extensions = None 99 | if extensions: 100 | for name_extension in extensions: 101 | extension = extensions[name_extension] 102 | path = extension['PATH'] 103 | name = extension['DISPLAY_NAME'] 104 | last_used = extension['LAST_USED'] 105 | print u'Extension {} opened by {} ({}) was openned last time at {}'.format( 106 | name_extension, name, path, last_used) 107 | 108 | # /Users/user/Library/Preferences/ByHost/ 109 | # Apple Accounts // com.apple.coreservices.appleidauthenticationinfo.*.plist 110 | try: 111 | accounts = parsed_plist['Accounts'] 112 | _ = parsed_plist['AuthCertificates'] 113 | _ = parsed_plist['AccessorVersions'] 114 | except KeyError: 115 | accounts = None 116 | if accounts: 117 | for name_account in accounts: 118 | account = accounts[name_account] 119 | try: 120 | apple_id = account['AppleID'] 121 | except KeyError: 122 | break 123 | name = account['FirstName'] 124 | family_name = account['LastName'] 125 | uuid = ['AccountUUID'] 126 | creationTime = account['CreationDate'] 127 | lastTime = account['LastSuccessfulConnect'] 128 | validateTime = account['ValidationDate'] 129 | 130 | print u'Apple account {} ({} {}) created at {}'.format( 131 | name_account, name, family_name, creationTime) 132 | print u'Apple account {} ({} {}) last time used at {}'.format( 133 | name_account, name, family_name, lastTime) 134 | print u'Apple account {} ({} {}) last validated at {}'.format( 135 | name_account, name, family_name, validateTime) 136 | 137 | ################ NO TIMESTIME #################### 138 | # /Users/moxilo/Library/Preferences 139 | # Recent documents 140 | # com.apple.Console.LSSharedFileList.plist 141 | # com.apple.Preview.LSSharedFileList.plist 142 | # com.apple.TextEdit.LSSharedFileList.plist 143 | # com.apple.recentitems.plist 144 | try: 145 | documents = parsed_plist['RecentDocuments'] 146 | except KeyError: 147 | documents = None 148 | if documents: 149 | for doc in documents['CustomListItems']: 150 | name = name.split('/').pop().replace('.plist', '').replace('.LSSharedFileList', '') 151 | print u'Recent document in {}: {}'.format( 152 | name, doc['Name']) 153 | # TODO: Specific Binary Structure, it must be parsed. 154 | #print doc['Bookmark'] 155 | 156 | try: 157 | documents = parsed_plist['RecentApplications'] 158 | except KeyError: 159 | documents = None 160 | if documents: 161 | for doc in documents['CustomListItems']: 162 | name = name.split('/').pop() 163 | print u'Recent applications in {}: {}'.format( 164 | name, doc['Name']) 165 | # TODO: Specific Binary Structure, it must be parsed. 166 | #print doc['Bookmark'] 167 | 168 | -------------------------------------------------------------------------------- /asl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ Apple System Log Parser """ 15 | 16 | # MSc Project in Royal Holloway, University of London. 17 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 18 | 19 | # README: 20 | # The goal of this tools is only for developing purpose. 21 | # The full documented and well implemented version is going to be in PLASO: 22 | # https://code.google.com/p/plaso/ 23 | # http://plaso.kiddaland.net/ 24 | 25 | 26 | # Disclaimer: it only was probed in 10.8 and 10.9. 27 | 28 | import construct 29 | import os 30 | import struct 31 | import sys 32 | import time 33 | 34 | # Magic file number. 35 | ASL_MAGIC = 'ASL DB\x00\x00\x00\x00\x00\x00' 36 | 37 | # Priority levels. 38 | ASL_MESSAGE_PRIORITY = { 39 | 0 : 'EMERGENCY', 40 | 1 : 'ALERT', 41 | 2 : 'CRITICAL', 42 | 3 : 'ERROR', 43 | 4 : 'WARNING', 44 | 5 : 'NOTICE', 45 | 6 : 'INFO', 46 | 7 : 'DEBUG'} 47 | 48 | # ASL Required Structures. 49 | 50 | # ASL header file structure 51 | ASL_HEADER_STRUCT = construct.Struct( 52 | 'asl_header_struct', 53 | construct.String('magic', 12), 54 | construct.UBInt32('version'), 55 | construct.UBInt64('offset'), 56 | construct.UBInt64('timestamp'), 57 | construct.UBInt32('cache_size'), 58 | construct.UBInt64('last_offset'), 59 | construct.Padding(36)) 60 | 61 | # Record = [Heap][Record_Struct][Values] 62 | # Heap = [Group of Dyn_Value]* 63 | # Values = [ADDR_TXT][ADDR_TXT][ADDR_TXT][ADDR_TXT](2x[ADDR_TXT])* 64 | # (Host) (Sender) (Facility) (message) 65 | 66 | # Record Struct 67 | ASL_RECORD_STRUCT = construct.Struct( 68 | 'asl_record_struct', 69 | construct.Padding(2), 70 | construct.UBInt32('tam_entry'), 71 | construct.UBInt64('next_offset'), 72 | construct.UBInt64('ASLMessageID'), 73 | construct.UBInt64('timestamp'), 74 | construct.UBInt32('nanosec'), 75 | construct.UBInt16('level'), 76 | construct.UBInt16('flags'), 77 | construct.UBInt32('pid'), 78 | construct.UBInt32('uid'), 79 | construct.UBInt32('gid'), 80 | construct.UBInt32('read_uid'), 81 | construct.UBInt32('read_gid'), 82 | construct.UBInt64('ref_pid')) 83 | 84 | # Pointer Values 85 | ASL_RECORD_ADDR_TXT = construct.Struct( 86 | 'addr_or_text', construct.String('addr_txt', 8)) 87 | 88 | # Pointer Dynamic Value 89 | ASL_RECORD_DYN_VALUE = construct.Struct( 90 | 'asl_record_text_header', 91 | construct.Padding(2), 92 | construct.PascalString( 93 | 'value', length_field = construct.UBInt32('length'))) 94 | 95 | # Print the header of the file 96 | def printHeader(header): 97 | print "\nASL Header:" 98 | print " Version: " + str(header.version) 99 | print " Timestamp: " + str(header.timestamp) 100 | print " FirstRecord: " + hex(header.offset) 101 | print " LastRecord: " + hex(header.last_offset) + "\n" 102 | 103 | # Print a record value 104 | # 105 | # Args: 106 | # record_header: values from the Record_Struct part. 107 | # values: values from the bottom part (Values) 108 | # pos: where the Record_Structure starts in the file. 109 | def printRecord(record_header, values, pos): 110 | # Static part of the entry 111 | human_time = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(record_header.timestamp)) 112 | print '\t Record in: {}'.format(hex(pos)) 113 | print '\t * Next record in: {}'.format(hex(record_header.next_offset)) 114 | print '\t * ASLMessageID: {}'.format(record_header.ASLMessageID) 115 | print '\t * Timestamp: {0} ({1})'.format(human_time, record_header.timestamp) 116 | print '\t * Level: {0} ({1}), PID: {2}'.format(ASL_MESSAGE_PRIORITY[record_header.level], 117 | record_header.level, record_header.pid) 118 | fieldsID = ['UID: {}'.format(record_header.uid), 'GID: {}'.format(record_header.gid)] 119 | # If it is a valid read u/gid: 120 | if record_header.read_uid != int('ffffffff', 16): 121 | fieldsID.append('Read_UID: {}'.format(record_header.read_uid)) 122 | if record_header.read_gid != int('ffffffff', 16): 123 | fieldsID.append('Read_GID: {}'.format(record_header.read_gid)) 124 | print '\t * {}'.format(', '.join(fieldsID)) 125 | 126 | # Dynamic part of the entry. 127 | # Host, Sender, Facility, Message, Name_Field1, Field1, Name_Field2, Field2, ... 128 | print '\t * Host: {0}'.format(values[0].partition('\x00')[0]) 129 | print '\t * Sender: {0}'.format(values[1].partition('\x00')[0]) 130 | print '\t * Facility: {0}'.format(values[2].partition('\x00')[0]) 131 | print '\t * Message: {0}'.format(values[3].partition('\x00')[0]) 132 | cont = 4 133 | while cont < (len(values) - 1): 134 | print '\t * {0}: {1}'.format(values[cont].partition('\x00')[0], 135 | values[cont+1].partition('\x00')[0]) 136 | cont += 2 137 | print '\t------------------------------------------------------' 138 | 139 | # Main program 140 | def __init__(): 141 | if len(sys.argv) != 2: 142 | print 'Use: python {0} ASLfile'.format(sys.argv[0]) 143 | exit(1) 144 | log = sys.argv[1] 145 | try: 146 | f = open(log, 'rb') 147 | except: 148 | print '[Error] The file ASL does not exist' 149 | exit(1) 150 | 151 | print '\nParsing the ASL file [{}].'.format(log) 152 | 153 | try: 154 | header = ASL_HEADER_STRUCT.parse_stream(f) 155 | except: 156 | print "[Error]It is not a ASL file, ASL Header not valid." 157 | exit(1) 158 | if header.magic != ASL_MAGIC: 159 | print "[Error]It is not a ASL file, ASL_MAGIC invalid." 160 | exit(1) 161 | 162 | printHeader(header) 163 | 164 | offset = header.offset 165 | last_offset = header.last_offset 166 | last = False 167 | 168 | # Parsing the dynamic fields from the entry 169 | while offset <= last_offset and last == False: 170 | # The heap of the entry is saved to try to avoid seek (performance issue) 171 | dynamic_start = f.tell() 172 | dynamic_part = f.read(offset - f.tell()) 173 | 174 | record_header = ASL_RECORD_STRUCT.parse_stream(f) 175 | 176 | # -2 -> + 6 - 8 177 | # +6 because the header already counts the padding + tam entry. 178 | # -8 because the last 8 byte register is a pointer to the previous entry. 179 | tam_entry = record_header.tam_entry - ASL_RECORD_STRUCT.sizeof() - 2 180 | 181 | # Dynamic part of the entry 182 | values = [] 183 | read_value = 0 184 | while tam_entry > 0: 185 | #print tam_entry 186 | data = ASL_RECORD_ADDR_TXT.parse_stream(f) 187 | tam_entry -= 8 188 | # If not direction or data, jump to the next 8 bytes 189 | # HELP: exists another option better than need to use the encode('hex')?? 190 | if data.addr_txt.encode('hex') != '0000000000000000': 191 | # If it is direction then jump, if it is not, then read the data 192 | if data.addr_txt.encode('hex')[0:1] != '8': 193 | # If the pointer points to the dynamic header of the entry 194 | pos = int(data.addr_txt.encode('hex'), 16) - dynamic_start 195 | if pos >= 0: 196 | values.append((ASL_RECORD_DYN_VALUE.parse(dynamic_part[pos:])).value) 197 | else: 198 | # Only if it is a pointer that points to the heap to another entry we use 199 | # the seek method, avoiding performance issues 200 | pos = f.tell() 201 | f.seek(int(data.addr_txt.encode('hex'), 16)) 202 | values.append((ASL_RECORD_DYN_VALUE.parse_stream(f)).value) 203 | # Come back to the position in the entry 204 | f.seek(pos) 205 | else: 206 | values.append(data.addr_txt[1:]) 207 | 208 | # Print the record 209 | printRecord(record_header, values, offset) 210 | 211 | #Read the last 8 bytes that points to the previous position 212 | f.read(8) 213 | 214 | # Last entry 215 | if record_header.next_offset < offset: 216 | last = True 217 | else: 218 | # Jump to the next entry 219 | offset = record_header.next_offset 220 | 221 | __init__() 222 | -------------------------------------------------------------------------------- /keychains.py: -------------------------------------------------------------------------------- 1 | #!/usr/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Cups Reading Control Files.""" 16 | 17 | # MSc Project in Royal Holloway, University of London. 18 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 19 | 20 | 21 | import construct 22 | import datetime 23 | import os 24 | import sys 25 | 26 | KEYCHAIN_MAGIC_HEADER = 'kych' 27 | KEYCHAIN_MAJOR_VERSION = 1 28 | KEYCHAIN_MINOR_VERSION = 0 29 | 30 | # DB HEADER 31 | KEYCHAIN_DB_HEADER = construct.Struct( 32 | 'db_header', 33 | construct.String('magic', 4), 34 | construct.UBInt16('major_version'), 35 | construct.UBInt16('minor_version'), 36 | construct.UBInt32('header_size'), 37 | construct.UBInt32('schema_offset'), 38 | construct.Padding(4)) 39 | 40 | # DB SCHEMA 41 | KEYCHAIN_DB_SCHEMA = construct.Struct( 42 | 'db_schema', 43 | construct.UBInt32('size'), 44 | construct.UBInt32('number_of_tables')) 45 | # For each umber_of_tables, the schema has a TABLE_OFFSET with the 46 | # offset starting in the DB_SCHEMA. 47 | TABLE_OFFSET = construct.UBInt32('table_offset') 48 | 49 | # TABLE 50 | TABLE_RECORD_TYPE = { 51 | 0: u'Schema information', 52 | 1: u'Schema indexes', 53 | 2: u'Schema attributes', 54 | 3: u'Schema parsing module', 55 | 10: u'Temporary table type', 56 | 11: u'Certificates', 57 | 12: u'Certificate Revocation List', 58 | 13: u'Policy', 59 | 14: u'Generic information', 60 | 15: u'Public key', 61 | 16: u'Private key', 62 | 17: u'Symmetric key', 63 | 18: u'Temporal table', 64 | 2147483648: u'Application password', 65 | 2147483649: u'Internet password', 66 | 2147483650: u'Apple share password', 67 | 2147483651: u'User X509 certificate', 68 | 2147483652: u'X509 revocate list', 69 | 2147483653: u'Unlock referral', 70 | 2147483654: u'Extended attribute', 71 | 2147487744: u'X509 Certificates', 72 | 2147516416: u'Metadata information'} 73 | 74 | TABLE_HEADER = construct.Struct( 75 | 'table_header', 76 | construct.UBInt32('table_size'), 77 | construct.UBInt32('record_type'), 78 | construct.UBInt32('number_of_records'), 79 | construct.UBInt32('first_record'), 80 | construct.UBInt32('index_offset'), 81 | construct.Padding(4), 82 | construct.UBInt32('recordnumbercount')) 83 | 84 | # RECORD 85 | TEXT = construct.PascalString( 86 | 'text', length_field = construct.UBInt32('length')) 87 | TIME = construct.Struct( 88 | 'timestamp', 89 | construct.String('year', 4), 90 | construct.String('month', 2), 91 | construct.String('day', 2), 92 | construct.String('hour', 2), 93 | construct.String('minute', 2), 94 | construct.String('second', 2), 95 | construct.Padding(2)) 96 | TYPE_TEXT = construct.String('type', 4) 97 | RECORD_HEADER = construct.Struct( 98 | 'record_entry', 99 | construct.UBInt32('entry_length'), 100 | construct.Padding(12), 101 | construct.UBInt32('ssgp_length'), 102 | construct.Padding(4), 103 | construct.UBInt32('creation_time'), 104 | construct.UBInt32('last_mod_time'), 105 | construct.UBInt32('text_description'), 106 | construct.Padding(16), 107 | construct.UBInt32('entry_name'), 108 | construct.Padding(20), 109 | construct.UBInt32('account_name'), 110 | construct.Padding(4), 111 | construct.UBInt32('where'), 112 | construct.UBInt32('protocol'), 113 | construct.UBInt32('type'), 114 | construct.Padding(4), 115 | construct.UBInt32('url')) 116 | 117 | # IPython.embed() 118 | f = open(sys.argv[1], 'rb') 119 | 120 | db_header = KEYCHAIN_DB_HEADER.parse_stream(f) 121 | if (db_header.minor_version != KEYCHAIN_MINOR_VERSION or 122 | db_header.major_version != KEYCHAIN_MAJOR_VERSION or 123 | db_header.magic != KEYCHAIN_MAGIC_HEADER): 124 | print u'It is not a valid Keychain file' 125 | exit(1) 126 | 127 | # From the schema we get the number of tables and where these tables are. 128 | db_schema = KEYCHAIN_DB_SCHEMA.parse_stream(f) 129 | table_offsets = [] 130 | print u'Number of tables: {}.'.format(db_schema.number_of_tables) 131 | for i in range(db_schema.number_of_tables): 132 | table_offsets.append(TABLE_OFFSET.parse_stream(f) + KEYCHAIN_DB_HEADER.sizeof()) 133 | 134 | 135 | for table_offset in table_offsets: 136 | print u'Table at {0}(0x{0:x})'.format(table_offset) 137 | f.seek(table_offset) 138 | table = TABLE_HEADER.parse_stream(f) 139 | 140 | ''' 141 | # Application 142 | if table.record_type == 2147483648: 143 | print u'\tRecord type: {}'.format(TABLE_RECORD_TYPE[table.record_type]) 144 | print u'\tSize: {}'.format(table.table_size) 145 | print u'\tNumber of records: {}'.format(table.number_of_records) 146 | print u'\tNumber of records count: {}'.format(table.recordnumbercount) 147 | print u'\tFirst record: {0}(0x{0:x}) = {1}'.format( 148 | table.first_record, table_offset + table.first_record) 149 | print u'\tFirst record index: {0}(0x{0:x}) = {1}'.format( 150 | table.index_offset, table_offset + table.index_offset) 151 | ''' 152 | 153 | if table.record_type == 2147483648 or table.record_type == 2147483649: 154 | print u'\tRecord type: {}'.format(TABLE_RECORD_TYPE[table.record_type]) 155 | print u'\tSize: {}'.format(table.table_size) 156 | print u'\tNumber of records: {}'.format(table.number_of_records) 157 | print u'\tNumber of records count: {}'.format(table.recordnumbercount) 158 | print u'\tFirst record: {0}(0x{0:x}) = {1}'.format( 159 | table.first_record, table_offset + table.first_record) 160 | print u'\tFirst record index: {0}(0x{0:x}) = {1}'.format( 161 | table.index_offset, table_offset + table.index_offset) 162 | 163 | f.seek(table_offset + table.first_record) 164 | for i in range(table.number_of_records): 165 | offset = f.tell() 166 | print u'\t\tRecord at: {0}(0x{0:x})'.format(offset) 167 | record = RECORD_HEADER.parse_stream(f) 168 | 169 | # Timestamps 170 | jump = record.creation_time - RECORD_HEADER.sizeof() - 1 171 | f.seek(jump, os.SEEK_CUR) 172 | creation_time = TIME.parse_stream(f) 173 | print u'\t\tCreation time: {}-{}-{} {}:{}:{}'.format( 174 | creation_time.year, creation_time.month, creation_time.day, 175 | creation_time.hour, creation_time.minute, creation_time.second) 176 | 177 | jump = record.last_mod_time - (f.tell() - offset) - 1 178 | last_mod_time = TIME.parse_stream(f) 179 | print u'\t\tLast Modification time: {}-{}-{} {}:{}:{}'.format( 180 | last_mod_time.year, last_mod_time.month, last_mod_time.day, 181 | last_mod_time.hour, last_mod_time.minute, last_mod_time.second) 182 | 183 | # Description name 184 | if record.text_description: 185 | jump = record.text_description - (f.tell() - offset) - 1 186 | f.seek(jump, os.SEEK_CUR) 187 | text_description = TEXT.parse_stream(f) 188 | print u'\t\tDescription: {}'.format(text_description) 189 | 190 | # Name 191 | jump = record.entry_name - (f.tell() - offset) - 1 192 | f.seek(jump, os.SEEK_CUR) 193 | entry_name = TEXT.parse_stream(f) 194 | print u'\t\tName: {}'.format(entry_name) 195 | 196 | # Account 197 | jump = record.account_name - (f.tell() - offset) - 1 198 | f.seek(jump, os.SEEK_CUR) 199 | account_name = TEXT.parse_stream(f) 200 | print u'\t\tAccount: {}'.format(account_name) 201 | 202 | # Where 203 | if record.where: 204 | jump = record.where - (f.tell() - offset) - 1 205 | f.seek(jump, os.SEEK_CUR) 206 | where = TEXT.parse_stream(f) 207 | jump = record.protocol - (f.tell() - offset) - 1 208 | f.seek(jump, os.SEEK_CUR) 209 | protocol = TYPE_TEXT.parse_stream(f) 210 | jump = record.type - (f.tell() - offset) - 1 211 | f.seek(jump, os.SEEK_CUR) 212 | type = TEXT.parse_stream(f) 213 | jump = record.url - (f.tell() - offset) - 1 214 | f.seek(jump, os.SEEK_CUR) 215 | url = TEXT.parse_stream(f) 216 | print u'\t\tWhere: {}{} ({}, {})'.format(where, url, protocol, type) 217 | 218 | f.seek(record.entry_length + offset) 219 | print "\t\t------------------------" 220 | 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /mac_recent.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import construct 5 | import plistlib 6 | import sys 7 | import time 8 | from binplist import binplist 9 | 10 | HEADER = construct.Struct( 11 | 'header', 12 | construct.String('magic', 4), 13 | construct.ULInt32('length'), 14 | construct.ULInt32('unknown1'), 15 | construct.Padding(36), 16 | construct.ULInt32('unknown2')) 17 | TOKEN = construct.Struct( 18 | 'token', 19 | construct.ULInt32('length'), 20 | construct.ULInt32('type')) 21 | STR = construct.String('string', 4) 22 | INT = construct.ULInt32('integer') 23 | INT64 = construct.ULInt64('integer') 24 | 25 | def ParseBookmark(bookmark): 26 | header = HEADER.parse(bookmark) 27 | if 'book' != header.magic: 28 | return 29 | 30 | bookmark = bookmark[HEADER.sizeof():] 31 | 32 | token = TOKEN.parse(bookmark) 33 | bookmark = bookmark[TOKEN.sizeof():] 34 | 35 | # PATH 36 | path = '' 37 | elem = 0 38 | while(token.type == 257): 39 | bookmark, value = getData(bookmark, token.length, token.type) 40 | path += '/' + value 41 | elem += 1 42 | token = TOKEN.parse(bookmark) 43 | bookmark = bookmark[TOKEN.sizeof():] 44 | print u'\tPath: {}'.format(path) 45 | 46 | # INODE PATH 47 | # Element 1537 48 | if token.type == 1537: 49 | bookmark, value = getData(bookmark, token.length, token.type) 50 | token = TOKEN.parse(bookmark) 51 | bookmark = bookmark[TOKEN.sizeof():] 52 | # 772 elements 53 | inodes = [] 54 | while(token.type == 772): 55 | bookmark, value = getData(bookmark, token.length, token.type) 56 | inodes.append(value) 57 | token = TOKEN.parse(bookmark) 58 | bookmark = bookmark[TOKEN.sizeof():] 59 | if inodes: 60 | print u'\tInode Path: /{}'.format(u'/'.join(inodes)) 61 | if len(inodes) != elem: 62 | print "\tWarning: the original path and the new path are different, check Sandbox Path." 63 | # Element 1537 64 | bookmark, value = getData(bookmark, token.length, token.type) 65 | token = TOKEN.parse(bookmark) 66 | bookmark = bookmark[TOKEN.sizeof():] 67 | 68 | # Timestamp 69 | bookmark, value = getData(bookmark, token.length, token.type) 70 | token = TOKEN.parse(bookmark) 71 | bookmark = bookmark[TOKEN.sizeof():] 72 | # 513 73 | bookmark, value = getData(bookmark, token.length, token.type) 74 | token = TOKEN.parse(bookmark) 75 | bookmark = bookmark[TOKEN.sizeof():] 76 | 77 | # User ID 78 | while(token.type == 771): 79 | bookmark, value = getData(bookmark, token.length, token.type) 80 | token = TOKEN.parse(bookmark) 81 | bookmark = bookmark[TOKEN.sizeof():] 82 | bookmark, value = getData(bookmark, token.length, token.type) 83 | print u'\tUser ID: {}'.format(value) 84 | token = TOKEN.parse(bookmark) 85 | bookmark = bookmark[TOKEN.sizeof():] 86 | 87 | # If external device 88 | while(token.type == 772): 89 | bookmark, value = getData(bookmark, token.length, token.type) 90 | token = TOKEN.parse(bookmark) 91 | bookmark = bookmark[TOKEN.sizeof():] 92 | if token.type == 771: 93 | bookmark, value = getData(bookmark, token.length, token.type) 94 | print u'\tUser ID: {}'.format(value[1:-1]) 95 | token = TOKEN.parse(bookmark) 96 | bookmark = bookmark[TOKEN.sizeof():] 97 | elif(token.type == 1024): 98 | bookmark, value = getData(bookmark, token.length, token.type) 99 | token = TOKEN.parse(bookmark) 100 | bookmark = bookmark[TOKEN.sizeof():] 101 | # 513 102 | bookmark, value = getData(bookmark, token.length, token.type) 103 | token = TOKEN.parse(bookmark) 104 | bookmark = bookmark[TOKEN.sizeof():] 105 | # External montpoint 106 | if token.type == 257: 107 | bookmark, value = getData(bookmark, token.length, token.type) 108 | print u'\tExternal device: {}'.format(value) 109 | token = TOKEN.parse(bookmark) 110 | bookmark = bookmark[TOKEN.sizeof():] 111 | 112 | # Hardisk Partition 113 | if token.type == 257: 114 | bookmark, value = getData(bookmark, token.length, token.type) 115 | print u'\tHD Partition Root Name: {}'.format(value) 116 | token = TOKEN.parse(bookmark) 117 | bookmark = bookmark[TOKEN.sizeof():] 118 | 119 | # UUID 120 | if token.type == 772: 121 | bookmark, value = getData(bookmark, token.length, token.type) 122 | token = TOKEN.parse(bookmark) 123 | bookmark = bookmark[TOKEN.sizeof():] 124 | # 1024 125 | bookmark, value = getData(bookmark, token.length, token.type) 126 | token = TOKEN.parse(bookmark) 127 | bookmark = bookmark[TOKEN.sizeof():] 128 | if token.type == 257: 129 | bookmark, value = getData(bookmark, token.length, token.type) 130 | print u'\tHD Root UUID: {}'.format(value) 131 | token = TOKEN.parse(bookmark) 132 | bookmark = bookmark[TOKEN.sizeof():] 133 | 134 | # 513 135 | if token.type == 513: 136 | bookmark, value = getData(bookmark, token.length, token.type) 137 | # print u'\tUnknown {}: {}'.format(token.type, value) 138 | token = TOKEN.parse(bookmark) 139 | bookmark = bookmark[TOKEN.sizeof():] 140 | # Mount disk 141 | if token.type == 257: 142 | bookmark, value = getData(bookmark, token.length, token.type) 143 | print u'\tHD Root mount in: {}'.format(value) 144 | token = TOKEN.parse(bookmark) 145 | bookmark = bookmark[TOKEN.sizeof():] 146 | 147 | # 1281 148 | if token.type == 1281: 149 | bookmark, value = getData(bookmark, token.length, token.type) 150 | token = TOKEN.parse(bookmark) 151 | bookmark = bookmark[TOKEN.sizeof():] 152 | # 513 153 | if token.type == 513: 154 | bookmark, value = getData(bookmark, token.length, 257) 155 | elements = value.split(';') 156 | sandbox_name = elements[0] 157 | sandbox_path = elements[len(elements)-1] 158 | print u'\tSandbox ID: {}'.format(sandbox_name) 159 | print u'\tSandbox Path: {}'.format(sandbox_path) 160 | token = TOKEN.parse(bookmark) 161 | bookmark = bookmark[TOKEN.sizeof():] 162 | 163 | 164 | def getData(data, length, type): 165 | times = length // 4 166 | if length % 4 > 0: 167 | times += 1 168 | # Text 169 | if type == 257: 170 | value = '' 171 | for i in range(times): 172 | value += u'{}'.format(STR.parse(data)) 173 | data = data[4:] 174 | value, _, _ = value.partition('\x00') 175 | return (data, u'{}'.format(value)) 176 | # Timestamp 177 | elif type == 1024: 178 | t = INT.parse(data) 179 | t64 = INT64.parse(data) 180 | data = data[8:] 181 | return (data, '{}, {}'.format(t, t64)) 182 | else: 183 | value = [] 184 | for i in range(times): 185 | value.append(INT.parse(data)) 186 | data = data[4:] 187 | # Inode 188 | if type == 772: 189 | return (data, u'{}'.format(value[0])) 190 | return (data, u'{}'.format(value)) 191 | 192 | def DebugParseBookmark(bookmark): 193 | print "-----------" 194 | header = HEADER.parse(bookmark) 195 | if 'book' != header.magic: 196 | return 197 | bookmark = bookmark[HEADER.sizeof():] 198 | while (len(bookmark) > 0): 199 | token = TOKEN.parse(bookmark) 200 | bookmark = bookmark[TOKEN.sizeof():] 201 | times = token.length // 4 202 | if token.length % 4 > 0: 203 | times += 1 204 | if token.type == 257: # or token.type == 513: 205 | value = '' 206 | for i in range(times): 207 | value += u'{}'.format(STR.parse(bookmark)) 208 | bookmark = bookmark[4:] 209 | print u'Text: {}'.format(value) 210 | else: 211 | value = [] 212 | for i in range(times): 213 | value.append(INT.parse(bookmark)) 214 | bookmark = bookmark[4:] 215 | if token.type == 1024: 216 | t = value.pop(0) + 978307200 217 | timestamp = time.strftime( 218 | '%Y-%m-%d %H:%M:%S', time.localtime(t)) 219 | print u'Timestamp: {}, extra: {}'.format( 220 | timestamp, value) 221 | else: 222 | print u'Unknown type {}({}): {}'.format( 223 | token.type, hex(token.type), value) 224 | print "--------///-------" 225 | 226 | name = sys.argv[1] 227 | fd = open(name, 'rb') 228 | plist = binplist.BinaryPlist(fd, False, False) 229 | 230 | print u'File: {}\n'.format(name) 231 | 232 | try: 233 | parsed_plist = plist.Parse() 234 | except binplist.FormatError, e: 235 | parsed_plist = plistlib.readPlist(name) 236 | 237 | 238 | # /Users/moxilo/Library/Preferences 239 | # Recent documents 240 | # com.apple.PROGRAM.LSSharedFileList.plist 241 | # com.apple.recentitems.plist 242 | try: 243 | documents = parsed_plist['RecentDocuments'] 244 | except KeyError: 245 | documents = None 246 | if documents: 247 | for doc in documents['CustomListItems']: 248 | name = name.split('/').pop().replace('.plist', '').replace('.LSSharedFileList', '') 249 | info_program = name.split('.') 250 | company = info_program[1] 251 | program = info_program[2] 252 | print u'\tRecent document open by {}({}): {}'.format( 253 | program, company, doc['Name']) 254 | # TODO: Specific Binary Structure, it must be parsed. 255 | ParseBookmark(doc['Bookmark']) 256 | print '' 257 | 258 | try: 259 | documents = parsed_plist['RecentApplications'] 260 | except KeyError: 261 | documents = None 262 | if documents: 263 | for doc in documents['CustomListItems']: 264 | name = name.split('/').pop() 265 | print u'Recent applications in {}: {}'.format( 266 | name, doc['Name']) 267 | # TODO: Specific Binary Structure, it must be parsed. 268 | ParseBookmark(doc['Bookmark']) 269 | 270 | -------------------------------------------------------------------------------- /bsm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ Basic Security Module """ 15 | 16 | # Reference (A lot of fields are not exactly as the doc says) 17 | # https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man5/audit.log.5.html 18 | 19 | # MSc Project in Royal Holloway, University of London. 20 | __author__ = 'Joaquin Moreno Garijo (bastionado@gmail.com)' 21 | 22 | # README: 23 | # The goal of this tools is only for developing purposes. 24 | # The full documented and well implemented version is going to be in PLASO: 25 | # https://code.google.com/p/plaso/ 26 | # http://plaso.kiddaland.net/ 27 | 28 | # Disclaimer: it only was probed in 10.8, 10.9 and OpenBSM. 29 | 30 | # TODO: Not all the structures are implemented. If you find a non implemented structure, please report to 31 | # me the ID of the structure an a RAW example of this structure (copy 50 bytes from the address of 32 | # the structure. As an example, if the program tell you a [WARNING] providing an integer position 33 | # of the structure, you only need to calculate the size as a integer + 50 and then "xxd -l size file" 34 | 35 | import construct 36 | import datetime 37 | import os 38 | import socket 39 | import struct 40 | import sys 41 | import time 42 | 43 | ##### CONSTANT ##### 44 | 45 | # BSM supported version (0x0b = 11) 46 | AUDIT_HEADER_VERSION = 11 47 | 48 | # Magic Trail Header 49 | BSM_TOKEN_TRAILER_MAGIC = 'b105' 50 | 51 | # IP Version constants. 52 | AU_IPv4 = 4 53 | AU_IPv6 = 16 54 | 55 | # Arbitrary tokens. 56 | # Type of data to print in a BSM_TOKEN_DATA. 57 | BSM_TOKEN_DATA_TYPE = { 58 | 0: u'AUR_CHAR', 59 | 1: u'AUR_SHORT', 60 | 2: u'AUR_INT32'} 61 | 62 | BSM_TOKEN_DATA_PRINT = { 63 | 0: u'Binary', 64 | 1: u'Octal', 65 | 2: u'Decimal', 66 | 3: u'Hexadecimal', 67 | 4: u'String'} 68 | 69 | # Numeric ERRORS representation, read it in OpenBSM project: "audit_errno.h". 70 | # TODO: I have been checked some of them against Mac OSX and I am changing 71 | # the name to be more clossed to Mac OS X and be more understandable. 72 | BSM_ERRORS = { 73 | 0: u'Success', 1: u'Operation not permitted', 74 | 2: u'No such file or directory', 75 | 3: u'No such process', 4: u'Interrupted system call', 76 | 5: u'Input/output error', 6: u'Device not configured', 77 | 7: u'Argument list too long', 8: u'Exec format error', 78 | 9: u'Bad file descriptor', 10: u'No child processes', 79 | 11: u'Resource temporarily unavailable', 12: u'Cannot allocate memory', 80 | 13: u'Permission denied', 14: u'Bad address', 81 | 15: u'Block device required', 16: u'Device busy', 17: u'File exists', 82 | 18: u'ross-device link', 19: u'Operation not supported by device', 83 | 20: u'Not a directory', 21: u'Is a directory', 22: u'Invalid argument', 84 | 23: u'Too many open files in system', 85 | 24: u'Too many open files', 25: u'Inappropriate ioctl for device', 86 | 26: u'Text file busy', 27: u'File too large', 87 | 28: u'No space left on device', 29: u'Illegal seek', 88 | 30: u'Read-only file system', 31: u'Too many links', 89 | 32: u'Broken pipe', 33: u'Numerical argument out of domain', 90 | 34: u'Result too large', 35: u'No message of desired type', 91 | 36: u'Identifier removed', 45: u'Resource deadlock avoided', 92 | 46: u'No locks available', 47: u'Operation canceled', 93 | 48: u'Operation not supported', 49: u'Disc quota exceeded', 94 | 66: u'Too many levels of remote in path', 67: u'Link has been severed', 95 | 71: u'Protocol error', 74: u'Multihop attempted', 96 | 77: u'Bad message', 78: u'File name too long', 97 | 79: u'Value too large to be stored in data type', 98 | 88: u'Illegal byte sequence', 89: u'Function not implemented', 99 | 90: u'Too many levels of symbolic links', 91: u'Restart syscall', 100 | 93: u'Directory not empty', 94: u'Too many users', 101 | 95: u'Socket operation on non-socket', 102 | 96: u'Destination address required', 97: u'Message too long', 103 | 98: u'Protocol wrong type for socket', 104 | 99: u'Protocol not available', 120: u'Protocol not supported', 105 | 121: u'Socket type not supported', 122: u'Operation not supported', 106 | 123: u'Protocol family not supported', 107 | 124: u'Address family not supported by protocol family', 108 | 125: u'Address already in use', 126: u'Can\'t assign requested address', 109 | 127: u'Network is down', 128: u'Network unreachable', 110 | 129: u'Network dropped connection on reset', 111 | 130: u'Software caused connection abort', 112 | 131: u'Connection reset by peer', 132: u'No buffer space available', 113 | 133: u'Socket is already connected', 134: u'Socket is not connected', 114 | 143: u'Can\'t send after socket shutdown', 115 | 144: u'Too many references: can\'t splice', 145: u'Operation timed out', 116 | 146: u'Connection refused', 147: u'Host is down', 117 | 148: u'No route to host', 149: u'Operation already in progress', 118 | 150: u'Operation now in progress', 151: u'Stale NFS file handle', 119 | 190: u'PROCLIM', 191: u'BADRPC', 192: u'RPCMISMATCH', 120 | 193: u'PROGUNAVAIL', 194: u'PROGMISMATCH', 195: u'PROCUNAVAIL', 121 | 196: u'FTYPE', 197: u'AUTH', 198: u'NEEDAUTH', 199: u'NOATTR', 122 | 200: u'DOOFUS', 201: u'USTRETURN', 202: u'NOIOCTL', 203: u'DIRIOCTL', 123 | 204: u'PWROFF', 205: u'DEVERR', 206: u'BADEXEC', 207: u'BADARCH', 124 | 208: u'SHLIBVERS', 209: u'BADMACHO', 210: u'POLICY'} 125 | 126 | # Numeric PROTOCOLS representation, read in OpenBSM project: "audit_fcntl.h". 127 | # TODO: not checked 128 | BSM_PROTOCOLS = { 129 | 0: u'UNSPEC', 1: u'LOCAL', 2: u'INET', 3: u'IMPLINK', 4: u'PUP', 130 | 5: u'CHAOS', 6: u'NS', 8: u'ECMA', 9: u'DATAKIT', 10: u'CCITT', 131 | 11: u'SNA', 12: u'DECnet', 13: u'DLI', 14: u'LAT', 15: u'HYLINK', 132 | 16: u'APPLETALK', 19: u'OSI', 23: u'IPX', 24: u'ROUTE', 133 | 25: u'LINK', 26: u'INET6', 27: u'KEY', 500: u'NETBIOS', 134 | 501: u'ISO', 502: u'XTP', 503: u'COIP', 504: u'CNT', 505: u'RTIP', 135 | 506: u'SIP', 507: u'PIP', 508: u'ISDN', 509: u'E164', 136 | 510: u'NATM', 511: u'ATM', 512: u'NETGRAPH', 513: u'SLOW', 137 | 514: u'CLUSTER', 515: u'ARP', 516: u'BLUETOOTH'} 138 | 139 | # Text representation of the audit event (/etc/security/audit_event) 140 | # Depends on the Mac OS X version. 141 | BSM_AUDIT_EVENT = { 142 | 0: u'indir system call', 143 | 1: u'exit(2)', 144 | 2: u'fork(2)', 145 | 3: u'open(2) - attr only', 146 | 4: u'creat(2)', 147 | 5: u'link(2)', 148 | 6: u'unlink(2)', 149 | 7: u'exec(2)', 150 | 8: u'chdir(2)', 151 | 9: u'mknod(2)', 152 | 10: u'chmod(2)', 153 | 11: u'chown(2)', 154 | 12: u'umount(2) - old version', 155 | 13: u'junk', 156 | 14: u'access(2)', 157 | 15: u'kill(2)', 158 | 16: u'stat(2)', 159 | 17: u'lstat(2)', 160 | 18: u'acct(2)', 161 | 19: u'mctl(2)', 162 | 20: u'reboot(2)', 163 | 21: u'symlink(2)', 164 | 22: u'readlink(2)', 165 | 23: u'execve(2)', 166 | 24: u'chroot(2)', 167 | 25: u'vfork(2)', 168 | 26: u'setgroups(2)', 169 | 27: u'setpgrp(2)', 170 | 28: u'swapon(2)', 171 | 29: u'sethostname(2)', 172 | 30: u'fcntl(2)', 173 | 31: u'setpriority(2)', 174 | 32: u'connect(2)', 175 | 33: u'accept(2)', 176 | 34: u'bind(2)', 177 | 35: u'setsockopt(2)', 178 | 36: u'vtrace(2)', 179 | 37: u'settimeofday(2)', 180 | 38: u'fchown(2)', 181 | 39: u'fchmod(2)', 182 | 40: u'setreuid(2)', 183 | 41: u'setregid(2)', 184 | 42: u'rename(2)', 185 | 43: u'truncate(2)', 186 | 44: u'ftruncate(2)', 187 | 45: u'flock(2)', 188 | 46: u'shutdown(2)', 189 | 47: u'mkdir(2)', 190 | 48: u'rmdir(2)', 191 | 49: u'utimes(2)', 192 | 50: u'adjtime(2)', 193 | 51: u'setrlimit(2)', 194 | 52: u'killpg(2)', 195 | 53: u'nfs_svc(2)', 196 | 54: u'statfs(2)', 197 | 55: u'fstatfs(2)', 198 | 56: u'unmount(2)', 199 | 57: u'async_daemon(2)', 200 | 58: u'nfs_getfh(2)', 201 | 59: u'setdomainname(2)', 202 | 60: u'quotactl(2)', 203 | 61: u'exportfs(2)', 204 | 62: u'mount(2)', 205 | 63: u'semsys(2)', 206 | 64: u'msgsys(2)', 207 | 65: u'shmsys(2)', 208 | 66: u'bsmsys(2)', 209 | 67: u'rfssys(2)', 210 | 68: u'fchdir(2)', 211 | 69: u'fchroot(2)', 212 | 70: u'vpixsys(2)', 213 | 71: u'pathconf(2)', 214 | 72: u'open(2) - read', 215 | 73: u'open(2) - read,creat', 216 | 74: u'open(2) - read,trunc', 217 | 75: u'open(2) - read,creat,trunc', 218 | 76: u'open(2) - write', 219 | 77: u'open(2) - write,creat', 220 | 78: u'open(2) - write,trunc', 221 | 79: u'open(2) - write,creat,trunc', 222 | 80: u'open(2) - read,write', 223 | 81: u'open(2) - read,write,creat', 224 | 82: u'open(2) - read,write,trunc', 225 | 83: u'open(2) - read,write,creat,trunc', 226 | 84: u'msgctl(2) - illegal command', 227 | 85: u'msgctl(2) - IPC_RMID command', 228 | 86: u'msgctl(2) - IPC_SET command', 229 | 87: u'msgctl(2) - IPC_STAT command', 230 | 88: u'msgget(2)', 231 | 89: u'msgrcv(2)', 232 | 90: u'msgsnd(2)', 233 | 91: u'shmctl(2) - illegal command', 234 | 92: u'shmctl(2) - IPC_RMID command', 235 | 93: u'shmctl(2) - IPC_SET command', 236 | 94: u'shmctl(2) - IPC_STAT command', 237 | 95: u'shmget(2)', 238 | 96: u'shmat(2)', 239 | 97: u'shmdt(2)', 240 | 98: u'semctl(2) - illegal command', 241 | 99: u'semctl(2) - IPC_RMID command', 242 | 100: u'semctl(2) - IPC_SET command', 243 | 101: u'semctl(2) - IPC_STAT command', 244 | 102: u'semctl(2) - GETNCNT command', 245 | 103: u'semctl(2) - GETPID command', 246 | 104: u'semctl(2) - GETVAL command', 247 | 105: u'semctl(2) - GETALL command', 248 | 106: u'semctl(2) - GETZCNT command', 249 | 107: u'semctl(2) - SETVAL command', 250 | 108: u'semctl(2) - SETALL command', 251 | 109: u'semget(2)', 252 | 110: u'semop(2)', 253 | 111: u'process dumped core', 254 | 112: u'close(2)', 255 | 113: u'system booted', 256 | 114: u'async_daemon(2) exited', 257 | 115: u'nfssvc(2) exited', 258 | 128: u'writel(2)', 259 | 129: u'writevl(2)', 260 | 130: u'getauid(2)', 261 | 131: u'setauid(2)', 262 | 132: u'getaudit(2)', 263 | 133: u'setaudit(2)', 264 | 134: u'getuseraudit(2)', 265 | 135: u'setuseraudit(2)', 266 | 136: u'auditsvc(2)', 267 | 137: u'audituser(2)', 268 | 138: u'auditon(2)', 269 | 139: u'auditon(2) - GETTERMID command', 270 | 140: u'auditon(2) - SETTERMID command', 271 | 141: u'auditon(2) - GPOLICY command', 272 | 142: u'auditon(2) - SPOLICY command', 273 | 143: u'auditon(2) - GESTATE command', 274 | 144: u'auditon(2) - SESTATE command', 275 | 145: u'auditon(2) - GQCTRL command', 276 | 146: u'auditon(2) - SQCTRL command', 277 | 147: u'getkernstate(2)', 278 | 148: u'setkernstate(2)', 279 | 149: u'getportaudit(2)', 280 | 150: u'auditstat(2)', 281 | 151: u'revoke(2)', 282 | 152: u'Solaris AUE_MAC', 283 | 153: u'enter prom', 284 | 154: u'exit prom', 285 | 155: u'Solaris AUE_IFLOAT', 286 | 156: u'Solaris AUE_PFLOAT', 287 | 157: u'Solaris AUE_UPRIV', 288 | 158: u'ioctl(2)', 289 | 173: u'one-sided session record', 290 | 174: u'msggetl(2)', 291 | 175: u'msgrcvl(2)', 292 | 176: u'msgsndl(2)', 293 | 177: u'semgetl(2)', 294 | 178: u'shmgetl(2)', 295 | 183: u'socket(2)', 296 | 184: u'sendto(2)', 297 | 185: u'pipe(2)', 298 | 186: u'socketpair(2)', 299 | 187: u'send(2)', 300 | 188: u'sendmsg(2)', 301 | 189: u'recv(2)', 302 | 190: u'recvmsg(2)', 303 | 191: u'recvfrom(2)', 304 | 192: u'read(2)', 305 | 193: u'getdents(2)', 306 | 194: u'lseek(2)', 307 | 195: u'write(2)', 308 | 196: u'writev(2)', 309 | 197: u'nfs server', 310 | 198: u'readv(2)', 311 | 199: u'Solaris old stat(2)', 312 | 200: u'setuid(2)', 313 | 201: u'old stime(2)', 314 | 202: u'old utime(2)', 315 | 203: u'old nice(2)', 316 | 204: u'Solaris old setpgrp(2)', 317 | 205: u'setgid(2)', 318 | 206: u'readl(2)', 319 | 207: u'readvl(2)', 320 | 208: u'fstat(2)', 321 | 209: u'dup2(2)', 322 | 210: u'mmap(2)', 323 | 211: u'audit(2)', 324 | 212: u'Solaris priocntlsys(2)', 325 | 213: u'munmap(2)', 326 | 214: u'setegid(2)', 327 | 215: u'seteuid(2)', 328 | 216: u'putmsg(2)', 329 | 217: u'getmsg(2)', 330 | 218: u'putpmsg(2)', 331 | 219: u'getpmsg(2)', 332 | 220: u'audit system calls place holder', 333 | 221: u'auditon(2) - get kernel mask', 334 | 222: u'auditon(2) - set kernel mask', 335 | 223: u'auditon(2) - get cwd', 336 | 224: u'auditon(2) - get car', 337 | 225: u'auditon(2) - get audit statistics', 338 | 226: u'auditon(2) - reset audit statistics', 339 | 227: u'auditon(2) - set mask per uid', 340 | 228: u'auditon(2) - set mask per session ID', 341 | 229: u'auditon(2) - get audit state', 342 | 230: u'auditon(2) - set audit state', 343 | 231: u'auditon(2) - get event class', 344 | 232: u'auditon(2) - set event class', 345 | 233: u'utssys(2) - fusers', 346 | 234: u'statvfs(2)', 347 | 235: u'xstat(2)', 348 | 236: u'lxstat(2)', 349 | 237: u'lchown(2)', 350 | 238: u'memcntl(2)', 351 | 239: u'sysinfo(2)', 352 | 240: u'xmknod(2)', 353 | 241: u'fork1(2)', 354 | 242: u'modctl(2) system call place holder', 355 | 243: u'modctl(2) - load module', 356 | 244: u'modctl(2) - unload module', 357 | 245: u'modctl(2) - configure module', 358 | 246: u'modctl(2) - bind module', 359 | 247: u'getmsg-accept', 360 | 248: u'putmsg-connect', 361 | 249: u'putmsg-send', 362 | 250: u'getmsg-receive', 363 | 251: u'acl(2) - SETACL comand', 364 | 252: u'facl(2) - SETACL command', 365 | 253: u'doorfs(2) - system call place holder', 366 | 254: u'doorfs(2) - DOOR_CALL', 367 | 255: u'doorfs(2) - DOOR_RETURN', 368 | 256: u'doorfs(2) - DOOR_CREATE', 369 | 257: u'doorfs(2) - DOOR_REVOKE', 370 | 258: u'doorfs(2) - DOOR_INFO', 371 | 259: u'doorfs(2) - DOOR_CRED', 372 | 260: u'doorfs(2) - DOOR_BIND', 373 | 261: u'doorfs(2) - DOOR_UNBIND', 374 | 262: u'p_online(2)', 375 | 263: u'processor_bind(2)', 376 | 264: u'inst_sync(2)', 377 | 265: u'configure socket', 378 | 266: u'setaudit_addr(2)', 379 | 267: u'getaudit_addr(2)', 380 | 268: u'Solaris umount(2)', 381 | 269: u'fsat(2) - place holder', 382 | 270: u'openat(2) - read', 383 | 271: u'openat(2) - read,creat', 384 | 272: u'openat(2) - read,trunc', 385 | 273: u'openat(2) - read,creat,trunc', 386 | 274: u'openat(2) - write', 387 | 275: u'openat(2) - write,creat', 388 | 276: u'openat(2) - write,trunc', 389 | 277: u'openat(2) - write,creat,trunc', 390 | 278: u'openat(2) - read,write', 391 | 279: u'openat(2) - read,write,create', 392 | 280: u'openat(2) - read,write,trunc', 393 | 281: u'openat(2) - read,write,creat,trunc', 394 | 282: u'renameat(2)', 395 | 283: u'fstatat(2)', 396 | 284: u'fchownat(2)', 397 | 285: u'futimesat(2)', 398 | 286: u'unlinkat(2)', 399 | 287: u'clock_settime(2)', 400 | 288: u'ntp_adjtime(2)', 401 | 289: u'setppriv(2)', 402 | 290: u'modctl(2) - configure device policy', 403 | 291: u'modctl(2) - configure additional privilege', 404 | 292: u'kernel cryptographic framework', 405 | 293: u'configure kernel SSL', 406 | 294: u'brandsys(2)', 407 | 295: u'Add IPsec policy rule', 408 | 296: u'Delete IPsec policy rule', 409 | 297: u'Clone IPsec policy', 410 | 298: u'Flip IPsec policy', 411 | 299: u'Flush IPsec policy rules', 412 | 300: u'Update IPsec algorithms', 413 | 301: u'portfs', 414 | 302: u'ptrace(2)', 415 | 303: u'chflags(2)', 416 | 304: u'fchflags(2)', 417 | 305: u'profil(2)', 418 | 306: u'ktrace(2)', 419 | 307: u'setlogin(2)', 420 | 308: u'reboot(2)', 421 | 309: u'revoke(2)', 422 | 310: u'umask(2)', 423 | 311: u'mprotect(2)', 424 | 312: u'setpriority(2)', 425 | 313: u'settimeofday(2)', 426 | 314: u'flock(2)', 427 | 315: u'mkfifo(2)', 428 | 316: u'poll(2)', 429 | 317: u'socketpair(2)', 430 | 318: u'futimes(2)', 431 | 319: u'setsid(2)', 432 | 320: u'setprivexec(2)', 433 | 321: u'nfssvc(2)', 434 | 322: u'getfh(2)', 435 | 323: u'quotactl(2)', 436 | 324: u'add_profil()', 437 | 325: u'kdebug_trace()', 438 | 326: u'fstat(2)', 439 | 327: u'fpathconf(2)', 440 | 328: u'getdirentries(2)', 441 | 329: u'truncate(2)', 442 | 330: u'ftruncate(2)', 443 | 331: u'sysctl(3)', 444 | 332: u'mlock(2)', 445 | 333: u'munlock(2)', 446 | 334: u'undelete(2)', 447 | 335: u'getattrlist()', 448 | 336: u'setattrlist()', 449 | 337: u'getdirentriesattr()', 450 | 338: u'exchangedata()', 451 | 339: u'searchfs()', 452 | 340: u'minherit(2)', 453 | 341: u'semconfig()', 454 | 342: u'sem_open(2)', 455 | 343: u'sem_close(2)', 456 | 344: u'sem_unlink(2)', 457 | 345: u'shm_open(2)', 458 | 346: u'shm_unlink(2)', 459 | 347: u'load_shared_file()', 460 | 348: u'reset_shared_file()', 461 | 349: u'new_system_share_regions()', 462 | 350: u'pthread_kill(2)', 463 | 351: u'pthread_sigmask(2)', 464 | 352: u'auditctl(2)', 465 | 353: u'rfork(2)', 466 | 354: u'lchmod(2)', 467 | 355: u'swapoff(2)', 468 | 356: u'init_process()', 469 | 357: u'map_fd()', 470 | 358: u'task_for_pid()', 471 | 359: u'pid_for_task()', 472 | 360: u'sysctl() - non-admin', 473 | 361: u'copyfile()', 474 | 43001: u'getfsstat(2)', 475 | 43002: u'ptrace(2)', 476 | 43003: u'chflags(2)', 477 | 43004: u'fchflags(2)', 478 | 43005: u'profil(2)', 479 | 43006: u'ktrace(2)', 480 | 43007: u'setlogin(2)', 481 | 43008: u'revoke(2)', 482 | 43009: u'umask(2)', 483 | 43010: u'mprotect(2)', 484 | 43011: u'mkfifo(2)', 485 | 43012: u'poll(2)', 486 | 43013: u'futimes(2)', 487 | 43014: u'setsid(2)', 488 | 43015: u'setprivexec(2)', 489 | 43016: u'add_profil()', 490 | 43017: u'kdebug_trace()', 491 | 43018: u'fstat(2)', 492 | 43019: u'fpathconf(2)', 493 | 43020: u'getdirentries(2)', 494 | 43021: u'sysctl(3)', 495 | 43022: u'mlock(2)', 496 | 43023: u'munlock(2)', 497 | 43024: u'undelete(2)', 498 | 43025: u'getattrlist()', 499 | 43026: u'setattrlist()', 500 | 43027: u'getdirentriesattr()', 501 | 43028: u'exchangedata()', 502 | 43029: u'searchfs()', 503 | 43030: u'minherit(2)', 504 | 43031: u'semconfig()', 505 | 43032: u'sem_open(2)', 506 | 43033: u'sem_close(2)', 507 | 43034: u'sem_unlink(2)', 508 | 43035: u'shm_open(2)', 509 | 43036: u'shm_unlink(2)', 510 | 43037: u'load_shared_file()', 511 | 43038: u'reset_shared_file()', 512 | 43039: u'new_system_share_regions()', 513 | 43040: u'pthread_kill(2)', 514 | 43041: u'pthread_sigmask(2)', 515 | 43042: u'auditctl(2)', 516 | 43043: u'rfork(2)', 517 | 43044: u'lchmod(2)', 518 | 43045: u'swapoff(2)', 519 | 43046: u'init_process()', 520 | 43047: u'map_fd()', 521 | 43048: u'task_for_pid()', 522 | 43049: u'pid_for_task()', 523 | 43050: u'sysctl() - non-admin', 524 | 43051: u'copyfile(2)', 525 | 43052: u'lutimes(2)', 526 | 43053: u'lchflags(2)', 527 | 43054: u'sendfile(2)', 528 | 43055: u'uselib(2)', 529 | 43056: u'getresuid(2)', 530 | 43057: u'setresuid(2)', 531 | 43058: u'getresgid(2)', 532 | 43059: u'setresgid(2)', 533 | 43060: u'wait4(2)', 534 | 43061: u'lgetfh(2)', 535 | 43062: u'fhstatfs(2)', 536 | 43063: u'fhopen(2)', 537 | 43064: u'fhstat(2)', 538 | 43065: u'jail(2)', 539 | 43066: u'eaccess(2)', 540 | 43067: u'kqueue(2)', 541 | 43068: u'kevent(2)', 542 | 43069: u'fsync(2)', 543 | 43070: u'nmount(2)', 544 | 43071: u'bdflush(2)', 545 | 43072: u'setfsuid(2)', 546 | 43073: u'setfsgid(2)', 547 | 43074: u'personality(2)', 548 | 43075: u'getscheduler(2)', 549 | 43076: u'setscheduler(2)', 550 | 43077: u'prctl(2)', 551 | 43078: u'getcwd(2)', 552 | 43079: u'capget(2)', 553 | 43080: u'capset(2)', 554 | 43081: u'pivot_root(2)', 555 | 43082: u'rtprio(2)', 556 | 43083: u'sched_getparam(2)', 557 | 43084: u'sched_setparam(2)', 558 | 43085: u'sched_get_priority_max(2)', 559 | 43086: u'sched_get_priority_min(2)', 560 | 43087: u'sched_rr_get_interval(2)', 561 | 43088: u'acl_get_file(2)', 562 | 43089: u'acl_set_file(2)', 563 | 43090: u'acl_get_fd(2)', 564 | 43091: u'acl_set_fd(2)', 565 | 43092: u'acl_delete_file(2)', 566 | 43093: u'acl_delete_fd(2)', 567 | 43094: u'acl_aclcheck_file(2)', 568 | 43095: u'acl_aclcheck_fd(2)', 569 | 43096: u'acl_get_link(2)', 570 | 43097: u'acl_set_link(2)', 571 | 43098: u'acl_delete_link(2)', 572 | 43099: u'acl_aclcheck_link(2)', 573 | 43100: u'sysarch(2)', 574 | 43101: u'extattrctl(2)', 575 | 43102: u'extattr_get_file(2)', 576 | 43103: u'extattr_set_file(2)', 577 | 43104: u'extattr_list_file(2)', 578 | 43105: u'extattr_delete_file(2)', 579 | 43106: u'extattr_get_fd(2)', 580 | 43107: u'extattr_set_fd(2)', 581 | 43108: u'extattr_list_fd(2)', 582 | 43109: u'extattr_delete_fd(2)', 583 | 43110: u'extattr_get_link(2)', 584 | 43111: u'extattr_set_link(2)', 585 | 43112: u'extattr_list_link(2)', 586 | 43113: u'extattr_delete_link(2)', 587 | 43114: u'kenv(8)', 588 | 43115: u'jail_attach(2)', 589 | 43116: u'sysctl(3)', 590 | 43117: u'linux ioperm', 591 | 43118: u'readdir(3)', 592 | 43119: u'linux iopl', 593 | 43120: u'linux vm86', 594 | 43121: u'mac_get_proc(2)', 595 | 43122: u'mac_set_proc(2)', 596 | 43123: u'mac_get_fd(2)', 597 | 43124: u'mac_get_file(2)', 598 | 43125: u'mac_set_fd(2)', 599 | 43126: u'mac_set_file(2)', 600 | 43127: u'mac_syscall(2)', 601 | 43128: u'mac_get_pid(2)', 602 | 43129: u'mac_get_link(2)', 603 | 43130: u'mac_set_link(2)', 604 | 43131: u'mac_execve(2)', 605 | 43132: u'getpath_fromfd(2)', 606 | 43133: u'getpath_fromaddr(2)', 607 | 43134: u'mq_open(2)', 608 | 43135: u'mq_setattr(2)', 609 | 43136: u'mq_timedreceive(2)', 610 | 43137: u'mq_timedsend(2)', 611 | 43138: u'mq_notify(2)', 612 | 43139: u'mq_unlink(2)', 613 | 43140: u'listen(2)', 614 | 43141: u'mlockall(2)', 615 | 43142: u'munlockall(2)', 616 | 43143: u'closefrom(2)', 617 | 43144: u'fexecve(2)', 618 | 43145: u'faccessat(2)', 619 | 43146: u'fchmodat(2)', 620 | 43147: u'linkat(2)', 621 | 43148: u'mkdirat(2)', 622 | 43149: u'mkfifoat(2)', 623 | 43150: u'mknodat(2)', 624 | 43151: u'readlinkat(2)', 625 | 43152: u'symlinkat(2)', 626 | 43153: u'mac_getfsstat(2)', 627 | 43154: u'mac_get_mount(2)', 628 | 43155: u'mac_get_lcid(2)', 629 | 43156: u'mac_get_lctx(2)', 630 | 43157: u'mac_set_lctx(2)', 631 | 43158: u'mac_mount(2)', 632 | 43159: u'getlcid(2)', 633 | 43160: u'setlcid(2)', 634 | 43161: u'taskname_for_pid()', 635 | 43162: u'access_extended(2)', 636 | 43163: u'chmod_extended(2)', 637 | 43164: u'fchmod_extended(2)', 638 | 43165: u'fstat_extended(2)', 639 | 43166: u'lstat_extended(2)', 640 | 43167: u'mkdir_extended(2)', 641 | 43168: u'mkfifo_extended(2)', 642 | 43169: u'open_extended(2) - attr only', 643 | 43170: u'open_extended(2) - read', 644 | 43171: u'open_extended(2) - read,creat', 645 | 43172: u'open_extended(2) - read,trunc', 646 | 43173: u'open_extended(2) - read,creat,trunc', 647 | 43174: u'open_extended(2) - write', 648 | 43175: u'open_extended(2) - write,creat', 649 | 43176: u'open_extended(2) - write,trunc', 650 | 43177: u'open_extended(2) - write,creat,trunc', 651 | 43178: u'open_extended(2) - read,write', 652 | 43179: u'open_extended(2) - read,write,creat', 653 | 43180: u'open_extended(2) - read,write,trunc', 654 | 43181: u'open_extended(2) - read,write,creat,trunc', 655 | 43182: u'stat_extended(2)', 656 | 43183: u'umask_extended(2)', 657 | 43184: u'openat(2) - attr only', 658 | 43185: u'posix_openpt(2)', 659 | 43186: u'cap_new(2)', 660 | 43187: u'cap_getrights(2)', 661 | 43188: u'cap_enter(2)', 662 | 43189: u'cap_getmode(2)', 663 | 43190: u'posix_spawn(2)', 664 | 43191: u'fsgetpath(2)', 665 | 43192: u'pread(2)', 666 | 43193: u'pwrite(2)', 667 | 43194: u'fsctl()', 668 | 43195: u'ffsctl()', 669 | 43196: u'lpathconf(2)', 670 | 43197: u'pdfork(2)', 671 | 43198: u'pdkill(2)', 672 | 43199: u'pdgetpid(2)', 673 | 43200: u'pdwait(2)', 674 | 44901: u'session start', 675 | 44902: u'session update', 676 | 44903: u'session end', 677 | 44904: u'session close', 678 | 6144: u'at-create atjob', 679 | 6145: u'at-delete atjob (at or atrm)', 680 | 6146: u'at-permission', 681 | 6147: u'cron-invoke', 682 | 6148: u'crontab-crontab created', 683 | 6149: u'crontab-crontab deleted', 684 | 6150: u'crontab-permission', 685 | 6151: u'inetd connection', 686 | 6152: u'login - local', 687 | 6153: u'logout - local', 688 | 6154: u'login - telnet', 689 | 6155: u'login - rlogin', 690 | 6156: u'mount', 691 | 6157: u'unmount', 692 | 6158: u'rsh access', 693 | 6159: u'su(1)', 694 | 6160: u'system halt', 695 | 6161: u'system reboot', 696 | 6162: u'rexecd', 697 | 6163: u'passwd', 698 | 6164: u'rexd', 699 | 6165: u'ftp access', 700 | 6166: u'init', 701 | 6167: u'uadmin', 702 | 6168: u'system shutdown', 703 | 6170: u'crontab-modify', 704 | 6171: u'ftp logout', 705 | 6172: u'login - ssh', 706 | 6173: u'role login', 707 | 6180: u' profile command', 708 | 6181: u'add filesystem', 709 | 6182: u'delete filesystem', 710 | 6183: u'modify filesystem', 711 | 6200: u'allocate-device success', 712 | 6201: u'allocate-device failure', 713 | 6202: u'deallocate-device success', 714 | 6203: u'deallocate-device failure', 715 | 6204: u'allocate-list devices success', 716 | 6205: u'allocate-list devices failure', 717 | 6207: u'create user', 718 | 6208: u'modify user', 719 | 6209: u'delete user', 720 | 6210: u'disable user', 721 | 6211: u'enable user', 722 | 6212: u'newgrp login', 723 | 6213: u'admin login', 724 | 6214: u'authenticated kadmind request', 725 | 6215: u'unauthenticated kadmind req', 726 | 6216: u'kdc authentication svc request', 727 | 6217: u'kdc tkt-grant svc request', 728 | 6218: u'kdc tgs 2ndtkt mismtch', 729 | 6219: u'kdc tgs issue alt tgt', 730 | 6300: u'sudo(1)', 731 | 6501: u'modify password', 732 | 6511: u'create group', 733 | 6512: u'delete group', 734 | 6513: u'modify group', 735 | 6514: u'add to group', 736 | 6515: u'remove from group', 737 | 6521: u'revoke object priv', 738 | 6600: u'loginwindow login', 739 | 6601: u'loginwindow logout', 740 | 7000: u'user authentication', 741 | 7001: u'SecSrvr connection setup', 742 | 7002: u'SecSrvr AuthEngine', 743 | 7003: u'SecSrvr authinternal mech', 744 | 32800: u'OpenSSH login', 745 | 45000: u'audit startup', 746 | 45001: u'audit shutdown', 747 | 45014: u'modify password', 748 | 45015: u'create group', 749 | 45016: u'delete group', 750 | 45017: u'modify group', 751 | 45018: u'add to group', 752 | 45019: u'remove from group', 753 | 45020: u'revoke object priv', 754 | 45021: u'loginwindow login', 755 | 45022: u'loginwindow logout', 756 | 45023: u'user authentication', 757 | 45024: u'SecSrvr connection setup', 758 | 45025: u'SecSrvr AuthEngine', 759 | 45026: u'SecSrvr authinternal mech', 760 | 45027: u'Calife', 761 | 45028: u'sudo(1)', 762 | 45029: u'audit crash recovery', 763 | 45030: u'SecSrvr AuthMechanism', 764 | 45031: u'Security Assessment' 765 | } 766 | 767 | 768 | ##### STRUCTURES ##### 769 | 770 | 771 | IPV4_STRUCT = construct.UBInt32('ipv4') 772 | 773 | IPV6_STRUCT = construct.Struct( 774 | 'ipv6', construct.UBInt64('high'), construct.UBInt64('low')) 775 | 776 | # TODO: Implement the following tokens: 777 | # au_to_ipc_perm, au_to_sock_unix 778 | 779 | # Tested structures. 780 | # INFO: I have ommited the ID in the structures declaration. 781 | # I used the BSM_TYPE first to read the ID, and then, the structure. 782 | # Tokens always start with an ID value that identifies their token 783 | # type and subsequent structure. 784 | # token type that we are going to read, the token structure. 785 | BSM_TYPE = construct.UBInt8('token_id') 786 | 787 | # Data type structures. 788 | BSM_TOKEN_DATA_CHAR = construct.String('value', 1) 789 | BSM_TOKEN_DATA_SHORT = construct.UBInt16('value') 790 | BSM_TOKEN_DATA_INTEGER = construct.UBInt32('value') 791 | 792 | # Common structure used by other structures. 793 | # audit_uid: integer, uid that generates the entry. 794 | # effective_uid: integer, the permission user used. 795 | # effective_gid: integer, the permission group used. 796 | # real_uid: integer, user id of the user that execute the process. 797 | # real_gid: integer, group id of the group that execute the process. 798 | # pid: integer, identification number of the process. 799 | # session_id: unknown, need research. 800 | BSM_TOKEN_SUBJECT_SHORT = construct.Struct( 801 | 'subject_data', 802 | construct.UBInt32('audit_uid'), 803 | construct.UBInt32('effective_uid'), 804 | construct.UBInt32('effective_gid'), 805 | construct.UBInt32('real_uid'), 806 | construct.UBInt32('real_gid'), 807 | construct.UBInt32('pid'), 808 | construct.UBInt32('session_id')) 809 | 810 | # Common structure used by other structures. 811 | # Identify the kind of inet (IPv4 or IPv6) 812 | # TODO: instead of 16, AU_IPv6 must be used. 813 | BSM_IP_TYPE_SHORT = construct.Struct( 814 | 'bsm_ip_type_short', 815 | construct.UBInt32('net_type'), 816 | construct.IfThenElse( 817 | 'ip_addr', lambda ctx: ctx['net_type'] == 16, 818 | IPV6_STRUCT, 819 | IPV4_STRUCT)) 820 | # Initial fields structure used by header structures. 821 | # length: integer, the lenght of the entry, equal to trailer (doc: length). 822 | # version: integer, version of BSM (AUDIT_HEADER_VERSION). 823 | # event_type: integer, the type of event (/etc/security/audit_event). 824 | # modifier: integer, unknown, need research (It is always 0). 825 | BSM_HEADER = construct.Struct( 826 | 'bsm_header', 827 | construct.UBInt32('length'), 828 | construct.UBInt8('version'), 829 | construct.UBInt16('event_type'), 830 | construct.UBInt16('modifier')) 831 | 832 | # First token of one entry. 833 | # timestamp: integer, Epoch timestamp of the entry. 834 | # microsecond: integer, the microsecond of the entry. 835 | BSM_HEADER32 = construct.Struct( 836 | 'bsm_header32', 837 | BSM_HEADER, 838 | construct.UBInt32('timestamp'), 839 | construct.UBInt32('microsecond')) 840 | 841 | BSM_HEADER64 = construct.Struct( 842 | 'bsm_header32', 843 | BSM_HEADER, 844 | construct.UBInt64('timestamp'), 845 | construct.UBInt64('microsecond')) 846 | 847 | BSM_HEADER32_EX = construct.Struct( 848 | 'header', 849 | BSM_HEADER, 850 | BSM_IP_TYPE_SHORT, 851 | construct.UBInt32('timestamp'), 852 | construct.UBInt32('microsecond')) 853 | 854 | # Token TEXT, provides extra information. 855 | BSM_TOKEN_TEXT = construct.PascalString( 856 | 'value', length_field = construct.UBInt16('length')) 857 | 858 | # Path of the executable. 859 | BSM_TOKEN_PATH = BSM_TOKEN_TEXT 860 | 861 | # Identified the end of the record (follow by TRAILER). 862 | # status: integer that identifies the status of the exit (BSM_ERRORS). 863 | # return: returned value from the operation. 864 | BSM_TOKEN_RETURN32 = construct.Struct( 865 | 'bsm_token_return32', 866 | construct.UBInt8('status'), 867 | construct.UBInt32('return_value')) 868 | 869 | BSM_TOKEN_RETURN64 = construct.Struct( 870 | 'bsm_token_return64', 871 | construct.UBInt8('status'), 872 | construct.UBInt64('return_value')) 873 | 874 | # Identified the number of bytes that was written. 875 | # magic: 2 bytes that identifes the TRAILER (BSM_TOKEN_TRAILER_MAGIC). 876 | # length: integer that has the number of bytes from the entry size. 877 | BSM_TOKEN_TRAILER = construct.Struct( 878 | 'bsm_token_trailer', 879 | construct.UBInt16('magic'), 880 | construct.UBInt32('record_length')) 881 | 882 | # A 32-bits argument. 883 | # num_arg: the number of the argument. 884 | # name_arg: the argument's name. 885 | # text: the string value of the argument. 886 | BSM_TOKEN_ARGUMENT32 = construct.Struct( 887 | 'bsm_token_argument32', 888 | construct.UBInt8('num_arg'), 889 | construct.UBInt32('name_arg'), 890 | BSM_TOKEN_TEXT) 891 | 892 | # A 64-bits argument. 893 | # num_arg: integer, the number of the argument. 894 | # name_arg: text, the argument's name. 895 | # text: the string value of the argument. 896 | BSM_TOKEN_ARGUMENT64 = construct.Struct( 897 | 'bsm_token_argument64', 898 | construct.UBInt8('num_arg'), 899 | construct.UBInt64('name_arg'), 900 | BSM_TOKEN_TEXT) 901 | 902 | # Identify an user. 903 | # terminal_id: unknown, research needed. 904 | # terminal_addr: unknown, research needed. 905 | BSM_TOKEN_SUBJECT32 = construct.Struct( 906 | 'bsm_token_subject32', 907 | BSM_TOKEN_SUBJECT_SHORT, 908 | construct.UBInt32('terminal_port'), 909 | IPV4_STRUCT) 910 | 911 | # Identify an user using a extended Token. 912 | # terminal_port: unknown, need research. 913 | # net_type: unknown, need research. 914 | BSM_TOKEN_SUBJECT32_EX = construct.Struct( 915 | 'bsm_token_subject32_ex', 916 | BSM_TOKEN_SUBJECT_SHORT, 917 | construct.UBInt32('terminal_port'), 918 | BSM_IP_TYPE_SHORT) 919 | 920 | # au_to_opaque // AUT_OPAQUE 921 | BSM_TOKEN_OPAQUE = BSM_TOKEN_TEXT 922 | 923 | # au_to_seq // AUT_SEQ 924 | BSM_TOKEN_SEQUENCE = BSM_TOKEN_DATA_INTEGER 925 | 926 | # Program execution with options. 927 | # For each argument we are going to have a string+ "\x00". 928 | # Example: [00 00 00 02][41 42 43 00 42 42 00] 929 | # 2 Arguments, Arg1: [414243] Arg2: [4242]. 930 | BSM_TOKEN_EXEC_ARGUMENTS = construct.UBInt32('number_arguments') 931 | BSM_TOKEN_EXEC_ARGUMENT = construct.macros.CString('text') 932 | 933 | # au_to_in_addr // AUT_IN_ADDR: 934 | BSM_TOKEN_ADDR = IPV4_STRUCT 935 | 936 | # au_to_in_addr_ext // AUT_IN_ADDR_EX: 937 | BSM_TOKEN_ADDR_EXT = construct.Struct( 938 | 'bsm_token_addr_ext', 939 | construct.UBInt32('net_type'), 940 | IPV6_STRUCT) 941 | 942 | # au_to_ip // AUT_IP: 943 | # TODO: parse this header in the correct way. 944 | BSM_TOKEN_IP = construct.String('binary_ipv4_add', 20) 945 | 946 | # au_to_ipc // AUT_IPC: 947 | BSM_TOKEN_IPC = construct.Struct( 948 | 'bsm_token_ipc', 949 | construct.UBInt8('object_type'), 950 | construct.UBInt32('object_id')) 951 | 952 | # au_to_iport // AUT_IPORT: 953 | BSM_TOKEN_PORT = construct.UBInt16('port_number') 954 | 955 | # au_to_file // AUT_OTHER_FILE32: 956 | BSM_TOKEN_FILE = construct.Struct( 957 | 'bsm_token_file', 958 | construct.UBInt32('timestamp'), 959 | construct.UBInt32('microsecond'), 960 | construct.PascalString( 961 | 'file_name', length_field = construct.UBInt16('length'))) 962 | 963 | # au_to_subject64 // AUT_SUBJECT64: 964 | BSM_TOKEN_SUBJECT64 = construct.Struct( 965 | 'bsm_token_subject64', 966 | BSM_TOKEN_SUBJECT_SHORT, 967 | construct.UBInt64('terminal_port'), 968 | IPV4_STRUCT) 969 | 970 | # au_to_subject64_ex // AU_IPv4: 971 | BSM_TOKEN_SUBJECT64_EX = construct.Struct( 972 | 'bsm_token_subject64_ex', 973 | BSM_TOKEN_SUBJECT_SHORT, 974 | construct.UBInt32('terminal_port'), 975 | construct.UBInt32('terminal_type'), 976 | BSM_IP_TYPE_SHORT) 977 | 978 | # au_to_process32 // AUT_PROCESS32: 979 | BSM_TOKEN_PROCESS32 = construct.Struct( 980 | 'bsm_token_process32', 981 | BSM_TOKEN_SUBJECT_SHORT, 982 | construct.UBInt32('terminal_port'), 983 | IPV4_STRUCT) 984 | 985 | # au_to_process64 // AUT_PROCESS32: 986 | BSM_TOKEN_PROCESS64 = construct.Struct( 987 | 'bsm_token_process64', 988 | BSM_TOKEN_SUBJECT_SHORT, 989 | construct.UBInt64('terminal_port'), 990 | IPV4_STRUCT) 991 | 992 | # au_to_process32_ex // AUT_PROCESS32_EX: 993 | BSM_TOKEN_PROCESS32_EX = construct.Struct( 994 | 'bsm_token_process32_ex', 995 | BSM_TOKEN_SUBJECT_SHORT, 996 | construct.UBInt32('terminal_port'), 997 | BSM_IP_TYPE_SHORT) 998 | 999 | # au_to_process64_ex // AUT_PROCESS64_EX: 1000 | BSM_TOKEN_PROCESS64_EX = construct.Struct( 1001 | 'bsm_token_process32_ex', 1002 | BSM_TOKEN_SUBJECT_SHORT, 1003 | construct.UBInt64('terminal_port'), 1004 | BSM_IP_TYPE_SHORT) 1005 | 1006 | # au_to_sock_inet32 // AUT_SOCKINET32: 1007 | BSM_TOKEN_AUT_SOCKINET32 = construct.Struct( 1008 | 'bsm_token_aut_sockinet32', 1009 | construct.UBInt16('net_type'), 1010 | construct.UBInt16('port_number'), 1011 | IPV4_STRUCT) 1012 | 1013 | # Info: checked against the source code of XNU, but not against 1014 | # real BSM file. 1015 | BSM_TOKEN_AUT_SOCKINET128 = construct.Struct( 1016 | 'bsm_token_aut_sockinet128', 1017 | construct.UBInt16('net_type'), 1018 | construct.UBInt16('port_number'), 1019 | IPV6_STRUCT) 1020 | 1021 | # au_to_socket_ex // AUT_SOCKET_EX 1022 | # TODO: Change the 26 for unixbsm.BSM_PROTOCOLS.INET6. 1023 | BSM_TOKEN_AUT_SOCKINET32_EX = construct.Struct( 1024 | 'bsm_token_aut_sockinet32_ex', 1025 | construct.UBInt16('socket_domain'), 1026 | construct.UBInt16('socket_type'), 1027 | construct.IfThenElse( 1028 | 'structure_addr_port', 1029 | lambda ctx: ctx['socket_domain'] == 26, 1030 | construct.Struct('addr_type', 1031 | construct.UBInt16('ip_type'), 1032 | construct.UBInt16('source_port'), 1033 | construct.UBInt64('saddr_high'), 1034 | construct.UBInt64('saddr_low'), 1035 | construct.UBInt16('destination_port'), 1036 | construct.UBInt64('daddr_high'), 1037 | construct.UBInt64('daddr_low')), 1038 | construct.Struct('addr_type', 1039 | construct.UBInt16('ip_type'), 1040 | construct.UBInt16('source_port'), 1041 | construct.UBInt32('source_address'), 1042 | construct.UBInt16('destination_port'), 1043 | construct.UBInt32('destination_address')))) 1044 | 1045 | # au_to_data // au_to_data 1046 | # how to print: BSM_TOKEN_DATA_PRINT. 1047 | # type: BSM_TOKEN_DATA_TYPE. 1048 | # unit_count: number of type values. 1049 | # BSM_TOKEN_DATA has a end field = type * unit_count 1050 | BSM_TOKEN_DATA = construct.Struct( 1051 | 'bsm_token_data', 1052 | construct.UBInt8('how_to_print'), 1053 | construct.UBInt8('data_type'), 1054 | construct.UBInt8('unit_count')) 1055 | 1056 | # au_to_attr32 // AUT_ATTR32 1057 | BSM_TOKEN_ATTR32 = construct.Struct( 1058 | 'bsm_token_attr32', 1059 | construct.UBInt32('file_mode'), 1060 | construct.UBInt32('uid'), 1061 | construct.UBInt32('gid'), 1062 | construct.UBInt32('file_system_id'), 1063 | construct.UBInt64('file_system_node_id'), 1064 | construct.UBInt32('device')) 1065 | 1066 | # au_to_attr64 // AUT_ATTR64 1067 | BSM_TOKEN_ATTR64 = construct.Struct( 1068 | 'bsm_token_attr32', 1069 | construct.UBInt32('file_mode'), 1070 | construct.UBInt32('uid'), 1071 | construct.UBInt32('gid'), 1072 | construct.UBInt32('file_system_id'), 1073 | construct.UBInt64('file_system_node_id'), 1074 | construct.UBInt64('device')) 1075 | 1076 | # au_to_exit // AUT_EXIT 1077 | BSM_TOKEN_EXIT = construct.Struct( 1078 | 'bsm_token_exit', 1079 | construct.UBInt32('status'), 1080 | construct.UBInt32('return_value')) 1081 | 1082 | # au_to_newgroups // AUT_NEWGROUPS 1083 | # INFO: we must read BSM_TOKEN_DATA_INTEGER for each group. 1084 | BSM_TOKEN_GROUPS = construct.UBInt16('group_number') 1085 | 1086 | # au_to_exec_env == au_to_exec_args 1087 | BSM_TOKEN_EXEC_ENV = BSM_TOKEN_EXEC_ARGUMENTS 1088 | 1089 | # au_to_zonename //AUT_ZONENAME 1090 | BSM_TOKEN_ZONENAME = BSM_TOKEN_TEXT 1091 | 1092 | #### TOKEN ID #### 1093 | # Only the checked structures are been added to the valid structures lists. 1094 | BSM_TYPE_LIST = { 1095 | 17: ['BSM_TOKEN_FILE', BSM_TOKEN_FILE], 1096 | 19: ['BSM_TOKEN_TRAILER', BSM_TOKEN_TRAILER], 1097 | 20: ['BSM_HEADER32', BSM_HEADER32], 1098 | 21: ['BSM_HEADER64', BSM_HEADER64], 1099 | 33: ['BSM_TOKEN_DATA', BSM_TOKEN_DATA], 1100 | 34: ['BSM_TOKEN_IPC', BSM_TOKEN_IPC], 1101 | 35: ['BSM_TOKEN_PATH', BSM_TOKEN_PATH], 1102 | 36: ['BSM_TOKEN_SUBJECT32', BSM_TOKEN_SUBJECT32], 1103 | 38: ['BSM_TOKEN_PROCESS32', BSM_TOKEN_PROCESS32], 1104 | 39: ['BSM_TOKEN_RETURN32', BSM_TOKEN_RETURN32], 1105 | 40: ['BSM_TOKEN_TEXT', BSM_TOKEN_TEXT], 1106 | 41: ['BSM_TOKEN_OPAQUE', BSM_TOKEN_OPAQUE], 1107 | 42: ['BSM_TOKEN_ADDR', BSM_TOKEN_ADDR], 1108 | 43: ['BSM_TOKEN_IP', BSM_TOKEN_IP], 1109 | 44: ['BSM_TOKEN_PORT', BSM_TOKEN_PORT], 1110 | 45: ['BSM_TOKEN_ARGUMENT32', BSM_TOKEN_ARGUMENT32], 1111 | 47: ['BSM_TOKEN_SEQUENCE', BSM_TOKEN_SEQUENCE], 1112 | 49: ['BSM_TOKEN_ATTR32', BSM_TOKEN_ATTR32], 1113 | 52: ['BSM_TOKEN_GROUPS', BSM_TOKEN_GROUPS], 1114 | 59: ['BSM_TOKEN_GROUPS', BSM_TOKEN_GROUPS], 1115 | 60: ['BSM_TOKEN_EXEC_ARGUMENTS', BSM_TOKEN_EXEC_ARGUMENTS], 1116 | 61: ['BSM_TOKEN_EXEC_ENV', BSM_TOKEN_EXEC_ENV], 1117 | 62: ['BSM_TOKEN_ATTR32', BSM_TOKEN_ATTR32], 1118 | 82: ['BSM_TOKEN_EXIT', BSM_TOKEN_EXIT], 1119 | 96: ['BSM_TOKEN_ZONENAME', BSM_TOKEN_ZONENAME], 1120 | 113: ['BSM_TOKEN_ARGUMENT64', BSM_TOKEN_ARGUMENT64], 1121 | 114: ['BSM_TOKEN_RETURN64', BSM_TOKEN_RETURN64], 1122 | 115: ['BSM_TOKEN_ATTR64', BSM_TOKEN_ATTR64], 1123 | 116: ['BSM_HEADER32_EX', BSM_HEADER32_EX], 1124 | 117: ['BSM_TOKEN_SUBJECT64', BSM_TOKEN_SUBJECT64], 1125 | 119: ['BSM_TOKEN_PROCESS64', BSM_TOKEN_PROCESS64], 1126 | 122: ['BSM_TOKEN_SUBJECT32_EX', BSM_TOKEN_SUBJECT32_EX], 1127 | 123: ['BSM_TOKEN_PROCESS32_EX', BSM_TOKEN_PROCESS32_EX], 1128 | 124: ['BSM_TOKEN_PROCESS64_EX', BSM_TOKEN_PROCESS64_EX], 1129 | 125: ['BSM_TOKEN_SUBJECT64_EX', BSM_TOKEN_SUBJECT64_EX], 1130 | 126: ['BSM_TOKEN_ADDR_EXT', BSM_TOKEN_ADDR_EXT], 1131 | 127: ['BSM_TOKEN_AUT_SOCKINET32_EX', BSM_TOKEN_AUT_SOCKINET32_EX], 1132 | 128: ['BSM_TOKEN_AUT_SOCKINET32', BSM_TOKEN_AUT_SOCKINET32], 1133 | 129: ['BSM_TOKEN_AUT_SOCKINET128', BSM_TOKEN_AUT_SOCKINET128]} 1134 | 1135 | #### FUNCTIONS #### 1136 | 1137 | # Formating a Token to be printed. 1138 | # 1139 | # Args: 1140 | # token_id: text name that identificate the Token ID 1141 | # token: the token structure to be formated. 1142 | # f: the bsm file. 1143 | # 1144 | # Return: 1145 | # A list with a well formated Token. 1146 | def FormatToken(token_id, token, f): 1147 | if token_id not in BSM_TYPE_LIST: 1148 | return u'Type unknown: {0} (0x{1:X})'.format(token_id, token_id) 1149 | bsm_type, _ = BSM_TYPE_LIST.get(token_id, ['', '']) 1150 | if bsm_type == 'BSM_TOKEN_TEXT': 1151 | return u'[{}: {}]'.format(bsm_type, _RawToUTF8(token)) 1152 | elif bsm_type == 'BSM_TOKEN_PATH': 1153 | return u'[{}: {}]'.format(bsm_type, _RawToUTF8(token)) 1154 | elif (bsm_type == 'BSM_TOKEN_RETURN32' or 1155 | bsm_type == 'BSM_TOKEN_RETURN64' or 1156 | bsm_type == 'BSM_TOKEN_EXIT'): 1157 | return u'[{}: {} ({}), System call status: {}]'.format( 1158 | bsm_type, BSM_ERRORS.get(token.status, 'Unknown'), 1159 | token.status, token.return_value) 1160 | elif (bsm_type == 'BSM_TOKEN_SUBJECT32' or 1161 | bsm_type == 'BSM_TOKEN_SUBJECT64'): 1162 | return (u'[{}: aid({}), euid({}), egid({}), uid({}), gid({}), ' 1163 | u'pid({}), session_id({}), terminal_port({}), ' 1164 | u'terminal_ip({})]'.format( 1165 | bsm_type, 1166 | token.subject_data.audit_uid, 1167 | token.subject_data.effective_uid, 1168 | token.subject_data.effective_gid, 1169 | token.subject_data.real_uid, 1170 | token.subject_data.real_gid, 1171 | token.subject_data.pid, 1172 | token.subject_data.session_id, 1173 | token.terminal_port, 1174 | _IPv4Format(token.ipv4))) 1175 | elif (bsm_type == 'BSM_TOKEN_SUBJECT32_EX' or 1176 | bsm_type == 'BSM_TOKEN_SUBJECT64_EX'): 1177 | if token.bsm_ip_type_short.net_type == AU_IPv6: 1178 | ip = _IPv6Format( 1179 | token.bsm_ip_type_short.ip_addr.high, 1180 | token.bsm_ip_type_short.ip_addr.low) 1181 | elif token.bsm_ip_type_short.net_type == AU_IPv4: 1182 | ip = _IPv4Format(token.bsm_ip_type_short.ip_addr) 1183 | else: 1184 | ip = 'unknown' 1185 | return (u'[{}: aid({}), euid({}), egid({}), uid({}), gid({}), ' 1186 | u'pid({}), session_id({}), terminal_port({}), ' 1187 | u'terminal_ip({})]'.format( 1188 | bsm_type, 1189 | token.subject_data.audit_uid, 1190 | token.subject_data.effective_uid, 1191 | token.subject_data.effective_gid, 1192 | token.subject_data.real_uid, 1193 | token.subject_data.real_gid, 1194 | token.subject_data.pid, 1195 | token.subject_data.session_id, 1196 | token.terminal_port, ip)) 1197 | elif (bsm_type == 'BSM_TOKEN_ARGUMENT32' or 1198 | bsm_type == 'BSM_TOKEN_ARGUMENT64'): 1199 | return u'[{}: {}({}) is 0x{:X}]'.format( 1200 | bsm_type, _RawToUTF8(token.value), 1201 | token.num_arg, token.name_arg) 1202 | elif (bsm_type == 'BSM_TOKEN_EXEC_ARGUMENTS' or 1203 | bsm_type == 'BSM_TOKEN_EXEC_ENV'): 1204 | arguments = [] 1205 | for _ in range(token): 1206 | arguments.append( 1207 | _RawToUTF8(BSM_TOKEN_EXEC_ARGUMENT.parse_stream( 1208 | f))) 1209 | return u'[{}: {}]'.format(bsm_type, u' '.join(arguments)) 1210 | elif (bsm_type == 'BSM_TOKEN_ZONENAME'): 1211 | return u'[{}: {}]'.format(bsm_type, _RawToUTF8(token)) 1212 | elif bsm_type == 'BSM_TOKEN_AUT_SOCKINET32': 1213 | return (u'[{0}: {1} ({2}) open in port {3}. Address {4}]'.format( 1214 | bsm_type, 1215 | BSM_PROTOCOLS.get(token.net_type, 'UNKNOWN'), 1216 | token.net_type, token.port_number, 1217 | _IPv4Format(token.ipv4))) 1218 | elif bsm_type == 'BSM_TOKEN_AUT_SOCKINET128': 1219 | return u'[{0}: {1} ({2}) open in port {3}. Address {4}]'.format( 1220 | bsm_type, 1221 | BSM_PROTOCOLS.get(token.net_type, 'UNKNOWN'), 1222 | token.net_type, token.port_number, 1223 | _Ipv6Format(token.ipv6.high, token.ipv6.low)) 1224 | elif bsm_type == 'BSM_TOKEN_ADDR': 1225 | return u'[{}: {}]'.format(bsm_type, _IPv4Format(token)) 1226 | elif bsm_type == 'BSM_TOKEN_IP': 1227 | return u'[IPv4_Header: 0x{}]'.format(token.encode('hex')) 1228 | elif bsm_type == 'BSM_TOKEN_ADDR_EXT': 1229 | return u'[{0}: {1} ({2}). Address {3}]'.format( 1230 | bsm_type, 1231 | BSM_PROTOCOLS.get(token.net_type, 'UNKNOWN'), 1232 | token.net_type, _Ipv6Format(token.ipv6.high, token.ipv6.low)) 1233 | elif bsm_type == 'BSM_TOKEN_PORT': 1234 | return u'[{}: {}]'.format(bsm_type, token) 1235 | elif bsm_type == 'BSM_TOKEN_TRAILER': 1236 | return u'[{}: {}]'.format(bsm_type, token.record_length) 1237 | elif bsm_type == 'BSM_TOKEN_FILE': 1238 | timestamp = token.timestamp 1239 | # TODO: Add microsecond... 1240 | # token.microsecond 1241 | human_timestamp = datetime.datetime.fromtimestamp( 1242 | timestamp).strftime('%Y-%m-%d %H:%M:%S') 1243 | return u'[{0}: {1}, timestamp: {2}]'.format( 1244 | bsm_type, _RawToUTF8(token.file_name), human_timestamp) 1245 | elif bsm_type == 'BSM_TOKEN_IPC': 1246 | return u'[{}: object type {}, object id {}]'.format( 1247 | bsm_type, token.object_type, token.object_id) 1248 | elif (bsm_type == 'BSM_TOKEN_PROCESS32' or 1249 | bsm_type == 'BSM_TOKEN_PROCESS64'): 1250 | return (u'[{}: aid({}), euid({}), egid({}), uid({}), gid({}), ' 1251 | u'pid({}), session_id({}), terminal_port({}), ' 1252 | u'terminal_ip({})]'.format( 1253 | bsm_type, 1254 | token.subject_data.audit_uid, 1255 | token.subject_data.effective_uid, 1256 | token.subject_data.effective_gid, 1257 | token.subject_data.real_uid, 1258 | token.subject_data.real_gid, 1259 | token.subject_data.pid, 1260 | token.subject_data.session_id, 1261 | token.terminal_port, 1262 | _IPv4Format(token.ipv4))) 1263 | elif (bsm_type == 'BSM_TOKEN_PROCESS32_EX' or 1264 | bsm_type == 'BSM_TOKEN_PROCESS64_EX'): 1265 | if token.bsm_ip_type_short.net_type == AU_IPv6: 1266 | ip = _IPv6Format( 1267 | token.bsm_ip_type_short.ip_addr.high, 1268 | token.bsm_ip_type_short.ip_addr.low) 1269 | elif token.bsm_ip_type_short.net_type == AU_IPv4: 1270 | ip = _IPv4Format(token.bsm_ip_type_short.ip_addr) 1271 | else: 1272 | ip = 'unknown' 1273 | return (u'[{}: aid({}), euid({}), egid({}), uid({}), gid({}), ' 1274 | u'pid({}), session_id({}), terminal_port({}), ' 1275 | u'terminal_ip({})]'.format( 1276 | bsm_type, 1277 | token.subject_data.audit_uid, 1278 | token.subject_data.effective_uid, 1279 | token.subject_data.effective_gid, 1280 | token.subject_data.real_uid, 1281 | token.subject_data.real_gid, 1282 | token.subject_data.pid, 1283 | token.subject_data.session_id, 1284 | token.terminal_port, ip)) 1285 | elif bsm_type == 'BSM_TOKEN_DATA': 1286 | data = [] 1287 | data_type = BSM_TOKEN_DATA_TYPE.get(token.data_type, '') 1288 | if data_type == 'AUR_CHAR': 1289 | for _ in range(token.unit_count): 1290 | data.append(BSM_TOKEN_DATA_CHAR.parse_stream(f)) 1291 | elif data_type == 'AUR_SHORT': 1292 | for _ in range(token.unit_count): 1293 | data.append(BSM_TOKEN_DAT_SHORT.parse_stream(f)) 1294 | elif data_type == 'AUR_INT32': 1295 | for _ in range(token.unit_count): 1296 | data.append(BSM_TOKEN_DATA_INTEGER.parse_stream(f)) 1297 | else: 1298 | data.append(u'Unknown type data') 1299 | # TODO: the data when it is string ends with ".", HW a space is return 1300 | # after uses the UTF-8 conversion. 1301 | return u'[{}: Format data: {}, Data: {}]'.format( 1302 | bsm_type, 1303 | BSM_TOKEN_DATA_PRINT[token.how_to_print], 1304 | _RawToUTF8(u''.join(data))) 1305 | elif (bsm_type == 'BSM_TOKEN_ATTR32' or 1306 | bsm_type == 'BSM_TOKEN_ATTR64'): 1307 | return (u'[{0}: Mode: {1}, UID: {2}, GID: {3}, ' 1308 | u'File system ID: {4}, Node ID: {5}, Device: {6}]'.format( 1309 | bsm_type, token.file_mode, token.uid, token.gid, 1310 | token.file_system_id, token.file_system_node_id, 1311 | token.device)) 1312 | elif bsm_type == 'BSM_TOKEN_GROUPS': 1313 | arguments = [] 1314 | for _ in range(token): 1315 | arguments.append( 1316 | _RawToUTF8(BSM_TOKEN_DATA_INTEGER.parse_stream( 1317 | f))) 1318 | return u'[{}: {}]'.format(bsm_type, u','.join(arguments)) 1319 | elif bsm_type == 'BSM_TOKEN_AUT_SOCKINET32_EX': 1320 | if BSM_PROTOCOLS.get(token.socket_domain, '') == 'INET6': 1321 | sadd = _Ipv6Format( 1322 | token.structure_addr_port.saddr_high, 1323 | token.structure_addr_port.saddr_low) 1324 | dadd = _Ipv6Format( 1325 | token.structure_addr_port.daddr_high, 1326 | token.structure_addr_port.daddr_low) 1327 | return u'[{}: from {} port {} to {} port {}]'.format( 1328 | bsm_type, sadd, token.structure_addr_port.source_port, 1329 | dadd, token.structure_addr_port.destination_port) 1330 | else: 1331 | return u'[{}: from {} port {} to {} port {}]'.format( 1332 | bsm_type, 1333 | _IPv4Format(token.structure_addr_port.source_address), 1334 | token.structure_addr_port.source_port, 1335 | _IPv4Format(token.structure_addr_port.destination_address), 1336 | token.structure_addr_port.destination_port) 1337 | elif bsm_type == 'BSM_TOKEN_OPAQUE': 1338 | return u'[{}: {}]'.format(bsm_type, token.encode('hex')) 1339 | elif bsm_type == 'BSM_TOKEN_SEQUENCE': 1340 | return u'[{}: {}]'.format(bsm_type, token) 1341 | 1342 | # Provide a readable IPv6 IP having the high and low part in 2 integers. 1343 | # Args: 1344 | # high: 64 bits integers number with the high part of the IPv6. 1345 | # low: 64 bits integers number with the low part of the IPv6. 1346 | # Returns: string with a well represented IPv6. 1347 | def _IPv6Format(high, low): 1348 | ipv6_string = IPV6_STRUCT.build( 1349 | construct.Container(high=high, low=low)) 1350 | return socket.inet_ntop( 1351 | socket.AF_INET6, ipv6_string) 1352 | 1353 | # Change an integer IPv4 address value for its 4 octets representation. 1354 | # Args: 1355 | # address: integer with the IPv4 address. 1356 | # Returns: IPv4 address in 4 octect representation (class A, B, C, D). 1357 | def _IPv4Format(address): 1358 | ipv4_string = IPV4_STRUCT.build(address) 1359 | return socket.inet_ntoa(ipv4_string) 1360 | 1361 | # Pyparsing reads in RAW, but the text must be in UTF8. 1362 | def _RawToUTF8(text): 1363 | try: 1364 | text = text.decode('utf-8') 1365 | except UnicodeDecodeError: 1366 | logging.warning( 1367 | u'Decode UTF8 failed, the message string may be cut short.') 1368 | text = text.decode('utf-8', 'ignore') 1369 | return text.partition('\x00')[0] 1370 | 1371 | # Read one BSM Event 1372 | # Args: 1373 | # f : BSM file. 1374 | # token_id: header token_id. 1375 | # event_number: the number of the event. 1376 | def ReadBSMEvent(f, token_id, event_number): 1377 | first_byte = f.tell() - 1 1378 | bsm_type, structure = BSM_TYPE_LIST.get(token_id, ['', '']) 1379 | if bsm_type == 'BSM_HEADER32': 1380 | token = structure.parse_stream(f) 1381 | elif bsm_type == 'BSM_HEADER64': 1382 | token = structure.parse_stream(f) 1383 | elif bsm_type == 'BSM_HEADER32_EX': 1384 | token = structure.parse_stream(f) 1385 | else: 1386 | print "[Error] At 0x{:X} header unknown.".format(f.tell()) 1387 | exit(1) 1388 | 1389 | data = [] 1390 | length = token.bsm_header.length 1391 | next_entry = first_byte + length 1392 | event_type = u'{0} ({1})'.format( 1393 | BSM_AUDIT_EVENT.get(token.bsm_header.event_type, 'UNKNOWN'), 1394 | token.bsm_header.event_type) 1395 | human_timestamp = datetime.datetime.fromtimestamp( 1396 | token.timestamp).strftime('%Y-%m-%d %H:%M:%S') 1397 | 1398 | # Read until we reach the end of the record. 1399 | while f.tell() < (first_byte + length): 1400 | # Check if it is a known token. 1401 | try: 1402 | token_id = BSM_TYPE.parse_stream(f) 1403 | except (IOError, construct.FieldError): 1404 | print ( 1405 | u'Unable to parse the Token ID at ' 1406 | u'position "{}"'.format(f.tell())) 1407 | return 1408 | # Unknown token id 1409 | if not token_id in BSM_TYPE_LIST: 1410 | f.seek(next_entry - f.tell(), os.SEEK_CUR) 1411 | print '\t[Unfinished] Event: {}.\n\tType: {}.\n\tTimestamp: {}.'.format( 1412 | event_number, event_type, human_timestamp) 1413 | for i in range(len(data)): 1414 | print u'\t{}'.format(data[i]) 1415 | print '' 1416 | return 1417 | else: 1418 | token = BSM_TYPE_LIST[token_id][1].parse_stream(f) 1419 | data.append(FormatToken(token_id, token, f)) 1420 | 1421 | if f.tell() > next_entry: 1422 | logging.warning( 1423 | u'Token ID {0} not expected at position 0x{1:X}.' 1424 | u'Jumping to the next entry'.format( 1425 | token_id, f.tell())) 1426 | f.seek(next_entry - f.tell(), os.SEEK_CUR) 1427 | return 1428 | print '\tEvent: {}.\n\tType: {}.\n\tTimestamp: {}.'.format( 1429 | event_number, event_type, human_timestamp) 1430 | for i in range(len(data)): 1431 | print u'\t{}'.format(data[i]) 1432 | print '' 1433 | 1434 | # Check if the file is a BSM file. 1435 | # 1436 | # Args: 1437 | # f : file that we want to check. 1438 | def VerifyFile(f): 1439 | type = BSM_TYPE.parse_stream(f) 1440 | if (BSM_TYPE_LIST[type][0] != 'BSM_HEADER32' and 1441 | BSM_TYPE_LIST[type][0] != 'BSM_HEADER64' and 1442 | BSM_TYPE_LIST[type][0] != 'BSM_HEADER32_ex'): 1443 | print '[Error] It is not a BSM file, unknown header token_id.' 1444 | exit(1) 1445 | try: 1446 | header = BSM_HEADER.parse_stream(f) 1447 | except: 1448 | print '[Error] It is not a BSM file, not a header structure.' 1449 | exit(1) 1450 | if header.version != AUDIT_HEADER_VERSION: 1451 | print '[WARNING] BSM version {} not supported.'.format(header.version) 1452 | f.close() 1453 | 1454 | 1455 | 1456 | # Main function. 1457 | def __init__(): 1458 | if len(sys.argv) != 2: 1459 | print 'Use: python {0} BSMfile'.format(sys.argv[0]) 1460 | exit(1) 1461 | log = sys.argv[1] 1462 | try: 1463 | f = open(log, 'rb') 1464 | except: 1465 | print '[Error] The file BSM does not exist' 1466 | exit(1) 1467 | 1468 | VerifyFile(f) 1469 | print '\nParsing BSM file [{}].\n'.format(log) 1470 | 1471 | try: 1472 | f = open(log, 'rb') 1473 | except: 1474 | print '[Error] The file BSM does not exist' 1475 | exit(1) 1476 | event_number = 0 1477 | token_id = BSM_TYPE.parse_stream(f) 1478 | while token_id: 1479 | event_number += 1 1480 | ReadBSMEvent(f, token_id, event_number) 1481 | try: 1482 | token_id = BSM_TYPE.parse_stream(f) 1483 | except: 1484 | token_id = None 1485 | f.close() 1486 | 1487 | 1488 | __init__() 1489 | 1490 | --------------------------------------------------------------------------------