- Home
- About us
- Products & Services
- Blog
- News
- Contact
- Client Portal
No matter how good a black list is there will always be a way to circumvent it. JavaScript is especially good at letting attackers hide their payloads using various characters. Billy Hoffman demonstrated this very clearly in his book Ajax Security where he shows how to encode payloads using whitespace and tabs (p.115-116). A better approach, such as a whitelist, is needed to provide holistic protection for web applications.
Creating a list of acceptable parameter values for your web app sounds like a daunting task for any decent size app. The good news is that we can automate much of the process using ModSecurity rules and Lua. This post will demonstrate one technique that can be used to allow ModSecurity to learn what a valid server response for a given resource looks like. By fingerprinting the server response, we can better detect web site defacements or injected data that may be part of a cross site scripting attack. After some discussions on the matter, Ryan Barnett (@ryancbarnett) and I came up with the following rules.
The first step is a Lua script that simply grabs the the server response from the ModSecurity RESPONSE_BODY parameter, then counts the number of iframes, scripts, links and images in the response. The script then sets a transactional variable with the number of iframes, scripts, links and images found
function main()
local response_body = m.getvar("RESPONSE_BODY", "none");
if response_body ~= "" then
local _, nscripts = string.gsub(response_body, "<script", "");
local _, niframes = string.gsub(response_body, "<iframe", "");
local _, nlinks = string.gsub(response_body, "a href", "");
local _, nimages = string.gsub(response_body, "<img", "");
m.setvar("tx.niframes", niframes);
m.setvar("tx.nscripts", nscripts);
m.setvar("tx.nlinks", nlinks);
m.setvar("tx.nimages", nimages);
m.log(3, "niframes[" .. niframes .. "]");
m.log(3, "nscripts[" .. nscripts .. "]");
m.log(3, "nlinks[" .. nlinks .. "]");
m.log(3, "nimages[" .. nimages .. "]");
return nil;
end
return nil;
end
The following ModSecurity rules:
SecRuleScript profile_page_scripts.lua "phase:4,t:none,nolog,pass"
SecRule &RESOURCE:'/(niframes|nscripts|nlinks|nimages)/' "@eq 0"
"skipAfter:END_PAGE_PROFILE,phase:4,t:none,nolog,pass, \
setvar:resource.niframes=%{tx.niframes}, \
setvar:resource.nscripts=%{tx.nscripts}, \
setvar:resource.nlinks=%{tx.nlinks}, \
setvar:resource.nimages=%{tx.nimages}"
SecRule TX:NIFRAMES "@eq %{resource.niframes}" "phase:4,t:none, \
nolog,pass,setvar:resource.profile_confidence_counter=+1"
SecRule TX:NSCRIPTS "@eq %{resource.nscripts}" "phase:4,t:none, \
nolog,pass,setvar:resource.profile_confidence_counter=+1"
SecRule TX:NLINKS "@eq %{resource.nlinks}" "phase:4,t:none, \
nolog,pass,setvar:resource.profile_confidence_counter=+1"
SecRule TX:NIMAGES "@eq %{resource.nimages}" "phase:4,t:none, \
nolog,pass,setvar:resource.profile_confidence_counter=+1"
Finally, we enforce the rules by:
SecRule RESOURCE:PROFILE_CONFIDENCE_COUNTER "@lt 40" "phase:4, \
t:none,nolog,pass,skipAfter:END_PAGE_PROFILE"
SecRule TX:NIFRAMES "!@eq %{resource.niframes}" "phase:4, \
t:none,block,msg:'Number of IFrames in Page Have Changed.', \
logdata:'Previous #: %{resource.niframes} and Current #: %{tx.niframes}',
severity:'3',setvar:'tx.msg=%{rule.msg}', \
setvar:tx.outbound_anomaly_score=+%{tx.error_anomaly_score}, \
setvar:tx.anomaly_score=+{tx.error_anomaly_score},\
setvar:tx.%{rule.id}-PROFILE/ANOMALY-%{matched_var_name}=%{tx.0}"
SecRule TX:NSCRIPTS "!@eq %{resource.nscripts}" "phase:4, \
t:none,block,msg:'Number of Scripts in Page Have Changed.', \
logdata:'Previous #: %{resource.nscripts} and Current #: %{tx.nscripts}',
severity:'3',setvar:'tx.msg=%{rule.msg}', \
setvar:tx.outbound_anomaly_score=+%{tx.error_anomaly_score}, \
setvar:tx.anomaly_score=+{tx.error_anomaly_score}, \
setvar:tx.%{rule.id}-PROFILE/ANOMALY-%{matched_var_name}=%{tx.0}"
SecRule TX:NLINKS "!@eq %{resource.nlinks}" "phase:4, \
t:none,block,msg:'Number of Links in PageHave Changed.', \
logdata:'Previous #: %{resource.nlinks} and Current #: %{tx.nlinks}',
severity:'3',setvar:'tx.msg=%{rule.msg}', \
setvar:tx.outbound_anomaly_score=+%{tx.error_anomaly_score}, \
setvar:tx.anomaly_score=+{tx.error_anomaly_score}, \
setvar:tx.%{rule.id}-PROFILE/ANOMALY-%{matched_var_name}=%{tx.0}"
SecRule TX:NIMAGES "!@eq %{resource.nimages}" "phase:4, \
t:none,block,msg:'Number of Images in Page Have Changed.', \
logdata:'Previous #: %{resource.nimages} and Current #: %{tx.nimages}',
severity:'3',setvar:'tx.msg=%{rule.msg}', \
setvar:tx.outbound_anomaly_score=+%{tx.error_anomaly_score}, \
setvar:tx.anomaly_score=+{tx.error_anomaly_score}, \
setvar:tx.%{rule.id}-PROFILE/ANOMALY-%{matched_var_name}=%{tx.0}"
SecMarker END_PAGE_PROFILE
Post new comment