Home » Industry Watch » Coldspots
.SoftwareUpdateAtLogout'I didn't have to enter my password to update. Is this typical for everyone?'
The Safari 4.0.5 update: Leopard users had to submit their password, Snow Leopard users didn't. There's a 'technology' under the bonnet with beginnings in 10.4 Tiger for this. An earlier article looked briefly at the mystery.
The Safari update - whether targeting 10.5 Leopard or 10.6 Snow Leopard - needs root privileges. The catch is that 10.5 users are required to submit their admin passwords (or the equivalent) whilst 10.6 users are not - they somehow get root escalation for free.
Code run from user land that can trigger (fool) a system into performing tasks as root represents a root exploit of the system. Both the Opener hole and the systemloginitems hole were root exploits. They were open for a long time; they seem to have finally been closed. But there could be another one.
The discovery comes courtesy of an 'innocent' comment by 'zapblast' at the MacRumors forums. It was also noted by others that Leopard 10.5 required authentication (submitting a password) to do the same thing.
The system runs at 'SUM (single user mode) root' before and after login. There is no 'user' per se in either case. There is not even root. There is only SUM (single user mode) root which coincidentally has a higher privilege level than ordinary root itself.
Something is triggering a change in the otherwise protected logout procedure. Software Update.app and its dependencies contain some of the clues.
/System/Library/CoreServices/Software Update.app
/System/Library/PrivateFrameworks/SoftwareUpdate.framework
Running Xstrings on the Software Update binary yields the following.
000000000001423e prepareForLogoutAndInstall
000000000001425d adminAuthorizationForInstall:
000000000001489c _willProductsRequireLogout:
0000000000014b9a runPrivilegedHelperTool
0000000000014bb2 preauthorizeForPrivilegedHelperTool
0000000000014bd6 isCurrentUserAdministrator
0000000000014bf1 preauthorizeForInstallationOfProducts:
The code is prepared to prompt for a password. Software Update.app for 10.5 namely had a 'suapphelper' embedded.
0000000000001f84 admin
0000000000001f8c /var/db/.SoftwareUpdateAtLogout
0000000000001fac /Library/Updates
0000000000004160 com.apple.suapphelper
0000000000006f84 admin
0000000000006f8c /var/db/.SoftwareUpdateAtLogout
0000000000006fac /Library/Updates
0000000000008180 com.apple.suapphelper
000000000000822a com.apple.translate
Running Xstrings on the Software Update framework binary yields the following.
0000000000026eca prepareForLogoutAndInstall
0000000000026eec c24@0:8^{AuthorizationOpaqueRef=}16
0000000000026f12 SUHelperProxy
0000000000026f20 adminAuthorizationForInstall:
0000000000026f40 bytes
0000000000026f4c isCurrentUserAdministrator
0000000000026f6c autorelease
0000000000026f78 length
0000000000026f7f com.apple.suhelperd
0000000000026f98 /Library/Updates
0000000000026fb0 /var/db/.SoftwareUpdateAtLogout
0000000000026fd0 index.trust
0000000000026fdc root
0000000000026fec index.plist
0000000000026ff8 Unable to remove old index.trust
0000000000027019 Could not write index.trust
0000000000027038 Failed permission set on index.trust
0000000000027060 Unknown file type encountered removing installed update.
00000000000270a0 Failed to remove directory at path %s
Note the entry at 0000000000026fb0: the system puts and then looks for what may be an empty file at the path /var/db/.SoftwareUpdateAtLogout.
Note as well that the path /var/db is root-owned and protected and remember that Snow Leopard users are not required to authenticate.
The path /Library/Updates is also mentioned. See the entry at 0000000000026fec - the 'index.plist' for Safari 4.0.5 is reproduced below. This file specifies which updates are to be installed. (The updates are found in the same hive.)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>InstallAtLogout</key>
<array>
<string>061-7784</string>
</array>
<key>ProductPaths</key>
<dict>
<key>061-7784</key>
<string>061-7784</string>
</dict>
</dict>
</plist>
There are also repeated references to 'index.trust'.
suhelperd, SUHelperProxy
suhelperd may also be a key.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.suhelperd</string>
<key>MachServices</key>
<dict>
<key>com.apple.suhelperd</key>
<true/>
</dict>
<key>OnDemand</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/System/Library/PrivateFrameworks/SoftwareUpdate.framework/Resources/suhelperd</string>
</array>
</dict>
</plist>
But suhelperd is only 'owned' by root - it doesn't have a set ID bit set. It can only run as root if a root process starts it. The first launchd process (a root process) starts suhelperd when Software Update.app checks for updates - and runs it as root. Some form of IPC would need to be used - how can launchd/suhelperd know the request's genuine? By checking bundle IDs and root certificates? Signed code can be subverted and the user isn't in control.
The SUHelperProxy class might also be a clue.
0000000000051028 -[SUHelperProxy isAuthorized]
0000000000051046 -[SUHelperProxy _createAdminAuthorizationForDaemon]
000000000005107a -[SUHelperProxy init]
0000000000051090 -[SUHelperProxy dealloc]
00000000000510a9 -[SUHelperProxy authorizeTool:]
00000000000510c9 -[SUHelperProxy prepareForLogoutAndInstall]
00000000000510f5 -[SUHelperProxy moveSharedFilesFromTempPath:forProductKey:]
0000000000051131 -[SUHelperProxy makeQueues]
000000000005114d -[SUHelperProxy removeMetadataCacheFromUpdates]
000000000005117d -[SUHelperProxy moveMetadataCacheToUpdatesFromPath:]
00000000000511b2 -[SUHelperProxy removeIndexFromUpdates]
00000000000511da -[SUHelperProxy moveIndexToUpdatesFromPath:]
0000000000051207 -[SUHelperProxy createDirectoryForProductKey:]
0000000000051236 -[SUHelperProxy removeDistForProductKey:withFilename:]
000000000005126d -[SUHelperProxy removeProductDirectoryForKey:]
000000000005129c -[SUHelperProxy sessionLock]
00000000000512b9 -[SUHelperProxy acquireSessionLock:]
00000000000512de -[SUHelperProxy releaseSessionLock:]
0000000000051303 -[SUHelperProxy cancelSessionLockBelowPriority:]
0000000000051334 -[SUHelperProxy createSpecialModeCookie:]
000000000005135e -[SUHelperProxy removeSpecialModeCookie]
0000000000051387 -[SUHelperProxy _launchDaemonMode]
00000000000513aa +[SUHelperProxy sharedProxy]
00000000000513c7 -[SUHelper run]
00000000000513d7 -[SUHelper _isClientPort:]
00000000000513f2 -[SUHelper sessionLock]
000000000005140a -[SUHelper(PrivateHelperMethods) _trustFilePath]
000000000005143b -[SUHelper(PrivateHelperMethods) _moveFileAndSetSanePermissionsFrom:to:]
0000000000051484 -[SUHelper(PrivateHelperMethods) _sharedPathForProductKey:createIfMissing:]
00000000000514d0 -[SUHelper(PrivateHelperMethods) _isSaneProductKey:]
0000000000051505 -[SUHelper removeSpecialModeCookie]
0000000000051529 -[SUHelper removeDistForProductKey:withFilename:]
000000000005155b -[SUHelper createDirectoryForProductKey:]
0000000000051585 -[SUHelper moveIndexToUpdatesFromPath:]
00000000000515ad -[SUHelper removeIndexFromUpdates]
00000000000515d0 -[SUHelper moveMetadataCacheToUpdatesFromPath:]
0000000000051600 -[SUHelper removeMetadataCacheFromUpdates]
000000000005162b -[SUHelper makeQueues]
0000000000051642 -[SUHelper moveSharedFilesFromTempPath:forProductKey:]
0000000000051679 -[SUHelper init]
000000000005168a -[SUHelper dealloc]
000000000005169e -[SUHelper createSpecialModeCookie:]
00000000000516c3 -[SUHelper removeProductDirectoryForKey:]
00000000000516ed -[SUHelper _areRightsValid:]
000000000005170a -[SUHelper _vendMachPortForPID:]
000000000005172b -[SUHelper _removeDeadPort:]
0000000000051748 -[SUHelper _shadowAuthorization:]
000000000005176a -[SUHelper authorizeTool:]
0000000000051785 -[SUHelper prepareForLogoutAndInstall]
00000000000517ac -[SUHelper acquireSessionLock:]
00000000000517cc -[SUHelper releaseSessionLock:]
00000000000517ec -[SUHelper cancelSessionLockBelowPriority:]
10.4 Tiger, 10.5 Leopard
Some of the above code is found in 10.5 Leopard where users are still asked to authenticate. Note the reference to the authorisation services ('system.privilege.admin') and the key argument 'preauthorize'.
0000000000022dac /var/db/.SoftwareUpdateAtLogout
0000000000022dcc /Library/Updates
0000000000022ddd caseInsensitiveCompare:
0000000000022df5 CaseInsensitive
0000000000022e08 c8@0:4
0000000000022e10 ^{AuthorizationOpaqueRef=}8@0:4
0000000000022e30 preauthorize
0000000000022e40 system.privilege.admin
0000000000022e58 @(#)PROGRAM:SoftwareUpdate PROJECT:SoftwareUpdate-182
The code is not found in Tiger 10.4.
launchd-shutdown.log
The following was part of a test run to see if the system could be provoked into revealing more about this 'secret hook'.
sudo touch /var/db/.SoftwareUpdateAtLogout
sudo touch /var/db/index.trust
index.trust remained after reboot but .SoftwareUpdateAtLogout was gone. The 300 KB file 'launchd-shutdown.log' contained several mentions of com.apple.SoftwareUpdate and the helper daemon.
-73795 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Stopping job.
-73770 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Sent SIGTERM signal
-72525 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Job was sent SIGTERM.
-71582 1 com.apple.launchd 0 com.apple.suhelperd Job is inactive. Removing.
-71571 1 com.apple.launchd 0 com.apple.suhelperd Closing receive right for com.apple.suhelperd
-71554 1 com.apple.launchd 0 com.apple.suhelperd Mach service deleted: com.apple.suhelperd
9053 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Mach service deleted (port died): WakeUpProcessPort
9147 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Mach service deleted (port died): com.apple.axserver
9181 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Mach service deleted (port died): com.apple.tsm.portname
9292 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Dispatching kevent callback.
9296 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate EVFILT_PROC event for job:
9366 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Reaping
9416 1 com.apple.launchd 5465 .com.apple.SoftwareUpdate Exited 0.082999 seconds after the first signal was sent
9461 1 com.apple.launchd 0 .com.apple.SoftwareUpdate Watching job (kickstart = false)
9465 1 com.apple.launchd 0 .com.apple.SoftwareUpdate Watching...
9517 1 com.apple.launchd 0 .com.apple.SoftwareUpdate Job is inactive. Removing.
9553 1 com.apple.launchd 0 .com.apple.SoftwareUpdate Removed
There are further references to 'root' in the file 'Distribution' in the Safari 4.0.5 download.
<choice id='su' suDisabledGroupID='Safari405SnowLeopard'>
<pkg-ref id='com.apple.pkg.Safari405SnowLeopard' auth='Root' onConclusion='RequireRestart' >
Safari4.0.5SnowLeopard.pkg
</pkg-ref>
</choice>
<choice id='manual'>
<pkg-ref id='manual' auth='Root' onConclusion='RequireRestart' >
#Safari4.0.5SnowLeopard.pkg
</pkg-ref>
</choice>
suhelperd has the key character strings as suspected.
0000000000007d80 /Library/Updates
0000000000007d98 /var/db/.SoftwareUpdateAtLogout
0000000000007db8 priority
0000000000007dc8 <%@ uid=%d, pid=%d, priority=%d
0000000000007de8 SUSessionLockAcquiredNotification
0000000000007e10 SUSessionLockReleasedNotification
0000000000007e38 SUSessionLockPreemptedNotification
There is no such module or the equivalent (suhelperd) on 10.4 or 10.5. This new twist in '.SoftwareUpdateAtLogout' 'technology' seems to have been implemented only for 10.6.
There are references to to /var/db in the launchd binary but they seem to be used in a different context.
The Bottom Line, The Weakest Link, The Closed Open Source
So how can Apple suddenly 'get root' on 10.6 without user authentication? A lot of people would really love to know.
Testing this peculiar scenario is time consuming. With enough time (and patience) it might be possible to get the system to reveal more of what is going on.
Things are absolutely great if all downloaded binaries and application packages are signed by Apple and/or 'index.trust' contains some sort of indication of authenticity.
But the fact remains that the scenario is set up a lot earlier by a process originating in 'user land' that's never escalated as was done explicitly on 10.5 and earlier.
And the only ostensible advantage to doing things this way is to not have to require a password.
With Apple's track record - the Opener and system login items security craters which were open for years and years - one would think they'd play it safe and just scrap the idea as folly and potentially bad PR, when all they win is not having to make the user submit a password.
Anyone (white hat or black) can know approximately how the system works: know where the flag file goes, know where the system will look for the actual update (which can be replaced by a rootkit). It could take a lot of time and a lot of patience to see if the system can be provoked into revealing more - entries in logs about encountering bogus logout commands etc.
But the file /var/db/.SoftwareUpdateAtLogout is created prior to logout and root privileges are needed to do anything in /var/db. But there's no privilege escalation for the software update, the Software Update binaries don't have a set user ID bit, and so forth.
Something in the system creates that file as root without user authentication. This means there's a 'trigger' in the system of some sort - some kind of IPC that's making this happen.
And that's where the weak spot would be.
The workings of Unix systems are well known. People know what file ownership means and they know how permissions and privilege escalation work. In knowing these things, they can trust their system.
Apple don't like documenting things. They like keeping parts of their 'open source' system closed. This is good for no one.
'I didn't have to enter my password to update. Is this typical for everyone?' - 'zapblast' at the MacRumors forums 'The media buzz over Opener went on the better part of a month and was then forgotten, but the fact remains that it is the single biggest security hole ever in the history of modern operating systems. No other operating system has ever offered such effortless escalation to superuser.' - Rixstep Industry Watch on Opener 3.9
|