When running an Apache web server, you can find yourself in a situation where you need to capture and log POST data to do some analysis and perhaps to investigate a bad actor to determine a fingerprint pattern. This can be done with the mod_security module, which is a powerful web application firewall that also provides HTTP traffic monitoring, logging and analysis capabilities.

Although this can also be done with mod_dumpio, I wanted to be able to create rulesets to shape the traffic after doing the analysis.

On a Debian system it is available as a package, libapache2-mod-security. Once installed you can enable the module with a2enmod mod-security.

For my specific use case I only wanted to enable the logging for a single virtual host, so I did not activate the module at the global apache configuration level, but within my specific VirtualHost directive.

SecRuleEngine On
SecAuditEngine On
SecRequestBodyAccess On
SecAuditLogParts ABCIJDEFHZ
SecRule REQUEST_METHOD "POST" "id:17,phase:2,t:none,pass,log,auditlog,msg:'Log POST data'"
SecAuditLog /var/log/apache2/somesite-audit.log

This will log quite a bit of information and in a busy production it could quickly consume large amounts of disk space. You may want to reduce the logging level with SecAuditEngine RelevantOnly, which will only log transactions that have generated an error or contain status codes that could be considered relevant.

The SecAuditLogParts setting configures which parts of the transaction should be logged, such as request header, request body, response body, etc. It breaks down to request, response and internal elements.

Request elements
  • B - Request headers.
  • C - Request body. Note: This information is present only if the request body exists and ModSecurity is configured to intercept it. For rules that trigger off the request body, if SecAuditEngine RelevantOnly is set this header will be automatically logged.
  • D - Reserved and not used yet.
  • I - Special Replacement for part C. It will log the same data as C in all cases except when multipart/form-data encoding in used. In that case, it will log a fake application/x-www-form-urlencoded body that contains the information about parameters but not about the files. This is handy if you don’t want to have files stored in your audit logs.
  • J - Contains information about the files uploaded using multipart/form-data encoding.
Response elements
  • E - Intermediary response body. Intermediary response body is the same as the actual response body unless ModSecurity intercepts the intermediary response body, in which case the actual response body will contain the error message (either the Apache default error message, or the ErrorDocument page). Note: This information is only present only when ModSecurity is configured to intercept response bodies, and if the audit log engine is configured to record it.
  • F - Final response headers (excluding the Date and Server headers).
  • G - Reserved, and not used yet.
  • H - Audit log trailer.
Special elements
  • A - Audit log header (This field is mandatory, it will always be logged.)
  • K - Contains a full list of every rule that matched (one per line) in the order they were matched. The rules are fully qualified and will thus show inherited actions and default operators.
  • Z - Final boundary, signifies the end of the entry. (This field is mandatory.)

The part that we are after is C, which also requires that the SecAuditEngine is turned on. Once this is all in place and the apache configuration is reloaded you should see data in the log file defined in SecAuditLog.

Below is a preview of a POST transaction. Notice the POST data contained within the --e1c1d516-C-- section.

--e1c1d516-A--
[28/Nov/2021:04:20:30 +0000] YaMDin8AAAEAACPuSP4AAAAE 127.0.0.1 49971 127.0.0.1 443
--e1c1d516-B--
POST /endpoint HTTP/1.1
Host: www.somesite.com
Connection: keep-alive
Content-Length: 105
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36 Slimjet/15.1.6.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: https://www.somesite.com/
Origin: https://www.somesite.com

--e1c1d516-C--
lang=en&long_url=some_data&add=more_data
--e1c1d516-F--
HTTP/1.1 200 OK
Upgrade: h2,h2c
Connection: Upgrade, Keep-Alive
Referer: https://www.somesite.com
Cache-Control: no-cache, must-revalidate
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Content-Length: 140
Keep-Alive: timeout=5, max=100
Content-Type: application/json

--e1c1d516-E--

--e1c1d516-H--
Message: Warning. Pattern match "POST" at REQUEST_METHOD. [file "/etc/apache2/sites-enabled/www.somesite.com.conf"] [line "18"] [id "1000"] [msg "Log POST data"]
Apache-Error: [file "apache2_util.c"] [line 273] [level 3] [client %s] ModSecurity: %s%s [uri "%s"]%s
Apache-Handler: application/x-httpd-php
Stopwatch: 1638073226244829 4259952 (- - -)
Stopwatch2: 1638073226244829 4259952; combined=2915, p1=726, p2=1909, p3=66, p4=160, p5=53, sr=91, sw=1, l=0, gc=0
Response-Body-Transformed: Dechunked
Producer: ModSecurity for Apache/2.9.1 (http://www.modsecurity.org/); OWASP_CRS/3.0.0.
Server: Apache/2.4.25 (Debian)
Engine-Mode: "ENABLED"

--e1c1d516-Z--

Beyond auditing, you can add a simple rule to deny access to a specific endpoint with a single line.

SecRule REQUEST_URI "@streq /bad-endpoint" "id:1,phase:1,t:lowercase,deny"

This would cause any request to /bad-endpoint to return a HTTP 403.

It is definitely a powerful tool that can be configured to suit your needs. It can be used in conjunction with other modules such as mod_evasive. It is also capable of executing shell scripts directly and perhaps directly interfacing with iptables and much more.

Resources