OWASP ModSecurity Securing WebGoat Section4 Sublesson 08.2 08.4 08.5 08.7

8. Cross-Site Scripting (XSS) -> 8.1  Phishing with XSS

8. Cross-Site Scripting (XSS) -> 8.2  LAB: Cross Site Scripting

8. Cross-Site Scripting (XSS) -> 8.4  Reflected XSS Attacks

8. Cross-Site Scripting (XSS) -> 8.5  Cross Site Request Forgery (CSRF)

8. Cross-Site Scripting (XSS) -> 8.7  Cross Site Tracing (XST) Attacks

The lessons are combined because the strategy and the ruleset to solve them are the same.

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

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

Strategy
Probably due to the constraints of WebGoat, it is not feasible to fully illustrate sophisticated CSRF and XST attacks so they can be mitigated with the core ruleset rules from 'modsecurity_crs_40_generic_attacks.conf' for generic XSS attacks.

However, Stage 3 of the Lab proved challenging. The employee Bruce has malicious XSS code stored in his profile and it is triggered when David views it. The malicious code is contained in an address field that is displayed in the HTML body:

8899 FreeBSD Drive alert(document.cookie)

The strategy to mitigate this vulnerability is to use ModSecurity as an egress filter on the response body. It is observed that WebGoat only uses javascript in the Head section of HTML files and that Javascript is used only from *.js files such as:



Because we know this, we can consider any other javascript code as malicious and, for example, require any javascript to start with:

<script language="JavaScript1.2" src="javascript/

Implementation
The generic XSS attacks are mitigated from rules in the file 'rulefile_08_xss.conf':

# False positives removed from following list: 'application' SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer \ "@pm jscript onsubmit copyparentfolder javascript meta onmove onkeydown onchange \    onkeyup activexobject expression onmouseup ecmascript onmouseover vbscript: \     <![cdata[ http: settimeout onabort shell: .innerhtml onmousedown onkeypress \     asfunction: onclick .fromcharcode background-image: .cookie ondragdrop onblur \     x-javascript mocha: onfocus javascript: getparentfolder lowsrc onresize @import \     alert onselect script onmouseout onmousemove background .execscript livescript: \     getspecialfolder vbscript iframe .addimport onunload createtextrange onload <input" \ "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,deny,log,auditlog, \    msg:'Cross-site Scripting (XSS) Attack - whole word matches',tag:'WEB_ATTACK/XSS', \     logdata:'%{TX.0}',redirect:/_error_pages_/lesson08a.html,severity:'3'"

# Enable the next line if want to change to 'pass' for detect-only mode # SecAction pass,nolog,skipAfter:959004

SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES \ "(?:\b(?:(?:type\b\W*?\b(?:text\b\W*?\b(?:j(?:ava)?|ecma|vb)| \    application\b\W*?\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange) \     |get(?:special|parent)folder|iframe\b.{0,100}?\bsrc)\b| \     on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)| \     c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\b\W* \     ?=|abort\b)|(?:l(?:owsrc\b\W*?\b(?:(?:java|vb)script|shell|http)|ivescript)| \     (?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha) \     :|s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b \     (?:(?:java|vb)script|shell|http):)|a(?:ctivexobject\b|lert\b\W*?\(|sfunction:))| \     <(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\btype\b\W*?\bimage)\b| \     ?(?:(?:script|meta)\b|iframe)|!\[cdata\[)|(?:\.(?:(?:execscrip|addimpor)t| \     (?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ "capture,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,log,auditlog,deny, \    msg:'Cross-site Scripting (XSS) Attack - script tags',tag:'WEB_ATTACK/XSS', \     logdata:'%{TX.0}',redirect:/_error_pages_/lesson08b.html,severity:'3'"

SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer \ "(?:\b(?:(?:type\b\W*?\b(?:text\b\W*?\b(?:j(?:ava)?|ecma|vb)| \    application\b\W*?\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange) \     |get(?:special|parent)folder|iframe\b.{0,100}?\bsrc)\b| \     on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)| \     c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\     b\W*?=|abort\b)|(?:l(?:owsrc\b\W*?\b(?:(?:java|vb)script|shell|http)|ivescript)| \     (?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha):| \     s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b \     (?:(?:java|vb)script|shell|http):)|a(?:ctivexobject\b|lert\b\W*?\(|sfunction:))| \     <(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\btype\b\W*?\bimage)\b| \      ?(?:(?:script|meta)\b|iframe)|!\[cdata\[)|(?:\.(?:(?:execscrip|addimpor)t| \     (?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ "capture,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,log, \    auditlog,deny,msg:'Cross-site Scripting (XSS) Attack - in request headers or XML', \     id:'959004',tag:'WEB_ATTACK/XSS',logdata:'%{TX.0}', \     redirect:/_error_pages_/lesson08c.html,severity:'3'"

For stopping the malicious stored XSS script, this rule is used:

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

# parse response body and write hidden values to file SecRuleScript "/etc/modsecurity/data/jscript_08.lua" "phase:4,t:none,log, \    auditlog,deny,msg:'Lesson 8 XSS Lab Stage 3, found nonconformant javascript \     tags using luascript',redirect:/_error_pages_/lesson08-3.html"

Since we are whitelisting the pattern that is accepting for using javascript, a Lua script is used. The source code is shown here in its entirety, replete with debug code for Level 9 debugging:

function main m.log(9, "Starting luascript file jscript_08.lua") print ("Executing luascript jscript_08.lua")

local tbuff = m.getvar("RESPONSE_BODY", "none") local str1 for a in string.gmatch(tbuff, "") do   str1 = string.format("\nLuascript: Trying to match: %s: ", a)    m.log(9, str1) if string.match(a, \      "^<script%s+language=\"JavaScript1.2\"%s+src=\"javascript/") == nil then      str1 = string.format("Luascript: from RESPONSE_BODY - \         string not matching: %s; exiting", a)      m.log(9, str1)      return str1    end  end

m.log(9, "Exiting luascript file jscript_08.lua") return nil end

Note that Lua is implemented in ModSecurity so that returning a value of 'nil' means there is no match in the SecRuleScript; you can think of 'nil' as a "good" return code meaning that nothing bad has happened. Anything else returned will cause a match and invoke the actions in the SecRuleScript.