tRewrite C code and add SHA256 support. - sup - small tool for privilege escalation
 (HTM) git clone https://git.parazyd.org/sup
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit cec5f362b273867f23126ccb7a756905c640c856
 (DIR) parent 96b227c7bf3acc15c0de695b074f2b32fcabe21e
 (HTM) Author: parazyd <parazyd@dyne.org>
       Date:   Tue,  2 Jul 2019 13:00:38 +0200
       
       Rewrite C code and add SHA256 support.
       
       Diffstat:
         M config.def.h                        |      33 +++++++++++++++++++------------
         M sup.c                               |     158 ++++++++++++++++---------------
       
       2 files changed, 103 insertions(+), 88 deletions(-)
       ---
 (DIR) diff --git a/config.def.h b/config.def.h
       t@@ -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" },
        };
 (DIR) diff --git a/sup.c b/sup.c
       t@@ -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.");
        }