Beyond the good ol' LaunchAgents - 34 - launchd boot tasks

This is part 34 in the series of “Beyond the good ol’ LaunchAgents”, where I try to collect various persistence techniques for macOS. For more background check the introduction.

The all mighty launchd, contains an embedded plist file in its __TEXT __config section, which contains various settings, BootStrap file locations (like LaunchDaemons and LaunchAgents) and it has also a Boot key, which defines various services, which will be run upon boot. They are called boot tasks. Although this is documented in Jonathan Levin’s *OS Internals Vol I. book, I think this is a not well known, and in fact I also totally forgot about it until Matt brought them to my attention recently.

We can dump this plist with the following one liner, which is pretty massive due to the fact that the plist is stored in little endian format.

otool -X -s __TEXT __config /sbin/launchd | awk '{print $2 $3 $4 $5}' | xxd -r -p | hexdump -v -e '1/4 "%08x"' -e '"\n"' | xxd -r -p 

I think the BootStrap config is very interesting as it contains more locations for Launch* files, than we are normally aware of. /private/var/db/ManagedConfigurationFiles/BackgroundTaskServices/Launch*/ especially stands out, this location is also SIP protected.

But let’s focus on how we can utilize this configuration to persist our own stuff.

There are 6 entries that we can potentially utilize, but each of them will require extra access beyond having root privileges. The first three are binaries which can be found either in /Library/Apple/ or /private/var/install.

<key>finish-demo-restore</key>
<dict>
	<key>Program</key>
	<string>/Library/Apple/usr/libexec/finish_demo_restore</string>
	<key>PerformAfterUserspaceReboot</key>
	<true/>
	<key>RequireSuccess</key>
	<true/>
</dict>

<key>shutdown_installer_tasks</key>
<dict>
	<key>Program</key>
	<string>/private/var/install/shutdown_installer_tasks</string>
	<key>RequireSuccess</key>
	<true/>
	<key>PerformAfterUserspaceReboot</key>
	<true/>
</dict>

<key>deferred_install</key>
<dict>
	<key>RebootOnSuccess</key>
	<true/>
	<key>RequireSuccess</key>
	<true/>
	<key>Program</key>
	<string>/private/var/install/deferred_install</string>
	<key>PerformAfterUserspaceReboot</key>
	<true/>
</dict>

These files don’t exists by default, and will only show up during installations, but in order for us to place them we need some sort of SIP bypass privileges.

The second set of three items are scripts from /etc/, and all of them are rc.* files.

<key>rc.server</key>
<dict>
	<key>Program</key>
	<string>/etc/rc.server</string>
	<key>PerformAfterUserspaceReboot</key>
	<true/>
	<key>AllowCrash</key>
	<true/>
</dict>

<key>rc.cdrom</key>
<dict>
	<key>PassLaunchBootModeAsArgument</key>
	<true/>
	<key>PerformInBaseSystem</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>rc.cdrom</string>
	</array>
	<key>Program</key>
	<string>/etc/rc.cdrom</string>
	<key>AllowCrash</key>
	<true/>
</dict>

<key>rc.netboot</key>
<dict>
	<key>RequireSuccess</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>rc.netboot</string>
		<string>init</string>
	</array>
	<key>Program</key>
	<string>/etc/rc.netboot</string>
</dict>

From these three only rc.netboot exists on default installations in macOS Sequoia. To create or edit any of these files, we will need kTCCServiceSystempolicySysAdminFiles TCC permissions (or the joker - Full Disk Access). If we don’t have one we will be presented with the following dialog.

TCC Prompt

Although it’s easier to obtain than a SIP bypass exploit, it can be still a massive limitation.

In summary, location which will require root and some sort of SIP bypass exploit:

/Library/Apple/usr/libexec/finish_demo_restore
/private/var/install/shutdown_installer_tasks
/private/var/install/deferred_install

Locations which will require root access and TCC permissions:

/etc/rc.server
/etc/rc.cdrom
/etc/rc.netboot

The full embedded plist is available here:

https://gist.github.com/theevilbit/1d4508fb5d5dcb9f4c27771dfcdb5a1d