HTML Apache2 Web Server Security

admin@worldsworstwriter.org 2026-05-19

Here are five security issues I found on an Apache2 web server running on a Raspberry Pi Zero.

Syn Flood Attacks

Client sends network syn requests without completing the connection. These connections build up causing the web server to slown down or go offline. Here is an example [not the real source IP addresses].

user@pizero:~> ss -aO
Netid  State     Recv-Q   Send-Q    Local Address:Port                  Peer Address:Port
tcp    SYN-RECV  0        0     [::ffff:10.0.1.2]:https       [::ffff:192.168.33.131]:3765
tcp    SYN-RECV  0        0     [::ffff:10.0.1.2]:https       [::ffff:192.168.35.156]:7058
tcp    SYN-RECV  0        0     [::ffff:10.0.1.2]:https        [::ffff:192.168.33.52]:55053
tcp    SYN-RECV  0        0     [::ffff:10.0.1.2]:https        [::ffff:192.168.32.15]:64241
tcp    SYN-RECV  0        0     [::ffff:10.0.1.2]:https        [::ffff:192.168.34.18]:28359
tcp    SYN-RECV  0        0     [::ffff:10.0.1.2]:https       [::ffff:192.168.32.109]:32137

The connections are from different client addresses on a Class B network. The SYN-RECV state means the web server is waiting for the client to complete the network connection. This is a normal 3-way TCP/IP handshake:

1. [Client] SYN ⏵     [Web Server]
2. [Client] ⏴ SYN-ACK [Web Server]
3. [Client] ACK ⏵     [Web Server]

This is an incomplete 2-way TCP/IP handshake resulting in a SYN-RECV connection state. These connections time out after 30 seconds.

1. [Client] SYN ⏵     [Web Server]
2, [Client] ⏴ SYN-ACK [Web Server]

It seems unlikely that 80 machines on the same network are sending SYN packets [8 packets/second] at the same time and not completing the handshake. More likely, a single machine is sending SYN packets with forged source addresses and no intention of completing the handshake. This looks like a SYN flood attack.

Enabling SYN cookies has no effect. Installed UFW on the server and blocked these networks. Now it's whack-a-mole, blocking networks when they show up.

This is a UFW rule to block a Class B network sending spoofed syn packets. These are not the real source IP addresses. Currently blocking 26 class B networks.

user@pizero:~> sudo ufw show added
ufw deny from 192.168.0.0/16 comment 'SYN-RECV'
ufw allow 80
ufw allow 443

This script uses UFW to block Class A network addresses when the number of SYN-RECV connections is larger than 50.

#!/bin/bash
# List network connections in SYN-RECV state ip addresses and counts
# Use UFW to block class A networks with large number of SYN-RECV connections
# Example:  ss -a
# tcp  SYN-RECV  0  0 [::ffff:10.0.1.2]:http [::ffff:111.170.36.179]:8284
#
# List SYN-RECV network connections to web server
syniplist="$(ss  -n state  syn-recv | sed -e '1d' | awk '{print $5}' | sed 's/\[::ffff://' | sed 's/\./ /g' | awk '{print $1}' | sort | uniq)"
#echo "syniplist: ""$syniplist"
#
# Remove \n
iplist=`echo "$syniplist" | sed -z "s/\n/ /g"`
#
# For each unique address count the number of connections
for ipaddr in `echo "$iplist"`; do
  echo -n "ipaddr: ""$ipaddr"" "
  ipcount="$(ss -a | grep SYN-RECV | grep "$ipaddr" | wc -l)"
  echo "count: ""$ipcount"
  #
  # Block IP address with large number of SYN-RECV connections
  if [ "$ipcount" -gt 50 ]; then
    blockaddr=$(echo "$ipaddr"".0.0.0/8")
    echo "$blockaddr"" has ""$ipcount"" connections"
    if [ -n "$1" ] # do not block if there is any parameter
    then
      exit 0
    fi
    echo "sudo ufw prepend deny from ""$blockaddr"" comment 'Block SYN Flood'"
    $(/usr/bin/sudo /usr/sbin/ufw prepend deny from "$blockaddr" comment "SYN-RECV")
  fi
done

Path traversal attacks

Client sends URI requests attempting to run a shell on the web server. Here is an example from the web server error log. The clue in this entry is /bin/sh. This is not the real source IP address.

[Tue Apr 21 18:28:35.358445 2026] [core:error] [pid 22019:tid 22029] [client 192.7.6.154:57632] AH10244: invalid URI path (/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/bin/sh)

First make sure apache is up to date.

# Show current version
apachectl -v 
# Update the Raspberry Pi Zero Software
sudo apt update
sudo apt full-upgrade
sudo apt clean
# Show the latest version
apachectl -v

Turn off CGI on the web server. CGI is disabled by default.

# Check if CGI is enabled
apache2ctl -M | grep cgi
# Disable if the check finds anything
sudo a2dismod cgi
sudo a2dismod cgid

This BASH script will list the traversal attack source IP addresses, the total number of source IP addresses, and UFW rules to block the source IP addresses. Add the rules to UFW by copy and paste.

#!/bin/bash
# List of traversal attack source IP addresses, total, and UFW rules to block. 
# Add the rules to UFW by copy and paste.
# Source IP addresses 
 for ip in `weblog err all | grep 'invalid URI path' | awk {'print $11'} | sed 's/:[^[:cntrl:]]*$//' | sort -n | uniq `; do echo $ip; done
# List total
for ip in `weblog err all | grep 'invalid URI path' | awk {'print $11'} | sed 's/:[^[:cntrl:]]*$//' | sort -n | uniq | wc`; do echo $ip; done
# Generate UFW Block rules
for ip in `weblog err all | grep 'invalid URI path' | awk {'print $11'} | sed 's/:[^[:cntrl:]]*$//' | sort -n  | uniq`; do echo '/usr/bin/sudo /usr/sbin/ufw prepend deny from '"$ip"' comment "Path Traversal Attack"'; done

PHP Info Probes

Clients checking for PHP info. Here are some examples from the access log.

192.168.248.169 - - [10/May/2026:13:06:02 -1000] "GET /phpinfo.php HTTP/1.1" 404 557 "-" "Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/44.0.2403.67 Mobile/12H321 Safari/600.1.4"
192.168.248.169 - - [10/May/2026:13:06:02 -1000] "GET /phpinfo/ HTTP/1.1" 404 557 "-" "Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/44.0.2403.67 Mobile/12H321 Safari/600.1.4"

This script will generate firewall block rules.

#!/bin/bash
# List phpinfo attack probe IP addresses from apache2 access.log
# 147.81.5.203 don't block my own ip address
for ip in `weblog | grep phpinfo | grep -v 147.81.5.203 | awk {'print $1'}`; do echo $ip; done
# List total
for ip in `weblog | grep phpinfo | grep -v 147.81.5.203 | awk {'print $1'} | wc -l`; do echo "Total: "$ip; done
# Generate UFW Block rules
for ip in `weblog | grep phpinfo | grep -v 147.81.5.203 | awk {'print $1'} | sort -n  | uniq`; do echo '/usr/bin/sudo /usr/sbin/ufw prepend deny from '"$ip"' comment "PHPinfo probe"'; done

Wordpress Info Probes

Clients checking for Wordpress info. Totally uncool. Here are some examples from the access log.

159.203.0.0 - - [19/May/2026:00:46:37 -1000] "GET /wp-content/plugins/burst-statistics/readme.txt HTTP/1.1" 404 3154 "-" "Mozlila/5.0 (Linux; Android 7.0; SM-G892A Bulid/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Moblie Safari/537.36"
34.122.0.0 - - [19/May/2026:06:42:06 -1000] "HEAD /wp/ HTTP/1.1" 301 184 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15"

This script will generate firewall block rules.

#!/bin/bash
# List Wordpress info probe IP addresses from apache2 access.log
# 147.81.5.203 don't block my own ip address
for ip in `weblog | grep wp | grep -v 147.81.5.203 | awk {'print $1'}`; do echo $ip; done
# List total
for ip in `weblog | grep wp | grep -v 147.81.5.203 | awk {'print $1'} | wc -l`; do echo "Total: "$ip; done
# Generate UFW Block rules
for ip in `weblog | grep wp | grep -v 147.81.5.203 | awk {'print $1'} | sort -n  | uniq`; do echo '/usr/bin/sudo /usr/sbin/ufw prepend deny from '"$ip"' comment "Wordpress info probe"'; done

Claudebot Downloads

This is not an attack and I don't care if Claude uses my web pages. Here are the log file entries. It seems kind of crazy to put articles on the web and then stop people from downloading them.

216.73.216.104 - - [31/May/2026:08:39:19 -1000] "GET /sitemap.xml HTTP/1.1" 404 500 "-" "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)"
216.73.216.104 - - [31/May/2026:08:39:19 -1000] "GET /robots.txt HTTP/1.1" 200 2843 "-" "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)"

This robots.txt file allows access to web crawlers.

User-agent: *
Allow: /
This robots.txt file blocks web crawlers that respect the robots.txt file.

User-agent: *
Disallow: /