Child pages
  • Guide to API Privilege Escalation - Legacy Method
Skip to end of metadata
Go to start of metadata

 

Introduction

Warning:

Poor implementations of this system may cause root-privilege vulnerabilities and compromised servers. Read the Security requirements section of this document before you use this system.

In cPanel & WHM version 11.36 and earlier, you must write the scripts to allow functions to run with elevated privileges. These scripts must  directly manipulate the data that passes through the  setuid  wrapper.

Notes:

  • Never use this any of these example files on a production system.
  • For information about privilege escalation in cPanel & WHM version 11.38 and later, read our API Privilege Escalation documentation.
  • To query all of the SETUID and SETGID values on a server, run the following command:

    find / -type f \( -perm -04000 -o -perm -02000 \) \-exec ls -lg {} \;

Security requirements

Whenever you use this system, do not manipulate files or directories that a user owns as the root user or execute any actions on unvalidated input.

Warning:

You must adhere to the following security practices:

  • Only use this system to execute code that must run as the root user.
  • Thoroughly validate any input that passes through this system.
  • Sanitize the admin script's environment.

To sanitize the admin script's environment, set environment variables to limit the paths from which the script loads libraries. 

  • This action sanitizes the @INC array and ensures that users cannot load arbitrary libraries.
  • The following example adds the /usr/local/cpanel/ directory to the environment, and then removes entries that do not match standard Perl library paths:

    BEGIN {
        unshift @INC, '/usr/local/cpanel';
        @INC = grep( !/(^\.|\.\.|\/\.+)/, @INC );
    }

Files

Privilege escalation systems in cPanel & WHM version 11.36 and earlier require the following key components: 

The admin binary and wrapper scripts follow a strict naming convention.

  • You must prefix the admin and wrap files with a unique identifier.
  • For example, if you create the admin binary and the example wrapper script, name the files exampleadmin and examplewrap, respectively.

The  Cpanel::AdminBin module

The Cpanel::AdminBin Perl module (/usr/local/cpanel/Cpanel/AdminBin.pm) passes information between the wrapper scripts and the system processes that run the code.

Notes:

  • This document's examples focus on two functions. The /usr/local/cpanel/Cpanel/AdminBin.pm file contains additional functions to use.
  • The example file for this method provides examples of how to use this code in the Test.pm file.

Click a tab to view information about a function:

The adminrun() function returns strings from the admin binary.

To call this function, use the following format:

adminrun($name, $cmd, @args )

This example uses the following variables:

  • $name — The admin script's filename's identifier prefix.
  • $cmd — The command to pass to the wrapper.
  • $args — A list of command arguments.

For example, to create the file wrapper script and a READ function that accepts path arguments via the adminrun() function, create a function that is similar to the following code:

my $file_contents = Cpanel::AdminBin::adminrun('file', 'READ', $OPTS{'path'});
  • This function executes the /usr/local/cpanel/bin/filewrap binary as setuid root and writes READ $path to it through STDIN
  • The filewrap binary executes the /usr/local/cpanel/bin/fileadmin binary and writes $uid READ $path to STDIN.

The adminfetchnocache() function returns data structures from the admin script.

To call this function, use the following format:

adminfetchnocache($name, $cachefile, $cmd, $format, @args)

This example uses the following variables:

  • $name — The admin script's filename's identifier prefix.
  • $cachefile — The cache file location. Because cPanel & WHM uses a nocache version in this method, pass '' to this variable.
  • $cmd — The command to pass to the wrapper.
  • $format — The format to use to communicate between the admin script and the code to call. We strongly recommend that you pass storage to this variable.
  • $args — A list of command arguments.

 For example, to implement the ability to retrieve a data structure that represents a directory as the root user via the adminfetchnocache() function, create a function that is similar to the following code:

my $dir_listing_hr = Cpanel::AdminBin::adminfetchnocache('file', '', 'LS', '', $opts{'path'});

The admin script

The admin script contains the code that runs as the root user. The script accepts data from STDIN and prints data to STDOUT, after which the wrapper binary executes the data.

  • You must store this script in the /usr/local/cpanel/bin/ directory with executable permissions.
  • You must prefix the admin script's filename with a unique identifier.

Note:

The example file for this method provides examples of how to use this code in the testadmin file.

When the script writes data to STDIN, use the following format:

$uid $cmd $args

This example uses the following variables:

  • $uid — The authenticated user's ID. The admin binary automatically prefixes this value.
  • $cmd — The command to execute.
  • $args — A list of command arguments.

Click on a tab to view version-specific information and examples:

my %commands = (
	'LS' => sub {
	    # pull in the values that were in @args 	    my ($dir) = @_;
        # Sanitize our input         if ( !defined $dir || $dir eq '' ) {
            print "Directory not defined\n";
            exit;
        }

        if ( !-d $dir ) {
            print "provided directory does not exist\n";
            exit;
        }
        my @files;
        # perform the action         opendir( my $dir_dh, $dir ) || die "Can't open directory: $dir";
        #build our data structure         foreach my $file ( readdir $dir_dh ) {
            push @files, $file;
        }
        closedir($dir_dh) || die "Can't close directory: $dir";
        # print out in YAML format         print Cpanel::YAML::Dump(\@files);
	},
  • Line 19 prints the @files data structure to STDOUT in the YAML format. 
    • This passes a data structure back to the subroutine that initiated the call. 
    • Call this file via the adminfetch() or adminfetchnocache() functions.
my %commands = (
	'LS' => sub {
	    # pull in the values that were in @args 	    my ($dir) = @_;
        # Sanitize our input         if ( !defined $dir || $dir eq '' ) {
            print "Directory not defined\n";
            exit;
        }

        if ( !-d $dir ) {
            print "provided directory does not exist\n";
            exit;
        }
        my @files;
        # perform the action         opendir( my $dir_dh, $dir ) || die "Can't open directory: $dir";
        #build our data structure         foreach my $file ( readdir $dir_dh ) {
            push @files, $file;
        }
        closedir($dir_dh) || die "Can't close directory: $dir";
        # print out in Storable format         Storable::nstore_fd( \@files, \*STDOUT );
	},
  • Line 19 prints a Storable file to STDOUT
    • This passes a data structure back to the subroutine that initiated the call. 
    • Call this file via the adminfetch() or adminfetchnocache() functions.


The wrapper binary

Build the wrapper binary from the source code in the /usr/local/cpanel/src/wrap/wrap.c file. It is a setuid binary, which you store in the /usr/local/cpanel/bin/ directory.

This binary handles the following tasks:

  • The user's privilege escalation.
  • The parent process that executes the binary is a cPanel process.
  • The flow of data between the admin script and the API call process.

You can modify and distribute the code in the /usr/local/cpanel/src/wrap/wrap.c file for use with cPanel & WHM systems if you meet the following conditions:

  • You may only distribute the code for non-commercial use.

    Note:

    To distribute the code for commercial use, you must obtain written approval from cPanel, Inc. Contact copyright@cpanel.net for details.

  • You must make an uncompiled version of this code publicly available.
  • You must ensure that users understand that cPanel, Inc does not provide support for modified versions of this code.
  • You must ensure that all copyright notices are intact in the modified version.
  • You must list the contact information for the modified version's maintainer within the source code.

When you modify this binary, you must modify the exec_list array to point to the correct admin script and use a unique identifier.

For example, the unmodified binary includes the following code:

struct executable_properties exec_list [1] = {
        { "testwrap",      "/usr/local/cpanel/bin/testadmin",      "root", "wheel", 1, 0 }
    };

After you make the appropriate changes, the code should resemble the following example:

struct executable_properties exec_list [1] = {
        { "myappwrap",      "/usr/local/cpanel/bin/myappadmin",      "root", "wheel", 1, 0 }
    };

Note:

You must also change the PROG_NAME variable in the Makefile file.