What's in a (task) name

Fixing a bug in lsmp(1)

Jonathan Levin, (@Morpheus______), http://newosxbook.com/ - 02/02/2020

About

Apple's lsmp(1) is a wonderful little utility to list Mach ports, and is similar to what I do in procexp(j) $PID ports. By using mach_port_space_info and friends, the utility can dump Mach port names of any PID, and - more importantly - find their peers, by using the kobject: a (poorly) permuted 32-bit of the kernel address of the ipc_port_t in kernel. There is a great discussion of this in my MOXiI Volume I (chapter 11) and Volume II (chapter 10), so I'm leaving that discussion out. Btw, the man page for lsmp(1) isn't installed by default IIRC, but it's part of the source (in system_cmds).

The utility, however, is also pretty hard to use - (terrible output formatting and a bug when used with no arguments, just printing out \n...) - and has a functional bug. It won't display TASK-NAME ports correctly, due to an "optimization" and Apple's user-space Mach folk not talking to the kernel space folk. It will display all TASK-NAME ports as pointing to the owning task, which is just wrong. TASK-NAME ports (the "unprivileged" task ports) are (unfortunately) rarely used, so the bug must have slipped Apple. In fact, it slipped me, till I started xn00ping around the kernel.

Fortunately, it's not that hard to fix.

THIS IS NOT AN EXPLOITABLE VULNERABILITY. If all you care about are CVEs, you're wasting your time. If you want to understand how Mach APIs work, read on

The Bug

Looking at the code handling the TASK-NAME and TASKs we see:

Two comments here stand out:

The first one is correct. In fact, too correct. mach_port_kernel_object works, BUT, it's intentionally broken. Observe the latest sources, from xnu-4903 (because AAPL is taking FOREVER WITH XNU-6153, #%#$%$#$%) for mach_port_kobject() (which is called from the former anyway):

Don't worry about MACH_IPC_DEBUG - that part is fine. But note DEVELOPMENT || DEBUG - both of which are not set (for obvious reasons - have I mentioned that the V_KERNEL_UNSLIDE_OR_PERM is terrible??). So kobject (*addrp) will be set to 0, even though ikot_type (*typep) will be (sometimes) non-zero, and the return value overall will be a KERN_SUCCESS. So - taskinfo->task_kobject == kobject will always be true (because both are 0), and the "optimization" will think that the NAME is a SELF pointer..

The fix

So, mach_port_kernel_object won't work. BUT mach_port_space_info still does, and will report the kobject value in the ipc_entry's iin_object (assuming we have the entitlements, or SIP is disabled). So, here are the fixes:

In port_details.c:

In task_details.c: First add a task_name field:

..and then collect the names to our own task:

..and expose our task information (because that's all static) through a getter:

You also need to add the task_name field in common.h, alongwith the prototype for getMyTaskDetails().

Download fixes:

Results

You can't fix this for MacOS when SIP is enabled since that despicable AMFI won't allow com.apple.system-task-ports and task_for_pid-allow. But it does work if SIP is disabled, or , on iOS, since you can easily fake sign with jtool2:

And, running the original vs. the new, and double checking with xn00p:

procexp $PID ports outdoes lsmp(1) anytime because, in addition to the ports, it also talks to launchd(1) over the bootstrap pipe to get the actual Mach service names. I got unofficial word from AAPL that they liked my trick there (an undocumented technique I also put into my launjctl(j)), but to reward me, they made launchd(1) only talk to platform binaries - i.e. it only works now in iOS..


  • Volume I - User Mode - Available, v1.3.3(7) - The FINAL update - ToC
  • Volume II - Available
  • Volume III - Security & Insecurity is available, v1.6

  • It's cheaper (and better for me) if you buy direct - by emailing moxii at this site, but if you use AMZN please follow these links? Thank you!