I used the following rules to call the lua scripts.
#Store the sessionid in tx.sessionid
REQUEST_COOKIES:'/(j?sessionid|(php)?sessid|(asp|jserv|jw)?session[-_]?(id)?|cf(id|token)|sid)/' "(\d{4,4})?([^\s]+?)[\:\.\;\s]" "phase:1,id:'33000',t:none,capture,pass,nolog,setvar:tx.sessionid=%{TX.2}"
#if it's a post request check if the hidden fields are modified.
SecRule &ARGS_POST_NAMES "!@eq 0" \
"phase:2,t:none,t:lowercase,nolog,pass,id:'33001',exec:/test/read-hidden-values-memcached.lua"
SecRule TX:PARAMETER_TAMPERING ".*" "phase:2,t:none,log,deny,msg:'Parameter Tampering; Hidden field.',id:'3002',logdata:'Location: %{matched_var}',tag:'PARAMETER_TAMPERING'"
#Store the sessionid in tx.sessionid
SecRule RESPONSE_HEADERS:/Set-Cookie2?/ "(?i:(j?sessionid|(php)?sessid|(asp|jserv|jw)?session[-_]?(id)?|cf(id|token)|sid)=(\d{4,4})?([^\s]+?)[\:\.\;\s])" "phase:3,id:'33003',t:none,pass,nolog,capture,setvar:tx.sessionid=%{TX.7}"
#if a sessionid is there save the hidden fields into the backend.
SecRule TX:SESSIONID "(.*)" "chain,phase:4,id:'33004',t:none,pass,nolog"
SecRule RESPONSE_BODY ".*" "t:none,nolog,exec:/test/write-hidden-values-memcached.lua"
The 2 Rules which extract the sessionid are from the CRS file modsecurity_crs_16_session_hijacking.conf.
I only modified the regex so it would save the real sessionid and not the sessionid with the server identifier.
I only modified the regex so it would save the real sessionid and not the sessionid with the server identifier.
Here the two lua scripts.
read-hidden-values-memcached.lua:
function main()
require ('Memcached')
jsessionid = m.getvar("tx.sessionid", "none")
if jsessionid then
m.log(9, "luascript: SESSIONID=\"" .. jsessionid .. "\"")
else
return 'no sessionid'
end
argspost = m.getvars("ARGS_POST_NAMES", "none");
memcache = Memcached.Connect({{'server1',9999},{'server2',9999}})
if memcache then
-- Examine all variables
for i=1, #argspost do
argname = argspost[i].value
modsecargname = "ARGS_POST." .. argname
-- use to make exception per ARG
if modsecargname == "ARGS_POST.xxx" or modsecargname == "ARGS_POST.xxx1" or modsecargname == "ARGS_POST.xxx2" then
m.log(9, "luascript: ARGNAME matched exception so no further processing")
else
memvalue = memcache:get(jsessionid .. argname)
if memvalue then
arg = m.getvars(modsecargname, "none")
if memvalue == string.format("\"%s\"", arg[1].value) then
m.log(9, "luascript: value matched, value = " .. arg[1].value .."")
else
m.setvar("tx.parameter_tampering", "Parameter tampered = " .. modsecargname .. " old value = " .. memvalue .. " new value = " .. arg[1].value)
end
end
end
end
else
m.log(9, "luascript: error in connect to memcache")
end
-- Nothing wrong found.
memcache:disconnect_all()
end
write-hidden-values-memcached.lua
function main()
require ('Memcached')
jsessionid = m.getvar("tx.sessionid", "none")
if jsessionid then
m.log(9, "luascript: SESSIONID" .. jsessionid .. ".")
fh = assert (io.tmpfile()) -- open temporary file
responsebody = m.getvar("RESPONSE_BODY", "none")
if responsebody then
fh:write(responsebody)
fh:flush()
else
fh:close() -- close file
return 'no Response Body'
end
fh:seek ("set", 0) -- back to start
-- read whole file with '*a'
local tbuff = fh:read ("*a")
m.log(9, "luascript: read done from file")
fh:close() -- close file
memcache = Memcached.Connect({{'server1',9999},{'server2',9999}})
if memcache then
for a in string.gmatch(tbuff, "<input .->") do
t = {}
m.log(9, "a = " .. a)
for k, v in string.gmatch(a, "(%w+)=\"(.-)\"") do
t[k]=v
-- m.log(9, "var = " .. k .. " = " .. v .. "")
end
if t.type == nil then
t.type = ''
end
if t.value == nil then
t.value = ''
end
if t.name == nil then
t.name = ''
end
if t.type:lower() == "hidden" and t.value ~= '' and t.name ~= '' then
memname = t.name
memvalue = string.format("\"%s\"", t.value)
memcache:set(jsessionid .. memname, memvalue,600)
end
end
-- str1 = string.format('Response body is: %s\n', var1)
else
m.log(9, "luascript: error in connect to memcache")
end
memcache:disconnect_all()
else
m.log(9, "luascript: no sessionid")
end
m.log(9, "luascript: script end")
end
Now your hidden fields should be protected.
You should use these Rules in combination with the session hijacking rules in order to protect your session too.
prerequisite: lua, luamemcached, luasocket
The next time i think i will post how you can modify the modsecurity_crs_16_session_hijacking.conf file in order to be HA aware and store the session data in memcached/kyoto tycoon for persistence.
You should use these Rules in combination with the session hijacking rules in order to protect your session too.
prerequisite: lua, luamemcached, luasocket
The next time i think i will post how you can modify the modsecurity_crs_16_session_hijacking.conf file in order to be HA aware and store the session data in memcached/kyoto tycoon for persistence.