Super User Do

1. Multi-user privilege escalation tool

This software aims to be a UNIX tool for generic secure usage when in need of privilege escalation. It is designed to run SUID, with "super-user powers" to execute things as root on the system it is installed. As such, it is designed for security, leveraging all possible measures to avoid vulnerabilities, including the reduction of complexity in its own source-code.

The purpose of sud is to execute commands as root (super-user) or as other users that are configured on the system. For the configuration of authorized users it relies on their belonging to groups wheel, sud or sudo, for info see vigr(8) or setuid(2).

For more general instructions, start from the homepage of SUD.

2. Code structure overview

The only source file is sud.c.

The overall structure of sud.c is simple:

{sud.c 2}
{Header files of system dependencies, 3}
{Macros and exit codes, 4}
{The main program, 5}

3. Headers

We want to have as less requirements as possible, so this list should be kept short and eventually include #ifdef directives for specific platform targets.

{Header files of system dependencies 3}
#define _DEFAULT_SOURCE 1
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// lstat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
// getpwnam
#include <pwd.h>
// getgrouplist
#include <grp.h>
// local parg module
#include "src/parg.h"
// release stamp
#ifdef RELEASE
#include "stamp.h"
#endif

Used in section 2

4. Preprocessor directives

{Macros and exit codes 4}
#define VERSION "1.0.0"
#define OK 0                  // status code for successful run
#define usage_error 1         // status code for command not found

#define ERR(fstr,...) { fprintf(stderr,fstr, ##__VA_ARGS__); fputc('\n',stderr); }
#define XXX(fstr,...) { fprintf(stderr,fstr, ##__VA_ARGS__); fputc('\n',stderr); }
#define ACT(fstr,...) { fprintf(stdout,fstr, ##__VA_ARGS__); fputc('\n',stdout); }

// maximum length of a command string
#define MAXCMD 512
#define PATH_MAX 1024

Used in section 2

5. Main

Now we come to the general layout of the main() function.

{The main program 5}
int main(int argc, char **argv)
{
	{Declare variables, 5}
    {Parse command-line options, 5}
    {Verify privileged access, 5}
    {Execute the command, 5}
    {Print any errors, 5}
}

Used in section 2

{Declare variables 5}
     // full path to command
    char fullcmd[PATH_MAX] = {0x0};

     // verify target command executable
    struct stat st;

     // recognize current user
    int uid;
    struct passwd *pw=0x0;

     // target privilege
    int target_uid=0;

     // cycling through groups to verify authorization
    int ngroups = 0;
    struct group* gr;

     // authorization flag
     register short authorized = 0;

Commandline option parsing is made using the excellent parg library by Jørgen Ibsen also released in public domain

{Parse command-line options 5}
     int c, optind;
     struct parg_state ps;
     parg_init(&ps);
     while ((c = parg_getopt(&ps, argc, argv, "hvu:")) != -1) {
	     if(fullcmd[0]) break;
	     switch (c) {

	     case 1: // stop to parse options, save the command and parse its arguments
	     if(!fullcmd[0]) {
		     struct stat tst;
		     char file[PATH_MAX];
		     char *p, *path = getenv ("PATH");
		     if (path) // Check if command is found in $PATH
			     for (p = path; *p; p++) {
				     if (*p==':' && (p>path&&*(p-1)!='\\')) {
					     *p = 0;
					     snprintf (file, sizeof (file)-1, "%s/%s", path, ps.optarg);
					     if (!lstat (file, &tst)) {
						     // command found
						     snprintf(fullcmd,PATH_MAX,"%s",file);
						     optind = ps.optind-1;
					         break;
					     }
					     *p = ':';
					     path = p+1;
				     }
			     }
	     }
	     break;
	     case 'h':
	     help:
		     ACT("Usage: %s [-h] [-v] [-u USER] COMMAND",argv[0]);
		     return OK;
		     break;
	     case 'v':
		     ACT("Sud version %s", VERSION);
#ifdef RELEASE
		     ACT("%s (sha512)",SHA512_SUD_C);
		     ACT("built on %s",BUILD_TIME);
#endif
		     return OK;
		     break;
	     case '?':
		     ERR("unknown option -%c", ps.optopt);
		     return usage_error;
		     break;
	     case 'u':
		     {
			     struct passwd *puid;
			     errno = 0;
			     puid = getpwnam(ps.optarg);
			     if(!puid && errno) ERR("error in %s: getpwnam",__func__);
			     if(puid) target_uid = puid->pw_uid;
		     }
			     break;
	     default:
		     ERR("error: unhandled option -%c", c);
		     return usage_error;
		     break;
	     }
     }
     // for (c = ps.optind; c < argc; ++c) {
	 //     ERR("leftover '%s'", argv[c]);
     // }
     if(argc==1) goto help;
     // XXX("optind: %i",ps.optind);
{Verify privileged access 5}

    // get the called UID and GID
    uid = getuid ();

    // get the username string from /etc/passwd
    pw = getpwuid( uid );
    if(!pw) {
	    ERR("uid not found in passwd: %s",strerror(errno));
	    return usage_error;
    }

    // one could maintain a log of calls here
    // XXX("sud %s called by %s(%d) gid(%d)\n",
    //     fullcmd, pw?pw->pw_name:"", uid, gid);

    // command does not exist as binary on the filesystem
       if (lstat (fullcmd, &st) == -1) {
	    ERR("cannot stat command: %s", fullcmd);
	    return usage_error;
    }
    if (st.st_mode & 0022) {
	    // command has wrong permissions (writable to others)
	    ERR("cannot run a binary others can write: %s", fullcmd);
	    return usage_error;
    }

    // get number of groups first
    getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups);
    gid_t groups[ngroups];
    // get the list of groups
    getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
    //example to print the groups name
    for (int i = 0; i < ngroups; i++){
	    gr = getgrgid(groups[i]);
	    if(!gr) {
		    ERR("getgrgid error: %s",strerror(errno));
		    return usage_error;
	    }
	    if(strncmp(gr->gr_name,"wheel",5)==0) authorized = 1;
	    if(strncmp(gr->gr_name,"sudo",4)==0) authorized = 1;
	    if(strncmp(gr->gr_name,"sud",3)==0) authorized = 1;
    }
    if(!authorized) {
	    ERR("user not authorized: %s",pw->pw_name);
	    return usage_error;
    }
{Execute the command 5}


       // privilege escalation
       if (setuid (target_uid) <0) {
	       ERR("setuid: %s",strerror(errno));
	       return usage_error;
       }
       if (seteuid (target_uid) <0) {
	       ERR("seteuid: %s",strerror(errno));
	       return usage_error;
       }

    // turn current process into the execution of command
    // XXX("execve: %s",fullcmd);
    execve(fullcmd, &argv[optind], NULL); //&argv[ps.optind], NULL);
{Print any errors 5}
    // execv returns only on errors
    ERR("execv: %s", strerror(errno));