Apache-related LF_* options not working

This forum is only for reproducible bugs with csf and lfd (i.e. not iptables problems, lack of understanding how to use a feature, etc). Posts must be accompanied with full technical details of the problem and how it can be recreated. Any posts not adhering to this, or not considered bugs, will be moved to the General Discussion (csf) forum.
Post Reply
MH-Stefan
Junior Member
Posts: 5
Joined: 06 Mar 2024, 12:22

Apache-related LF_* options not working

Post by MH-Stefan »

We've noticed that some Apache-related LF_* rules (LF_APACHE_404 and LF_MODSEC specifically) no longer seem to get triggered. I've simulated multiple 404 errors and triggered random ModSecurity rules, but CSF didn't pick up any of the events, and my non-whitelisted IP address didn't get blocked at all.

In the /usr/local/csf/lib/ConfigServer/RegexMain.pm file, I see that all Apache-related regex rules, except for the LF_HTACCESS regex rule, are looking just for the "client" tag while on current cPanel servers with Apache, the IP address is often logged in the "remote" tag. For example:

Code: Select all

[Wed Oct 23 13:02:14.726330 2024] [core:info] [pid 2707490:tid 2707492] [remote 12.34.56.78:17219] AH00128: File does not exist: /home/user/public_html/test123
Take note of the "remote" tag. CSF is looking just for the "client" tag, which in this example is not even present.

The regex for LF_APACHE_404 is also looking for the wrong log level. Such errors are logged with the "info" level and not the "error" level.

Our solution was to correct the respective REGEX rules and build custom rules in regex.custom.pm, as follows:

Code: Select all

# BEGIN - Custom REGEX Rules
# mod_security v2 (Apache)
if (($config{LF_MODSEC}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied/)) {
	my $ip = $5;
	my $domain = "";
	if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	my $ruleid = "unknown";
	if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
	if (checkip(\$ip)) {return ("mod_security (id:$ruleid) triggered by",$ip,"mod_security2",$config{LF_MODSEC},"80,443",$config{LF_MODSEC_PERM},"1");} else {return}
}

# Apache 404 errors
if (($config{LF_APACHE_404}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?info\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?File does not exist\:/)) {
	my $ip = $5;
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	my $ruleid = "unknown";
	if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
	if (checkip(\$ip)) {return ("Excessive amount of 404 errors triggered by",$ip,"apache_404",$config{LF_APACHE_404},"80,443",$config{LF_APACHE_404_PERM},"1")} else {return}
}
# END - Custom REGEX Rules
I've done only some brief testing, so please use the above rules with caution. I don't recommend using these in production without proper testing.

It would be great if the ConfigServer team would review the regex rules and adapt them to the current Apache logs. This issue was noticed on an up-to-date CloudLinux 8 server with cPanel v124 and Apache 2.
MH-Stefan
Junior Member
Posts: 5
Joined: 06 Mar 2024, 12:22

Re: Apache-related LF_* options not working

Post by MH-Stefan »

It seems like LF_APACHE_401 and LF_APACHE_403 don't seem to always work either, so I've built custom rules for all Apache-related LF_* options:

Code: Select all

# BEGIN - Custom REGEX Rules
# mod_security v2 (Apache)
if (($config{LF_MODSEC}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied/)) {
	my $ip = $5;
	my $domain = "";
	if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	my $ruleid = "unknown";
	if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
	if (checkip(\$ip)) {return ("mod_security (id:$ruleid) triggered by",$ip,"mod_security2",$config{LF_MODSEC},"80,443",$config{LF_MODSEC_PERM},"1");} else {return}
}

# CXS Apache
if (($config{LF_CXS}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied with code \d\d\d \(phase 2\)\. File \"[^\"]*\" rejected by the approver script \"\/etc\/cxs\/cxscgi\.sh\"/)) {
	my $ip = $5;
	my $acc = "";
	my $domain = "";
	if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	if (checkip(\$ip)) {return ("cxs mod_security triggered by","$ip|$acc|$domain","cxs",$config{LF_CXS},"80,443",$config{LF_CXS_PERM},"1")} else {return}
}

# CXS Litespeed
if (($config{LF_CXS}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\]( \[client \S+\])? (\w+: )?ModSecurity:(( \[[^]]+\])*)? Access denied with code \d\d\d, \[Rule: 'FILES_TMPNAMES' '\@inspectFile \/etc\/cxs\/cxscgi\.sh'\] \[id "1010101"\]/)) {
	my $ip = $5;
	my $acc = "";
	my $domain = "";
	if ($line =~ /\] \[hostname "([^\"]+)"\] \[/) {$domain = $1}
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	if (checkip(\$ip)) {return ("cxs mod_security triggered by","$ip|$acc|$domain","cxs",$config{LF_CXS},"80,443",$config{LF_CXS_PERM},"1")} else {return}
}

# mod_qos
if (($config{LF_QOS}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?mod_qos\(\d+\): access denied,/)) {
	my $ip = $5;
	my $acc = "";
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	if (checkip(\$ip)) {return ("mod_qos triggered by","$ip|$acc","mod_qos",$config{LF_QOS},"80,443",$config{LF_QOS_PERM},"1")} else {return}
}

# Apache symlink race condition
if (($config{LF_SYMLINK}) and ($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?Caught race condition abuser/)) {
	my $ip = $5;
	my $acc = "";
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	if ($line !~ /\/cgi-sys\/suspendedpage\.cgi$/) {
		if (checkip(\$ip)) {return ("symlink race condition triggered by","$ip|$acc","symlink",$config{LF_SYMLINK},"80,443",$config{LF_SYMLINK_PERM},"1")} else {return}
	}
}

# Apache 401 errors
if (($config{LF_APACHE_401}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?(user  not found|user \w+ not found|user \w+: authentication failure for "\/\w+\/")\:/)) {
	my $ip = $5;
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	my $ruleid = "unknown";
	if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
	if (checkip(\$ip)) {return ("Excessive amount of 401 errors triggered by",$ip,"apache_401",$config{LF_APACHE_401},"80,443",$config{LF_APACHE_401_PERM},"1")} else {return}
}

# Apache 403 errors
if (($config{LF_APACHE_403}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?error\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?client denied by server configuration\:/)) {
	my $ip = $5;
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	my $ruleid = "unknown";
	if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
	if (checkip(\$ip)) {return ("Excessive amount of 403 errors triggered by",$ip,"apache_403",$config{LF_APACHE_403},"80,443",$config{LF_APACHE_403_PERM},"1")} else {return}
}

# Apache 404 errors
if (($config{LF_APACHE_404}) and ($globlogs{HTACCESS_LOG}{$lgfile}) and ($line =~ /^\[\S+\s+\S+\s+\S+\s+\S+\s+\S+\] \[(\S*:)?info\] (\[pid \d+(:tid \d+)?\] )?\[(client|remote) (\S+)\] (\w+: )?File does not exist\:/)) {
	my $ip = $5;
	$ip =~ s/^::ffff://;
	if ($config{LF_APACHE_ERRPORT} == 2 and $ip =~ /(.*):\d+$/) {$ip = $1}
	my $ruleid = "unknown";
	if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
	if (checkip(\$ip)) {return ("Excessive amount of 404 errors triggered by",$ip,"apache_404",$config{LF_APACHE_404},"80,443",$config{LF_APACHE_404_PERM},"1")} else {return}
}
# END - Custom REGEX Rules
Please use with caution. I don't recommend using these in production without prior testing.
Post Reply