Montag, 18. Juli 2011

Protect hidden fields with mod_security (HA Environment)

If you want to protect hidden fields in an HA/LB environment than the lua integration of mod_security is your salvation. I found a sample in the webgoat/GotRoot Rules (didn't found the download link anymore) which are using 2 lua scripts.The samples scan the RESPONSE_BODY for hidden fields and save them in a file. I modified them in order to speak with a memcached backend and store there the hidden fields. Of course you can use kyoto tycoon to get replication, see my first post ;).
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.
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.

Sonntag, 10. Juli 2011

Floatingpoint CVE-2010-4476 protection with mod_security

Some months ago i tried to block attacks which tried to abuse the Floatingpoint vulnarability with mod_security which is described in the following site http://blog.fortify.com/blog/2011/02/08/Double-Trouble.
I used the lua script which was posted on the spiderlabs blog site http://blog.spiderlabs.com/2011/02/java-floating-point-dos-attack-protection.html .
I copied the exact configuration which was documented but something didn't seem right.
The following curl command:
curl -H "Accept-Language: en-us;q=2.2250738585072012e-308" http://localhost/
was blocked by mod_security with the following Auditlog entry.
Message: Access denied with code 403 (phase 2). Pattern match ".*" at TX:floatingpointdos. [file "/test/apache/conf/modsecurity/modsecurity_crs_15_exception.conf"] [line "36"] [msg "Floating Point DoS Payload Found."] [data "Location: REQUEST_HEADERS:User-Agent"] [tag "CVE-2010-4476"]

The Request was blocked but it didn't recognized that i sent a malicious Accept-Language Header not a malicious User-Agent Header. After that i looked into the lua script and there where some errors in it.
The lua script used the string.gmatch function which seems to be always true.
The following if statement "if string.gmatch(FilteredPattern, Pattern) then" matched always and so the first parameter which where checked was printed in the audit log, this was in my case the User-Agent Header.

Here is the modified Lua script which worked for me.

#!/test/lua/bin/lua
function main()
 local Pattern = "2225073858507201";  
  -- Get the ModSec collections
  local Headers = m.getvars("REQUEST_HEADERS");
  local Args = m.getvars("ARGS");
  for i = 1, #Headers do
    FilteredPattern,NumChanges=string.gsub(Headers[i].value, "[.]", "")
    for j in string.gmatch(FilteredPattern, Pattern) do  
      m.setvar("tx.floatingpointdos", Headers[i].name)
      return ("Potential Floating Point DoS Attack via variable: " ..Headers[i].name ..  ".");
    end
  end
  for i = 1, #Args do
    FilteredPattern,NumChanges=string.gsub(Args[i].value, "[.]", "")
    for j in string.gmatch(FilteredPattern, Pattern) do
      m.setvar("tx.floatingpointdos", Args[i].name)
      return ("Potential Floating Point DoS Attack via variable: " ..Args[i].name ..  ".");
    end
  end
  return nil;
end

Maybe it helps someone who had the same problem or some false positives.

Michael 

Freitag, 8. Juli 2011

Kyoto Tycoon a memcached alternative with replication?

Today i searched for a replication mechanism for memcached. I only found an extension for memcache repcached  => don't know if it's maintained,  last update was 2009. I searched further and found Membase but that didn't fit my needs.
After that i read something about kyoto tycoon which offers full replication support for an in memory cache/database and it has a module addon which speaks the memcache protocol so the client side doesn't need to be changed if i switch to tyccon => That sounds good.
It also has many other features which you can read about at fallabs.com, like different backends ...
The Installation was quite easy.
kyotocabinet-1.2.65> ./configure --prefix=/home/test/kyotocabinet-1.2.65 && make && make check && make install
kyototycoon-0.9.49> ./configure --prefix=/home/test/kyototycoon-0.9.49 --with-kc=/home/test/kyotocabinet-1.2.65 && make && make check && make install
make sure you use gcc 4.2+ or bear with the warnings.
For my testing i used a Dual Master configuration since i want to be able to save and get data from both servers and auto recovery should also work.
No manual intervention should be needed if one of the servers crashes and is started again after a while.
Start the first master server.
~/kyototycoon_0.9.49/bin>LD_LIBRARY_PATH="/home/test/kyotocabinet/lib:/home/test/kyototycoon/lib:$LD_LIBRARY_PATH"
~/kyototycoon_0.9.49/bin>./ktserver -dmn -log /home/test/kyototycoon_0.9.49/ktserver0.log -ls -bgs /home/test/kyototycoon_0.9.49/backgroundsnap0 -bgsi 30 -pid /home/test/kyototycoon_0.9.49/tycoon0.pid -host localhost -port 10000 -ulog /home/test/kyototycoon_0.9.49/0000.ulog -sid 0 -mhost localhost -mport 10001 -rts /home/test/kyototycoon_0.9.49/0000.rts -plsv /home/test/kyototycoon_0.9.49/libexec/ktplugservmemc.so -plex 'port=10002' ':#bnum=10000000#ktcapsiz=1g'
Start the second master server
~/kyototycoon_0.9.49/bin>./ktserver -dmn -log /home/test/kyototycoon_0.9.49/ktserver1.log -ls -bgs /home/test/kyototycoon_0.9.49/backgroundsnap1 -bgsi 30 -pid /home/test/kyototycoon_0.9.49/tycoon1.pid -host localhost -port 10001 -ulog /home/test/kyototycoon_0.9.49/0001.ulog -sid 1 -mhost localhost -mport 10000 -rts /home/test/kyototycoon_0.9.49/0001.rts -plsv /home/test/kyototycoon_0.9.49/libexec/ktplugservmemc.so -plex 'port=10003' ':#bnum=10000000#ktcapsiz=1g'

The option used have the following meaning.

  • plsv plugable server
  • plex option for the plugable server (memcache)
  • port specifies the native port (you must use for the tools from tyccon like ktremotemgr)
  • mport specifies the remote port of the second master server.
  • bgs make a background copy of the cache/database
  • bgsi is the intervall for the background copy
  • ulog updatelog (needed for replication)
  • rts replication timestamp

Now test the functionality with
~/kyototycoon_0.9.49/bin> ./ktremotemgr set -host localhost -port 10000 test1 value1
~/kyototycoon_0.9.49/bin> ./ktremotemgr set -host localhost -port 10000 test2 value2
~/kyototycoon_0.9.49/bin> ./ktremotemgr list -pv -host localhost -port 10000
test1   value1
test2   value2
~/kyototycoon-0.9.49/bin> ./ktremotemgr list -pv -host localhost -port 10001
test1   value1
test2   value2
This shows that normal replication is working.
Now kill one master server
~/kyototycoon-0.9.49/bin>  kill `cat /home/test/kyototycoon_0.9.49/tycoon1.pid`
~/kyototycoon-0.9.49/bin> ./ktremotemgr set -host localhost -port 10000 test3 value3
~/kyototycoon-0.9.49/bin> ./ktremotemgr set -host localhost -port 10000 test4 value4
~/kyototycoon-0.9.49/bin> ./ktremotemgr list -pv -host localhost -port 10001
./ktremotemgr: DB::open failed: : 6: network error: connection failed
~/kyototycoon-0.9.49/bin> ./ktremotemgr list -pv -host localhost -port 10000
test1   value1
test4   value4
test2   value2
test3   value3
start the second master again.
~/kyototycoon-0.9.49/bin>./ktremotemgr list -pv -host localhost -port 10001
test1   value1
test4   value4
test2   value2
test3   value3
This shows that the values which where saved when the second master was down are also available.
I did some performance testing and it seems that even when replication is used with tycoon the response time is the same as memcached.
Maybe the next time i will post some performance metrics if i have time.

Michael

My first post after a long hibernation ;)

Hi,

The whole world is already blogging it seems that only me was asleep for the last decade.
But now i'm awake ;) and i think i should start my own blog too.
I will post mainly about technical stuff which i'm interested in and daily work problems and how they were solved. I will do this because i also get the most valuable information about technical stuff from blogs or forums. And now i will share my two cents too ;)
Of course you have already noticed i'm not a native English speaker so bear with me.

Michael