Development Guides Home >> Guide to API Privilege Escalation
Guide to API Privilege Escalation - Application Files
The application file contains the functions that you wish for the system to escalate. The application file performs the following steps:
- Receives and sanitizes the data.
- Authorizes the action.
- Performs the requested action.
- Returns the result.
How to write the application
You can write your AdminBin
application in any programming language. However, in cPanel & WHM version 54 and newer, we recommend that you write the file as a Perl modulino that subclasses the Cpanel::AdminBin::Script::Class
module.
-
The
root
user must own the file. -
You
must
set the file to
0700
permissions (writable, readable, and executable by owner). -
You
must
shebang
the
AdminBin
application properly, or compile it. -
Store this file with the configuration file in a new namespace in the
/usr/local/cpanel/bin/admin/
directory.-
The namespace and the directory name that you create in the
/usr/local/cpanel/bin/admin/
must be identical. -
For example, you could create the
TheNameSpace
namespace and/usr/local/cpanel/bin/admin/TheNameSpace
directory.
-
The namespace and the directory name that you create in the
-
The configuration file should contain the following text:
mode=full
For more about the configuration file and its parameters, read the Configuration File documentation.
The Call method (54+)
- cPanel, L.L.C. introduced this functionality in cPanel & WHM version 54 for admin modules in Perl. We strongly recommend that you use this method.
-
This example file uses
full
mode, which theCpanel::AdminBin::Script::Call
module requires.
#!/usr/local/cpanel/3rdparty/bin/perl
# Package the module.
package MyNamespace::MyExample;
# Use the strict pragma. (Manners!)
use strict;
#Make this module inherit from this "groundwork" admin module.
#This eliminates a large swath of the boilerplate code that admin #modules used to require!
use parent qw( Cpanel::AdminBin::Script::Call );
# Use any additional modules.
use Cpanel::Logger ();
use Cpanel::PwCache ();
#Run the module as a script if (and only if) it is called that way.
#This "modulino" approach helps to facilitate testing and code
#reusability; for example, you could put some of this class's methods
#into a base class, then have 2 or more admin modules inherit
#from that base class.
__PACKAGE__->run() if !caller;
#This special function is a "whitelist" of actions that
#a caller may call. Anything not listed here cannot be called.
#
#By convention, these functions are named
#in ALLCAPS_SNAKE_CASE.
sub _actions {
return qw(
DEATH
ECHO
MIRROR
BOUNCY
HASHIFY
);
}
#If, for some reason, you want to enable an admin action for a demo account,
#then list that action here.
sub _demo_actions { }
#------------------------
#Each "action" is its own method.
#It receives the arguments in list form as they were passed
#in to Cpanel::AdminBin::Call::call().
#
#For example, you could call the ECHO action thusly:
#
#Cpanel::AdminBin::Call::call(
# 'MyNamespace',
# 'MyExample',
# 'ECHO',
# 'the string',
#);
#
#...and ECHO would receive 'the string' as the $string argument.
#-----------------------
sub ECHO {
my ($self, $string) = @_;
return $string;
}
sub MIRROR {
my ($self, $string) = @_;
return scalar reverse $string;
}
sub BOUNCY {
my ($self, $string) = @_;
return _bouncy($string);
}
sub HASHIFY {
my ($self, $string) = @_;
return { ourdata => $string };
}
sub DEATH {
die 'Arrgh!';
}
#This OPTIONAL function will be called before the function invocation.
#
#The $metadata_hr is a hash reference that contains, as of 11.54,
#a "wantarray" key that indicates the calling context: void, scalar, or list.
#(cf. perldoc wantarray)
#
#$args_ar is the array of arguments from the caller.
#
#NOTE: Both of the foregoing are "live" references. Take extra care
#not to modify them!
#
sub pre_execute_hook {
my ($self, $metadata_hr, $args_ar) = @_;
my $mod_name = (__PACKAGE__ =~ s<.*::><>r);
Cpanel::Logger->new()->warn(
sprintf(
"User %s called the module "%s" with the function "%s" and arguments [%s]",
$self->get_caller_username(),
$self->get_action(),
join( q< >, @$args_ar ),
)
);
return; #the actual return value(s) is/are ignored
}
sub _bouncy {
my $data_in = shift;
my $data_out = q{};
for my $i ( 0 .. length($data_in) - 1 ) {
if ($i % 2) {
$data_out .= substr( $data_in,$i,1);
}
else {
$data_out .= uc(substr( $data_in,$i,1));
}
}
return $data_out;
}
1;
The Standard method
The application file must sanitize its own inputs. The system does not sanitize them.
This example file uses simple
mode. However, we recommend that you use full
mode whenever possible.
#!/usr/local/cpanel/3rdparty/bin/perl
use strict;
use Cpanel::AdminBin::Serializer ();
use Cpanel::Logger ();
use Cpanel::PwCache ();
my $stdin = <STDIN>;
chomp $stdin;
my ($uid,$function,$data) = split (/ /,$stdin,3);
#
sanitize the input; in this case, only alphanumeric, underscore, space,
period, and exclamation are allowed $data =~ s/![\w \.\!]//g;
# make a note in the logs! my $user = (Cpanel::PwCache::getpwuid($uid))[0];
my $logger = Cpanel::Logger->new();
$logger -> warn("Myexample called by user $user with function: $function");
if ($function eq 'ECHO') {
print $data;
exit(0);
}
elsif ($function eq 'MIRROR') {
print scalar reverse($data);
exit(0);
}
elsif ($function eq 'BOUNCY') {
print _bouncy($data);
exit(0);
}
elsif ($function eq 'HASHIFY') {
print ".\n" . Cpanel::AdminBin::Serializer::Dump ({ 'ourdata' => $data} );
exit(0);
}
else {
print "Invalid function specified to MyExample adminbin function";
exit(1);
}
1;
sub _bouncy {
my $data_in = shift;
my $data_out = q{};
for my $i (0..length($data_in)-1) {
if ($i % 2) {
$data_out .= substr( $data_in,$i,1); }
else {
$data_out .= uc(substr( $data_in,$i,1)); }}
return $data_out;
}
Parameters
The parameter order that the system passes to the Cpanel::Wrap::send_cpwrapd_request
method is constant. However, some parameters' behavior depends on the configuration file's mode
value.
For more information, read our Configuration Files documentation.
Parameter | Type | Description | Possible values | Example |
---|---|---|---|---|
uid |
integer | Required The authenticated user's user ID. The system passes this value automatically. The configuration file's mode value determines this parameter's behavior. |
A valid system user ID. | 1000 |
function |
string | Required The application's pseudo-function. The configuration file's mode value determines this parameter's behavior. |
A valid string. | ECHO |
data |
scalar or data structure | Required The data to process. The configuration file's mode value determines this parameter's behavior. |
|
data |
action |
string | Required The output's behavior. This value defaults to run . If the AdminBin application module return starts with .\n , the system automatically switches the action to fetch . |
|
run |
env |
hash reference | Required A hash reference of keys and values to set in the environment before the AdminBin application executes. |
|
WHM50 |
module |
string | Required The application's filename. | The application file's filename. | Example |
namespace |
string | Required The application's namespace. | The application's namespace. This value is identical to the directory name. | NameSpace |
stream |
string | A filehandle to which the system streams the application's output. Only use this parameter if you set the action parameter to stream . |
A valid filehandle. | Filehandle |
version |
string | Always set this value to Cpanel::AdminBin::Serializer::VERSION . |
Cpanel::AdminBin::Serializer::VERSION |
Cpanel::AdminBin::Serializer::VERSION |