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.
- Download an example
.tar.gz
file.
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 Escalationdocumentation.
Security requirements
- MultiExcerpt named '
- ' was not found
Files
Privilege escalation systems in cPanel & WHM version 11.36 and earlier require the following key components:
- The
Cpanel::AdminBin
module - The
admin
script - A wrapper binary
The admin
binary and wrapper scripts follow a strict naming convention.
- You must prefix the
admin
andwrap
files with a unique identifier. - For example, if you create the
admin
binary and theexample
wrapper script, name the filesexampleadmin
andexamplewrap
, 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:
Adminrun()
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
— Theadmin
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 assetuid root
and writesREAD $path
to it throughSTDIN
. - The
filewrap
binary executes the/usr/local/cpanel/bin/fileadmin
binary and writes$uid READ $path
toSTDIN
.
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. Theadmin
binary automatically prefixes this value.$cmd
— The command to execute.$args
— A list of command arguments.
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
— Theadmin
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 assetuid root
and writesREAD $path
to it throughSTDIN
. - The
filewrap
binary executes the/usr/local/cpanel/bin/fileadmin
binary and writes$uid READ $path
toSTDIN
.
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. Theadmin
binary automatically prefixes this value.$cmd
— The command to execute.$args
— A list of command arguments.
cPanel & WHM version 11.36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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 toSTDOUT
in the YAML format.- This passes a data structure back to the subroutine that initiated the call.
- Call this file via the
adminfetch()
oradminfetchnocache()
functions.
cPanel & WHM version 11.34 and earlier
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 toSTDOUT
.- This passes a data structure back to the subroutine that initiated the call.
- Call this file via the
adminfetch()
oradminfetchnocache()
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:
1
2
3
|
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:
1
2
3
|
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.