#!/usr/bin/python # bom_safety.py # MOAB-05-01-2007: Apple DiskManagement BOM Local Privilege Escalation Vulnerability # # Copyright (c) 2007 William A. Carrel # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of William Carrel nor the names of any contributors # may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import grp import os import os.path import struct import sys # Big old list of dangerous BOMs that Repair Permissions uses. # Thanks Finlay! dangerous_boms=["/Library/Receipts/BaseSystem.pkg/Contents/Archive.bom", "/Library/Receipts/Essentials.pkg/Contents/Archive.bom", "/Library/Receipts/AdditionalEssentials.pkg/Contents/Archive.bom", "/Library/Receipts/BSD.pkg/Contents/Archive.bom", "/Library/Receipts/BSDSDK.pkg/Contents/Archive.bom", "/Library/Receipts/X11User.pkg/Contents/Archive.bom", "/Library/Receipts/X11SDK.pkg/Contents/Archive.bom", "/Library/Receipts/CommonAccessCard.pkg/Contents/Archive.bom", "/Library/Receipts/CommonCriteriaTools.pkg/Contents/Archive.bom", "/Library/Receipts/Internal.pkg/Contents/Archive.bom", "/Library/Receipts/FatLibraries.pkg/Contents/Archive.bom", "/Library/Receipts/DevDocumentation.pkg/Contents/Archive.bom", "/Library/Receipts/DevExamples.pkg/Contents/Archive.bom", "/Library/Receipts/DevSDK.pkg/Contents/Archive.bom", "/Library/Receipts/DeveloperTools.pkg/Contents/Archive.bom", "/Library/Receipts/Java.pkg/Contents/Archive.bom", "/Library/Receipts/DevInternal.pkg/Contents/Archive.bom", "/Library/Receipts/DevFatLibraries.pkg/Contents/Archive.bom", "/Library/Receipts/AddressBook.pkg/Contents/Archive.bom", "/Library/Receipts/Automator.pkg/Contents/Archive.bom", "/Library/Receipts/Mail.pkg/Contents/Archive.bom", "/Library/Receipts/MigrationAssistant.pkg/Contents/Archive.bom", "/Library/Receipts/OxfordDictionaries.pkg/Contents/Archive.bom", "/Library/Receipts/iCal.pkg/Contents/Archive.bom", "/Library/Receipts/iChat.pkg/Contents/Archive.bom", "/Library/Receipts/iTunes.pkg/Contents/Archive.bom", "/Library/Receipts/MicrosoftIE.pkg/Contents/Archive.bom", "/Library/Receipts/Safari.pkg/Contents/Archive.bom", "/Library/Receipts/AdditionalFonts.pkg/Contents/Archive.bom", "/Library/Receipts/AdditionalAsianFonts.pkg/Contents/Archive.bom", "/Library/Receipts/BrotherPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/EpsonPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/CanonPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/HewlettPackardPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/LexmarkPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/GimpPrintPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/ElectronicsForImagingPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/RicohPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/XeroxPrinterDrivers.pkg/Contents/Archive.bom", "/Library/Receipts/QuickTimeStreamingServer.pkg/Contents/Archive.bom", "/Library/Receipts/ApplicationsServer.pkg/Contents/Archive.bom", "/Library/Receipts/ServerFatLibraries.pkg/Contents/Archive.bom", "/Library/Receipts/ServerInternal.pkg/Contents/Archive.bom", "/Library/Receipts/ServerAdminTools.pkg/Contents/Archive.bom", "/Library/Receipts/ServerSetup.pkg/Contents/Archive.bom", "/Library/Receipts/ServerEssentials.pkg/Contents/Archive.bom"] def CreatePlaceholder(path): root_uid = 0 admin_gid = grp.getgrnam('admin')[2] open(path, 'w') os.chown(path, root_uid, admin_gid) os.chmod(path, 0644) def main(): # Make sure we have root if os.geteuid() != 0: print "Sorry, we need to be root for this." sys.exit(1) # Make sure we don't clobber the backup if os.access("%s.orig" % dangerous_boms[0], os.F_OK): print "%s.orig exists!" % dangerous_boms[0] print "It is the backup, and should be moved somewhere safe before running" print "this script again." sys.exit(1) os.umask(022) # First, we make /Library/Receipts sticky so Mallory can't rename the # sensitive BOM's out of the way os.chmod("/Library/Receipts", 01775) for bom in dangerous_boms: # Determine the parent directories we need to protect contents_dir = os.path.dirname(bom) pkg_dir = os.path.dirname(contents_dir) # If these paths don't exist, we make placeholders to block Mallory. if os.access(pkg_dir, os.F_OK): os.chmod(pkg_dir, 01775) if os.access(contents_dir, os.F_OK): os.chmod(contents_dir, 01775) if os.access(bom, os.F_OK): os.chmod(bom, 0644) else: # BOM doesn't exist CreatePlaceholder(bom) else: # contents_dir doesn't exist CreatePlaceholder(contents_dir) else: # pkg_dir doesn't exist CreatePlaceholder(pkg_dir) # Lastly, we edit BaseSystem.pkg's BOM so that "Repair Permissions" doesn't # take the sticky bit off of /Library Receipts. bom_contents = open(dangerous_boms[0],"rb").read() # Make a backup backupfile = open("%s.orig" % dangerous_boms[0],"wb") backupfile.write(bom_contents) backupfile.close() # Now for the fun part... # Find the string "Receipts" in the BOM file, it only occurs once. receipts_pos = bom_contents.find("Receipts") receipts_perm = struct.unpack(">H", bom_contents[receipts_pos-23:receipts_pos-21])[0] if receipts_perm != 040775: print "Receipts perms in BOM wrong: %o instead of 40775" % receipts_perm sys.exit(1) # Alright, here we go... bomfile = open(dangerous_boms[0],"wb") bomfile.write(bom_contents[:receipts_pos-23]) bomfile.write(struct.pack(">H", 041775)) bomfile.write(bom_contents[receipts_pos-21:]) bomfile.close() # Done! print "BOM safety completed" if __name__ == '__main__': main()