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.2 LAB: Cross Site Scripting

Stage 1: Stored XSS

Stage 3: Stored XSS Revisited

Stage 5: Reflected XSS

8.4 Reflected XSS Attacks

8.5 Cross Site Request Forgery (CSRF)

8.7 Cross Site Tracing (XST) Attacks

Stages 2, 4, and 6 of the lab uses the developer version of WebGoat so that the source code can be modified to

prevent the attacks in Stages 1, 3, and 5 respectively, but that's what we're doing here so these stages do not

pertain to us.

Lesson overviews
See [relative paths].

Lesson solutions
See [relative paths].

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.