Automated cPanel Remote Backup without remote credentials

cPanel has a “full backup” feature that can store the backup on a remote FTP server or can use Secure Copy (SCP) to push the files to a remote location. Normally you would want to automate this process via a cron job using a script or a php file. A full description can be found here and I am not going to cover this use case: Howto: Automatic cPanel Backups

cPanel backup with remote credentials

My concern was that the backup script will contain credentials to the cpanel and the remote connection (FTP or SCP) in plan text. Besides the credentials being exposed on the remote server you need to run a FTP server that increases the risk of compromise (outdated package, misconfiguration, you name it).

code snippet from the backup script

$auth = base64_encode("username:password");
$domain = "https://example.com:2083";
$theme = "paper_lantern";
$ftp = false;
$ftp_server = "fortress.com";
$ftp_username = "strong_user";
$ftp_password = "super_secret";
$ftp_port = "21";
$ftp_directory = "/";

cPanel backup with without remote credentials

If you want to avoid exposing your remote server to vulnerabilities and potentially leaking credentials in transit (ie FTP sends the password in clear text) there is a nice trick I have implemented.

The idea is to generate the backup file on the remote server that will be stored in the root folder. We don’t let cPanel push the backup remotely, but we’ll fetch the file directly from the remote server.

In this instance the backup script resides on the remote server (our fortress) and holds just the credentials to the cPanel instance. You can even obfuscate the password in different ways to make it even harder for anyone to retrieve the password if they manage to get gain access to the content of the file itself.

$pass = exec("id -nu | md5sum | awk -F- '{ print \$1 }'");

Generating and downloading the backup

  1. To generate the backup we’ll need to hit the /backup/dofullbackup.html first. This will start generating the full backup in the background.
  2. In the second stage the script will check for all available backups /backup/fullbackup.html generated previously and will try to get the file /download?file=.
  3. The final stage is to delete the old backup from the remote server using the json api endpoint /json-api/cpanel
<?php

$login  = 'cpanel_user';
$pass   = exec("id -nu | md5sum | awk -F- '{ print \$1 }'");
$auth   = base64_encode("$login:$pass");
$domain = "https://example.com:2083";
$theme  = "paper_lantern";

// Do not change below 
$url     = $domain . "/frontend/" . $theme . "/backup/dofullbackup.html";
$data    = array();
$options = array(
    'http' => array(
        'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Basic $auth\r\n",
        'method' => 'POST',
        'content' => http_build_query($data)
    ),
    'ssl' => array(
        'verify_peer' => false,
        'verify_peer_name' => false,
        'allow_self_signed' => true
    )
);

# execute backup generation
$context = stream_context_create($options);
$result  = file_get_contents($url, false, $context);

$url     = $domain . "/frontend/" . $theme . "/backup/fullbackup.html";
$options = array(
    'http' => array(
        'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Basic $auth\r\n",
        'method' => 'GET'
    ),
    'ssl' => array(
        'verify_peer' => false,
        'verify_peer_name' => false,
        'allow_self_signed' => true
    )
);

$context = stream_context_create($options);
$result  = file_get_contents($url, false, $context);

# get downloadable files
preg_match_all("|/download\?file=(.*?)\"|", $result, $matches);
foreach ($matches[1] as $match) {
    $auth = $login . ':' . $pass;
    
    // due to memory contrains we will do the download with curl
    $url = $domain . "/download?file=" . $match;
    exec("curl -s -u " . escapeshellarg($auth) . ' ' . escapeshellarg($url) . " -o " . escapeshellarg("/home/backup/hosting_backup/" . $match));
    
    // now delete them
    $url = $domain . "/json-api/cpanel";
    exec("curl -s -u " . escapeshellarg($auth) . ' ' . escapeshellarg($url) . " --data " . escapeshellarg("cpanel_jsonapi_module=Fileman&amp;cpanel_jsonapi_func=fileop&amp;cpanel_jsonapi_apiversion=2&amp;filelist=1&amp;multiform=1&amp;doubledecode=0&amp;op=unlink&amp;metadata=[object Object]&amp;sourcefiles=%2fhome%2fpmcnet%2f" . $match));
}

if ($result === FALSE) {
    exit("Error backing up server.");
}

You can download the source code by visiting the GitHub repo.

References

Howto: Automatic cPanel Backups
cPanel Auto Backup Script Generator
Howto: Automatic cPanel Backups
cPanel Automated Backup Script
How do I Generate a cPanel Backup using an Automated Script?
cPanel Automated Backup Script Using PHP, Cron Jobs and FTP

Csaba is passionate about Cyber Security, Pentesting and just making things work.

Leave a reply:

Your email address will not be published.

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Site Footer

Sliding Sidebar