This site is the archived OWASP Foundation Wiki and is no longer accepting Account Requests.
To view the new OWASP Foundation website, please visit https://owasp.org

Difference between revisions of "OWASP ModSecurity Securing WebGoat Section4 Sublesson 07.1"

From OWASP
Jump to: navigation, search
(Implementation)
(Implementation)
 
(4 intermediate revisions by the same user not shown)
Line 3: Line 3:
 
=== Lesson overview ===
 
=== Lesson overview ===
  
Refer to the zip file with the WebGoat lesson overviews. See Appendix A for more information.
+
The WebGoat lesson overview is included with the WebGoat lesson solution.
  
 
=== Lesson solution ===  
 
=== Lesson solution ===  
Line 99: Line 99:
 
Test the standalone program (in this case on Windows) by running from the command line:
 
Test the standalone program (in this case on Windows) by running from the command line:
 
<pre>
 
<pre>
lua request-on_07-1.lua
+
C:\lua\bin> lua request-on_07-1.lua
 
</pre>
 
</pre>
  
Line 134: Line 134:
  
 
   # action is triggered if script returns non-nil value
 
   # action is triggered if script returns non-nil value
   SecRuleScript "/etc/modsecurity/data/request-on_07-1.lua" "phase:2,t:none,log,auditlog,deny,severity:3,msg:'Luascript: Concurrency -> Thread Safety: request is pending',tag:'CONCURRENCY',redirect:/_error_pages_/lesson07-1.html"
+
   SecRuleScript "/etc/modsecurity/data/request-on_07-1.lua" "phase:2,t:none,log,auditlog, \
   SecAction "phase:2,allow:request,t:none,log,auditlog,msg:'Luascript: Concurrency -> Thread Safety: request is NOT pending'"
+
deny,severity:3,msg:'Luascript: Concurrency -> Thread Safety: request is pending', \
 +
tag:'CONCURRENCY',redirect:/_error_pages_/lesson07-1.html"
 +
   SecAction "phase:2,allow:request,t:none,log,auditlog, \
 +
msg:'Luascript: Concurrency -> Thread Safety: request is NOT pending'"
 
</pre>
 
</pre>
  
Line 146: Line 149:
 
   SecRule TX:MENU "!@eq 800" "phase:4,t:none,pass,skip:1"
 
   SecRule TX:MENU "!@eq 800" "phase:4,t:none,pass,skip:1"
  
   # this will decrement the request-in-progress for any request in Lesson 7 but will not go less than zero
+
   # this will decrement the request-in-progress for any request in Lesson 7  
 +
  #  but will not go less than zero
 
   SecRuleScript "/etc/modsecurity/data/request-off_07-1.lua" \  
 
   SecRuleScript "/etc/modsecurity/data/request-off_07-1.lua" \  
 
"phase:4,t:none,log,auditlog,allow,msg:'Luascript: Concurrency -> \  
 
"phase:4,t:none,log,auditlog,allow,msg:'Luascript: Concurrency -> \  
Line 157: Line 161:
  
 
[[Image:OWASP_ModSecurity_Securing_WebGoat_Thread_safety_SS0.jpg]]
 
[[Image:OWASP_ModSecurity_Securing_WebGoat_Thread_safety_SS0.jpg]]
 
(screenshot lesson07-1_dave.jpg)
 
 
  
 
Quickly followed by Jeff, who gets blocked and receives this message:
 
Quickly followed by Jeff, who gets blocked and receives this message:
  
 
[[Image:OWASP_ModSecurity_Securing_WebGoat_Thread_safety_SS1.jpg]]
 
[[Image:OWASP_ModSecurity_Securing_WebGoat_Thread_safety_SS1.jpg]]
 
(screenshot lesson07-1_jeff.jpg)
 
  
 
=== Comments ===
 
=== Comments ===
  
* This solution shows the process of developing a standalone Lua script and then integrating it with ModSecurity.
+
* This solution shows the process of developing a standalone Lua script on Windows and then integrating it into ModSecurity on Linux.

Latest revision as of 03:04, 1 November 2008

7. Concurrency -> 7.1 Thread Safety Problem

Lesson overview

The WebGoat lesson overview is included with the WebGoat lesson solution.

Lesson solution

Refer to the zip file with the WebGoat lesson solutions. See Appendix A for more information.

Strategy

This WebGoat lesson demonstrates a thread safety problem: when 2 users login almost simultaneously, the first user receives the second user's data.

To solve this lesson, at first an attempt to use ModSecurity rules was made, but figuring out how global persistence works and debugging got too time-consuming for an inexperienced ModSec user, so Lua was chosen.

When the first user logs in, a lock is set and the 2nd user is not allowed to log in until the first user's request returns.

Implementation

For this lesson solution, we will walk through the development process from a standalone Lua script on Windows to the ModSecurity solution on Linux.

Retrieved from the web proxy are the POST body parameters:

username=jeff&SUBMIT=Submit

A class and method is used for standalone development and testing that closely resembles the ModSecurity implementation of writing to the debug log file:

-- BEGIN: CUT HERE WHEN IN MODSEC   
-- for standalone testing, simulate ModSec functions 
-- have to replace ':' with '.'; e.g. 'm:log' with 'm.log'

ModSec = {}	
    
function ModSec:new (o)
	o = o or {}
  setmetatable(o, self)
  self.__index = self
  return o
end
    
function ModSec:log (loglevel, msg)
  print(msg)
end    
    
m = ModSec:new()
-- END: CUT HERE WHEN IN MODSEC  

So, 'm:log(9, msg2)' in the standalone version, which prints the debug messages to the command line, needs to be edited to 'm.log(9, msg2)' for writing to the ModSecurity debug log.

The project also standardized on debug log messaging:

  msg0 = "Luascript (request-on_07-1.lua): "
  msg2 = ""

A debug log message would be assembled like this:

  msg1 = string.format("Request is already pending; user name is '%s'", username)
  msg2 = msg0 .. msg1
  m:log(9, msg2)

This ensures a consistent format and it makes searching through a debug log easier.

The solution is started using a standalone Lua script and simulating an HTTP request which is simple because we only have to run the script to increment the number of users logging in.

The format of the data file 'lesson07-1.data' is:

Entry{
  requestpending = 0
}

The source code to process a request (minus the debug messages) from 'request-on_07-1.lua' is:

  local username = "jsnow"

  function Entry (b) 
    local ecount = b.requestpending
    
    if ecount >= 1 then
      msg1 = string.format("Request is already pending; user name is '%s'", username)
      retval = msg1   
    end 
    
    -- increment count and build string to write back to file
    ecount = ecount + 1
    outstr = string.format("Entry{\n  requestpending = %d\n}\n\n", ecount) 
  end
	
  dofile(datafile)

The 'dofile' function only makes one pass because there is only one entry in the data file.

Test the standalone program (in this case on Windows) by running from the command line:

C:\lua\bin> lua request-on_07-1.lua

Each time the program is run, the number of logged in users should increment by 1 in the data file until the maximum allowed is reached.

If the number of users logging in is greater or equal to 1, the function returns with a non-nil value, which causes a match for the ModSecurity rule. When the count is incremented, the data file with the new count is written back to disk:

  local fh2 = io.open(datafile, "w+")
  fh2:write(outstr)
  fh2:flush()
  fh2:close()

Once the program works as intended, modify it and integrate it into ModSecurity on Linux.

To convert to ModSecurity, we only have to: 1. Cut out the log class and method 2. Change m:log to m.log 3. replace the hard-coded 'username' with:

 
  local username = m.getvar("ARGS_POST.username", "none")

4. Remove the 'main()' call at the end of the file.

It's that simple!

The Modsecurity rules are in the file 'rulefile_07-1_thread-safety.conf'.

For the request:

  SecRule ARGS:menu "!@eq 800" "phase:2,t:none,skip:4"
  SecRule &ARGS_POST:SUBMIT "@eq 0" "nolog,skip:3"
  SecRule &ARGS_POST:username "@eq 0" "nolog,skip:2"

  # action is triggered if script returns non-nil value
  SecRuleScript "/etc/modsecurity/data/request-on_07-1.lua" "phase:2,t:none,log,auditlog, \
deny,severity:3,msg:'Luascript: Concurrency -> Thread Safety: request is pending', \
tag:'CONCURRENCY',redirect:/_error_pages_/lesson07-1.html"
  SecAction "phase:2,allow:request,t:none,log,auditlog, \
msg:'Luascript: Concurrency -> Thread Safety: request is NOT pending'"

For the HTTP response, the user count is decremented each time so running another Lua script, 'request-off_07-1.lua', simulates that in standalone mode. You can view the source - it's exactly the same as above except it decrements the count instead of incrementing the user count.

Once the Lua program is working in standalone mode, convert it and move it over to ModSecurity.

The ModSecurity rules are:

  SecRule TX:MENU "!@eq 800" "phase:4,t:none,pass,skip:1"

  # this will decrement the request-in-progress for any request in Lesson 7 
  #   but will not go less than zero
  SecRuleScript "/etc/modsecurity/data/request-off_07-1.lua" \ 
"phase:4,t:none,log,auditlog,allow,msg:'Luascript: Concurrency -> \ 
Thread Safety: in RESPONSE; decrementing requests by 1'"

The best way to test the Lua scripts when integrating into ModSecurity is to first comment out the response rules, then when the request rules are working, do the opposite; then enable both sets once the request set of rules are working properly.

When enabled and doing the WebGoat sublesson, Dave logs in first:

OWASP ModSecurity Securing WebGoat Thread safety SS0.jpg

Quickly followed by Jeff, who gets blocked and receives this message:

OWASP ModSecurity Securing WebGoat Thread safety SS1.jpg

Comments

  • This solution shows the process of developing a standalone Lua script on Windows and then integrating it into ModSecurity on Linux.