sup

small tool for privilege escalation
git clone https://git.parazyd.org/sup
Log | Files | Refs | README | LICENSE

commit cec5f362b273867f23126ccb7a756905c640c856
parent 96b227c7bf3acc15c0de695b074f2b32fcabe21e
Author: parazyd <parazyd@dyne.org>
Date:   Tue,  2 Jul 2019 13:00:38 +0200

Rewrite C code and add SHA256 support.

Diffstat:
Mconfig.def.h | 33++++++++++++++++++++-------------
Msup.c | 158+++++++++++++++++++++++++++++++++++++++++--------------------------------------
2 files changed, 103 insertions(+), 88 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -1,20 +1,27 @@ -#define USER 1000 -#define GROUP -1 +/* See LICENSE file for copyright and license details. */ +/* User and group to run as */ #define SETUID 0 #define SETGID 0 -#define CHROOT "" -#define CHRDIR "" +/* sup authorizations + * + * The format is as follows: + * - UID allowed to run the command (-1 means anyone) + * - Alias/command used to call the executable + * - Path to the executable to run + * - SHA256 checksum of the executable + */ +static struct rule_t rules[] = { + { 1000, "ls", "/bin/ls", + "87e8fd7d813c135875aca43a4da43d3ced41d325ed2931906444471b4e93e017" }, -#define ENFORCE 1 + { -1, "grep", "/bin/grep", + "fedeb344d1be24f4a340591cd25ed81f7e46ea12772f563c9c9f43773028e23a" }, -static struct rule_t rules[] = { - { USER, GROUP, "whoami", "/usr/bin/whoami" }, - { USER, GROUP, "ifconfig", "/sbin/ifconfig" }, - { USER, GROUP, "ls", "/bin/ls" }, - { USER, GROUP, "wifi", "/root/wifi.sh" }, - { USER, GROUP, "cp", "*"}, // allow to run this program in PATH - { USER, GROUP, "*", "*"}, // allow to run any program in PATH - { 0 }, + { 1000, "tar", "/bin/tar", + "fedeb344d1be24f4a340591cd25ed81f7e46ea12772f563c9c9f43773028e23a" }, + + { 1001, "test", "./a.out", + "254260b676a44f1529f7c855f0126a57a3fbd7ec8a74de08835f08e8e6ed21be" }, }; diff --git a/sup.c b/sup.c @@ -1,103 +1,111 @@ -/* pancake <nopcode.org> -- Copyleft 2009-2011 */ +/* See LICENSE file for copyright and license details. */ -#include <errno.h> -#include <unistd.h> #include <stdio.h> -#include <string.h> #include <stdlib.h> +#include <string.h> #include <sys/stat.h> +#include <unistd.h> + +#include "arg.h" +#include "sha256.h" -#define HELP "sup [-hlv] [cmd ..]" -#define VERSION "sup 0.1 pancake <nopcode.org> copyleft 2011" +#define nelem(x) (sizeof (x) / sizeof *(x)) struct rule_t { - int uid; - int gid; + const int uid; const char *cmd; const char *path; + const char *hash; }; #include "config.h" -static int die(int ret, const char *org, const char *str) { - fprintf (stderr, "%s%s%s\n", org?org:"", org?": ":"", str); - return ret; +char *argv0; + +void die(char *msg) { + fprintf(stderr, "%s\n", msg); + exit(1); } -static char *getpath(const char *str) { - struct stat st; - static char file[4096]; - char *p, *path = getenv ("PATH"); - if (path) - for (p = path; *p; p++) { - if (*p==':' && (p>path&&*(p-1)!='\\')) { - *p = 0; - snprintf (file, sizeof (file)-1, "%s/%s", path, str); - if (!lstat (file, &st)) - return file; - *p = ':'; - path = p+1; - } +#define CHUNK 1048576 /* 1MiB */ +static uint32 getsha(const char *path, unsigned char *dest) { + static sha256_context sha; + + unsigned char buf[CHUNK]; + uint32 len, tot = 0; + FILE *fd; + + fd = fopen(path, "r"); + if (!fd) + die("Can not read binary file."); + + sha256_starts(&sha); + + while ((len = fread(buf, 1, CHUNK, fd))) { + sha256_update(&sha, buf, len); + tot += len; } - return NULL; + fclose(fd); + + sha256_finish(&sha, dest); + return tot; } -int main(int argc, char **argv) { - const char *cmd; - int i, uid, gid, ret; +int main(int argc, char *argv[]) { + unsigned int c, i, lflag = 0; + unsigned char digest[32]; + char output[65]; + struct stat st; - if (argc < 2 || !strcmp (argv[1], "-h")) - return die (1, NULL, HELP); + ARGBEGIN { + case 'l': + lflag = 1; + break; + default: + die("Usage: sup [-l] command [ args ... ]"); + } ARGEND; - if (!strcmp (argv[1], "-v")) - return die (1, NULL, VERSION); + if (lflag) { + printf("List of compiled authorizations:\n"); + for (i = 0; i < nelem(rules); i++) + printf("\nuser: %d\ncmd: %s\nbinary: %s\nsha256: %s\n", + rules[i].uid, rules[i].cmd, rules[i].path, rules[i].hash); - if (!strcmp (argv[1], "-l")) { - for (i = 0; rules[i].cmd != NULL; i++) - printf ("%d %d %10s %s\n", rules[i].uid, rules[i].gid, - rules[i].cmd, rules[i].path); return 0; } - uid = getuid (); - gid = getgid (); - - for (i = 0; rules[i].cmd != NULL; i++) { - if (*rules[i].cmd=='*' || !strcmp (argv[1], rules[i].cmd)) { -#if ENFORCE - struct stat st; - if (*rules[i].path=='*') { - if (*argv[1]=='.' || *argv[1]=='/') - cmd = argv[1]; - else if (!(cmd = getpath (argv[1]))) - return die (1, "execv", "cannot find program"); - } else cmd = rules[i].path; - if (lstat (cmd, &st) == -1) - return die (1, "lstat", "cannot stat program"); - if (st.st_mode & 0222) - return die (1, "stat", "cannot run writable binaries."); -#endif - if (uid != SETUID && rules[i].uid != -1 && rules[i].uid != uid) - return die (1, "urule", "user does not match"); - - if (gid != SETGID && rules[i].gid != -1 && rules[i].gid != gid) - return die (1, "grule", "group id does not match"); - - if (setuid (SETUID) == -1 || setgid (SETGID) == -1 || - seteuid (SETUID) == -1 || setegid (SETGID) == -1) - return die (1, "set[e][ug]id", strerror (errno)); -#ifdef CHROOT - if (*CHROOT) - if (chdir (CHROOT) == -1 || chroot (".") == -1) - return die (1, "chroot", strerror (errno)); - if (*CHRDIR) - if (chdir (CHRDIR) == -1) - return die (1, "chdir", strerror (errno)); -#endif - ret = execv (cmd, argv+1); - return die (ret, "execv", strerror (errno)); + if (argc < 1) + die("Usage: sup [-l] command [ args ... ]"); + + for (i = 0; i < nelem(rules); i++) { + if (!strcmp(argv[0], rules[i].cmd)) { + + if (rules[i].uid != getuid() && rules[i].uid != -1) + die("Unauthorized user."); + + if (stat(rules[i].path, &st) == -1) + die("Can not stat program."); + + if (st.st_mode & 0022) + die("Can not run binaries others can write."); + + if (getsha(rules[i].path, digest) != st.st_size) + die("Binary file differs from size read."); + + for (c = 0; c < 32; c++) + sprintf(output + (c*2), "%02x", digest[c]); + output[64] = '\0'; + + if (strncmp(rules[i].hash, output, 64)) + die("Checksums do not match."); + + if (setgid(SETGID) < 0) die("setgid failed"); + if (setuid(SETUID) < 0) die("setuid failed"); + + if (execv(rules[i].path, argv) < 0) + die("execv failed."); } } - return die (1, NULL, "Sorry"); + die("Unauthorized command."); }