Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2017-2370 — auf Mac 10.12.2 | Kitploit
Tools/GitHubGitHub/peterpan0927/cve-2017-2370
Privilege EscalationiOS-SicherheitSpeicherforensikExploitationInformationsbeschaffungBinary-Exploitation
GitHubpeterpan0927/cve-2017-2370

CVE-2017-2370

auf Mac 10.12.2

Repository anzeigen
20324vor 8 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

0x00. Einleitung

Dieser Privilege-Escalation-Exploit nutzt eine Schwachstelle in mach_voucher_extract_attr_recipe_trap, und die Kernmethode des Exploits basiert auf MACH_MSG_OOL_PORTS_DESCRIPTOR-Nachrichten.

Einfach gesagt: Wenn eine mach_msg eine ool descriptor-Nachricht sendet, kopiert der Kernel die angegebenen Daten aus dem Benutzerraum in den Kernelraum und behält diese Daten bei, bis die Ziel-Task die Nachricht verarbeitet. Wenn die Ziel-Task eine Nachricht mit ool descriptor empfängt, kopiert der Kernel die Daten aus dem Kernelraum in den Benutzerraum (nicht unbedingt eine echte Kopie). Daher kann dieser technische Punkt genutzt werden, um Daten in den Kernel-Heap zu schreiben oder aus dem Kernel zu lesen.

Da dieser Exploit deutlich komplexer ist als Trident, werde ich ihn Schritt für Schritt analysieren – von der Entstehung der Schwachstelle bis zur vollständigen Ausnutzung. Der Code ist auf meinem GitHub verfügbar.

0x01. Entstehung der Schwachstelle

In iOS 10 und macOS 10.12 wurde eine neue Funktion namens mach_voucher_extract_attr_recipe_trap hinzugefügt. Es handelt sich um einen Mach trap, der innerhalb der Sandbox aufgerufen werden kann. Nachfolgend der Quellcode dieser Funktion:

kern_return_t
  mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args *args)
  {
    ipc_voucher_t voucher = IV_NULL;
    kern_return_t kr = KERN_SUCCESS;
    mach_msg_type_number_t sz = 0;
	// Kopiert die Adresse von recipe_size in sz; sz enthält dann den Wert von kalloc_size
    if (copyin(args->recipe_size, (void *)&sz, sizeof(sz)))     <---------- (a)
      return KERN_MEMORY_ERROR;

    if (sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE)
      return MIG_ARRAY_TOO_LARGE;

    voucher = convert_port_name_to_voucher(args->voucher_name);
    if (voucher == IV_NULL)
      return MACH_SEND_INVALID_DEST;

    mach_msg_type_number_t __assert_only max_sz = sz;

    if (sz < MACH_VOUCHER_TRAP_STACK_LIMIT) {
      /* keep small recipes on the stack for speed */
      uint8_t krecipe[sz];
      if (copyin(args->recipe, (void *)krecipe, sz)) {
        kr = KERN_MEMORY_ERROR;
        goto done;
      }
      kr = mach_voucher_extract_attr_recipe(voucher, args->key,
                                            (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
      assert(sz <= max_sz);

      if (kr == KERN_SUCCESS && sz > 0)
        kr = copyout(krecipe, (void *)args->recipe, sz);
    } else {
      uint8_t *krecipe = kalloc((vm_size_t)sz);                 <---------- (b)
      if (!krecipe) {
        kr = KERN_RESOURCE_SHORTAGE;
        goto done;
      }

      if (copyin(args->recipe, (void *)krecipe, args->recipe_size)) {         <----------- (c)
        kfree(krecipe, (vm_size_t)sz);
        kr = KERN_MEMORY_ERROR;
        goto done;
      }

      kr = mach_voucher_extract_attr_recipe(voucher, args->key,
                                            (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
      assert(sz <= max_sz);

      if (kr == KERN_SUCCESS && sz > 0)
        kr = copyout(krecipe, (void *)args->recipe, sz);
      kfree(krecipe, (vm_size_t)sz);
    }

    kr = copyout(&sz, args->recipe_size, sizeof(sz));

  done:
    ipc_voucher_release(voucher);
    return kr;
  }
  1. Durch Analyse lässt sich erkennen, dass an Punkt (a) der 4-Byte-Benutzerraumzeiger args->recipe_size in sz geschrieben wird.
  2. An Punkt (b): Wenn sz zwischen MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE (5120) und MACH_VOUCHER_TRAP_STACK_LIMIT (256) liegt, wird ein Kernel-Heap-Puffer der Größe sz allokiert.
  3. An Punkt (c) wird der Benutzerraumspeicher in den gerade allokierten Bereich kopiert, aber die übergebene Kopiergröße ist nicht das für die Heap-Allokation verwendete sz, sondern ein Benutzerraumzeiger. Dies führt zu einem Heap-Überlauf, den wir ausnutzen. Zudem hat die Funktion copyin die Eigenschaft, dass sie bei einer nicht gemappten Seite abbricht – diese Eigenschaft wird in unserem PoC ausgenutzt:

copyin

0x01. Ausnutzungsschritte

  1. Zunächst müssen wir den Heap-Speicher kontrollieren. Dazu verwenden wir die Technik des Heap Fengshui, da nach der Randomisierung der freelist die Position neu zugewiesener Speicherblöcke nicht mehr vorhersagbar ist.

Zuerst müssen wir verstehen, wie mach msg mit MACH_MSG_OOL_PORTS_DESCRIPTOR umgeht. Wenn der Kernel eine komplexe Nachricht empfängt und feststellt, dass es sich um einen ports descriptor handelt, übergibt er diese (aufgerufen durch ipc_kmsg_copyin) an die Funktion ipc_kmsg_copyin_ool_ports_descriptor, um alle Port-Objekte auszulesen. Diese Funktion ruft kalloc auf, um den benötigten Speicher zu allozieren (auf 64-Bit-Systemen ist der allozierte Speicher das Doppelte der Eingabe, da die Länge der name 4 Bytes beträgt) und konvertiert dann gültige port-name-Werte in die tatsächliche ipc_port-Objektadresse. Für Eingaben von MACH_PORT_NULL oder MACH_PORT_DEAD bleiben die Werte unverändert.

/* calculate length of data in bytes, rounding up */
if (os_mul_overflow(count, sizeof(mach_port_t), &ports_length)) { 
	*mr = MACH_SEND_TOO_LARGE; 
	return NULL; 
} 

if (os_mul_overflow(count, sizeof(mach_port_name_t), &names_length)) { 
    *mr = MACH_SEND_TOO_LARGE;
	return NULL; 
} 

if(ports_length == 0){
    return user_desc;
}

data = kalloc(ports_length); // Allokiert Speicher
... 
objects = (ipc_object_t *) data; 

dsc->address = data; 

for ( i = 0; i < count; i++) { 
    mach_port_name_t name = names[i]; 
    ipc_object_t object;
    if (!MACH_PORT_VALID(name)) {
        objects[i] = (ipc_object_t)CAST_MACH_NAME_TO_PORT(name);// IPC_PORT_DEAD continue; 
    } 
...
}

Beim Angriff senden wir daher viele MACH_PORT_DEAD, um den Speicherbereich mit 0xFFFFFFFFFFFFFFFF (MACH_PORT_DEAD) zu füllen, und lösen dann die Schwachstelle aus, um einen der IPC_PORT_DEAD in einen vom Angreifer vorbereiteten Speicherbereich zu ändern. Wenn der Bereich auf eine gültige ipc port-Struktur zeigt, kann der Angreifer nach dem Empfang der OOL PORTS-Nachricht im Benutzerraum den port name erhalten, der diesem ipc_port entspricht, und den nächsten Angriffsschritt durchführen.

Heap Fengshui

  1. Konstruktion des ipc_object-Objekts

Da wir diesen fake port bereits haben, müssen wir für die Informationsoffenlegung wissen, nach welchen Parametern der Kernel ihn unterschiedlich behandelt. Zunächst betrachten wir die Struktur von ipc_port:

struct ipc_port {
	// Der Zeiger auf ipc_object befindet sich in den ersten acht Bytes – Ziel unseres Überlaufangriffs
	struct ipc_object ip_object; // Typ des Port-Objekts struct ipc_mqueue,ip_messages;
	struct ipc_queue ip_messages; // Nachrichtenwarteschlange
	union {
               struct ipc_space *receiver;
               struct ipc_port *destination;
               ipc_port_timestamp_t timestamp;
    }data;
	union {
    	ipc_importance_task_t imp_task;
    	ipc_kobject_t kobject; // Kernel-Objekt zum Port
    	uintptr_t alias;
	}kdata;
	...
} __attribute__((__packed__));

Dabei gibt es ein Kernel-Objekt, das mit dem Port verknüpft ist. Welchem Kernel-Objekttyp dieses ipc_port entspricht, wird durch die Eigenschaften von ipc_object bestimmt. Wir konstruieren also gezielt das ipc_object.

fakeport->io_bits = IO_BITS_ACTIVE | IKOT_CLOCK; // Als IKOT_CLOCK-Objekt und aktiv setzen
fakeport->io_lock_data[12] = 0x11;	// Port-Sperre als aktiv setzen, um Deadlock zu vermeiden

Der Kernel betrachtet dieses ipc_port dann als Port für die Kommunikation mit einem IKOT_CLOCK-Objekt. Das nächste Ziel ist es, die Kernel-Basisadresse zu leaken:

Tool herunterladen